From f3120226c430271f067c2a55910f615f2a6bb389 Mon Sep 17 00:00:00 2001 From: tznind Date: Fri, 5 Jul 2024 19:26:29 +0100 Subject: [PATCH 01/48] Start pulling Animation a bit at a time through chat gpt from https://github.com/ChrisBuilds/terminaltexteffects --- Terminal.Gui/TextEffects/Animation.cs | 566 ++++++++++++++++++++++++ Terminal.Gui/TextEffects/Easing.cs | 303 +++++++++++++ UnitTests/TextEffects/AnimationTests.cs | 191 ++++++++ 3 files changed, 1060 insertions(+) create mode 100644 Terminal.Gui/TextEffects/Animation.cs create mode 100644 Terminal.Gui/TextEffects/Easing.cs create mode 100644 UnitTests/TextEffects/AnimationTests.cs diff --git a/Terminal.Gui/TextEffects/Animation.cs b/Terminal.Gui/TextEffects/Animation.cs new file mode 100644 index 000000000..c7e17fff0 --- /dev/null +++ b/Terminal.Gui/TextEffects/Animation.cs @@ -0,0 +1,566 @@ +using static Unix.Terminal.Curses; + +namespace Terminal.Gui.TextEffects; + +public enum SyncMetric +{ + Distance, + Step +} + +public class CharacterVisual +{ + public string Symbol { get; set; } + public bool Bold { get; set; } + public bool Dim { get; set; } + public bool Italic { get; set; } + public bool Underline { get; set; } + public bool Blink { get; set; } + public bool Reverse { get; set; } + public bool Hidden { get; set; } + public bool Strike { get; set; } + public Color Color { get; set; } + public string FormattedSymbol { get; private set; } + private string _colorCode; + + public CharacterVisual (string symbol, bool bold = false, bool dim = false, bool italic = false, bool underline = false, bool blink = false, bool reverse = false, bool hidden = false, bool strike = false, Color color = null, string colorCode = null) + { + Symbol = symbol; + Bold = bold; + Dim = dim; + Italic = italic; + Underline = underline; + Blink = blink; + Reverse = reverse; + Hidden = hidden; + Strike = strike; + Color = color; + _colorCode = colorCode; + FormattedSymbol = FormatSymbol (); + } + + private string FormatSymbol () + { + string formattingString = ""; + if (Bold) formattingString += Ansitools.ApplyBold (); + if (Italic) formattingString += Ansitools.ApplyItalic (); + if (Underline) formattingString += Ansitools.ApplyUnderline (); + if (Blink) formattingString += Ansitools.ApplyBlink (); + if (Reverse) formattingString += Ansitools.ApplyReverse (); + if (Hidden) formattingString += Ansitools.ApplyHidden (); + if (Strike) formattingString += Ansitools.ApplyStrikethrough (); + if (_colorCode != null) formattingString += Colorterm.Fg (_colorCode); + + return $"{formattingString}{Symbol}{(formattingString != "" ? Ansitools.ResetAll () : "")}"; + } + + public void DisableModes () + { + Bold = false; + Dim = false; + Italic = false; + Underline = false; + Blink = false; + Reverse = false; + Hidden = false; + Strike = false; + } +} + +public class Frame +{ + public CharacterVisual CharacterVisual { get; } + public int Duration { get; } + public int TicksElapsed { get; set; } + + public Frame (CharacterVisual characterVisual, int duration) + { + CharacterVisual = characterVisual; + Duration = duration; + TicksElapsed = 0; + } + + public void IncrementTicks () + { + TicksElapsed++; + } +} + +public class Scene +{ + public string SceneId { get; } + public bool IsLooping { get; } + public SyncMetric? Sync { get; } + public EasingFunction Ease { get; } + public bool NoColor { get; set; } + public bool UseXtermColors { get; set; } + public List Frames { get; } = new List (); + public List PlayedFrames { get; } = new List (); + public Dictionary FrameIndexMap { get; } = new Dictionary (); + public int EasingTotalSteps { get; private set; } + public int EasingCurrentStep { get; private set; } + public static Dictionary XtermColorMap { get; } = new Dictionary (); + + public Scene (string sceneId, bool isLooping = false, SyncMetric? sync = null, EasingFunction ease = null, bool noColor = false, bool useXtermColors = false) + { + SceneId = sceneId; + IsLooping = isLooping; + Sync = sync; + Ease = ease; + NoColor = noColor; + UseXtermColors = useXtermColors; + } + + public void AddFrame (string symbol, int duration, Color color = null, bool bold = false, bool dim = false, bool italic = false, bool underline = false, bool blink = false, bool reverse = false, bool hidden = false, bool strike = false) + { + string charVisColor = null; + if (color != null) + { + if (NoColor) + { + charVisColor = null; + } + else if (UseXtermColors) + { + if (color.XtermColor != null) + { + charVisColor = color.XtermColor; + } + else if (XtermColorMap.ContainsKey (color.RgbColor)) + { + // Build error says Error CS0029 Cannot implicitly convert type 'int' to 'string' Terminal.Gui (net8.0) D:\Repos\TerminalGuiDesigner\gui.cs\Terminal.Gui\TextEffects\Animation.cs 120 Active + charVisColor = XtermColorMap [color.RgbColor].ToString (); + } + else + { + var xtermColor = Hexterm.HexToXterm (color.RgbColor); + XtermColorMap [color.RgbColor] = int.Parse (xtermColor); + charVisColor = xtermColor; + } + } + else + { + charVisColor = color.RgbColor; + } + } + + if (duration < 1) + { + throw new ArgumentException ("duration must be greater than 0"); + } + + var charVis = new CharacterVisual (symbol, bold, dim, italic, underline, blink, reverse, hidden, strike, color, charVisColor); + var frame = new Frame (charVis, duration); + Frames.Add (frame); + for (int i = 0; i < frame.Duration; i++) + { + FrameIndexMap [EasingTotalSteps] = frame; + EasingTotalSteps++; + } + } + + public CharacterVisual Activate () + { + if (Frames.Count > 0) + { + return Frames [0].CharacterVisual; + } + else + { + throw new InvalidOperationException ("Scene has no frames."); + } + } + + public CharacterVisual GetNextVisual () + { + var currentFrame = Frames [0]; + var nextVisual = currentFrame.CharacterVisual; + currentFrame.IncrementTicks (); + if (currentFrame.TicksElapsed == currentFrame.Duration) + { + currentFrame.TicksElapsed = 0; + PlayedFrames.Add (Frames [0]); + Frames.RemoveAt (0); + if (IsLooping && Frames.Count == 0) + { + Frames.AddRange (PlayedFrames); + PlayedFrames.Clear (); + } + } + return nextVisual; + } + + public void ApplyGradientToSymbols (Gradient gradient, IList symbols, int duration) + { + int lastIndex = 0; + for (int symbolIndex = 0; symbolIndex < symbols.Count; symbolIndex++) + { + var symbol = symbols [symbolIndex]; + double symbolProgress = (symbolIndex + 1) / (double)symbols.Count; + int gradientIndex = (int)(symbolProgress * gradient.Spectrum.Count); + foreach (var color in gradient.Spectrum.GetRange (lastIndex, Math.Max (gradientIndex - lastIndex, 1))) + { + AddFrame (symbol, duration, color); + } + lastIndex = gradientIndex; + } + } + + public void ResetScene () + { + foreach (var sequence in Frames) + { + sequence.TicksElapsed = 0; + PlayedFrames.Add (sequence); + } + Frames.Clear (); + Frames.AddRange (PlayedFrames); + PlayedFrames.Clear (); + } + + public override bool Equals (object obj) + { + if (obj is Scene other) + { + return SceneId == other.SceneId; + } + return false; + } + + public override int GetHashCode () + { + return SceneId.GetHashCode (); + } +} + +public class Animation +{ + public Dictionary Scenes { get; } = new Dictionary (); + public EffectCharacter Character { get; } + public Scene ActiveScene { get; private set; } + public bool UseXtermColors { get; set; } = false; + public bool NoColor { get; set; } = false; + public Dictionary XtermColorMap { get; } = new Dictionary (); + public int ActiveSceneCurrentStep { get; private set; } = 0; + public CharacterVisual CurrentCharacterVisual { get; private set; } + + public Animation (EffectCharacter character) + { + Character = character; + CurrentCharacterVisual = new CharacterVisual (character.InputSymbol); + } + + public Scene NewScene (bool isLooping = false, SyncMetric? sync = null, EasingFunction ease = null, string id = "") + { + if (string.IsNullOrEmpty (id)) + { + bool foundUnique = false; + int currentId = Scenes.Count; + while (!foundUnique) + { + id = $"{Scenes.Count}"; + if (!Scenes.ContainsKey (id)) + { + foundUnique = true; + } + else + { + currentId++; + } + } + } + + var newScene = new Scene (id, isLooping, sync, ease); + Scenes [id] = newScene; + newScene.NoColor = NoColor; + newScene.UseXtermColors = UseXtermColors; + return newScene; + } + + public Scene QueryScene (string sceneId) + { + if (!Scenes.TryGetValue (sceneId, out var scene)) + { + throw new ArgumentException ($"Scene {sceneId} does not exist."); + } + return scene; + } + + public bool ActiveSceneIsComplete () + { + if (ActiveScene == null) + { + return true; + } + return ActiveScene.Frames.Count == 0 && !ActiveScene.IsLooping; + } + + public void SetAppearance (string symbol, Color? color = null) + { + string charVisColor = null; + if (color != null) + { + if (NoColor) + { + charVisColor = null; + } + else if (UseXtermColors) + { + charVisColor = color.XtermColor; + } + else + { + charVisColor = color.RgbColor; + } + } + CurrentCharacterVisual = new CharacterVisual (symbol, color: color, _colorCode: charVisColor); + } + + public static Color RandomColor () + { + var random = new Random (); + var colorHex = random.Next (0, 0xFFFFFF).ToString ("X6"); + return new Color (colorHex); + } + + public static Color AdjustColorBrightness (Color color, float brightness) + { + float HueToRgb (float p, float q, float t) + { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6f) return p + (q - p) * 6 * t; + if (t < 1 / 2f) return q; + if (t < 2 / 3f) return p + (q - p) * (2 / 3f - t) * 6; + return p; + } + + float r = int.Parse (color.RgbColor.Substring (0, 2), System.Globalization.NumberStyles.HexNumber) / 255f; + float g = int.Parse (color.RgbColor.Substring (2, 2), System.Globalization.NumberStyles.HexNumber) / 255f; + float b = int.Parse (color.RgbColor.Substring (4, 2), System.Globalization.NumberStyles.HexNumber) / 255f; + + float max = Math.Max (r, Math.Max (g, b)); + float min = Math.Min (r, Math.Min (g, b)); + float h, s, l = (max + min) / 2f; + + if (max == min) + { + h = s = 0; // achromatic + } + else + { + float d = max - min; + s = l > 0.5f ? d / (2f - max - min) : d / (max + min); + if (max == r) + { + h = (g - b) / d + (g < b ? 6 : 0); + } + else if (max == g) + { + h = (b - r) / d + 2; + } + else + { + h = (r - g) / d + 4; + } + h /= 6; + } + + l = Math.Max (Math.Min (l * brightness, 1), 0); + + if (s == 0) + { + r = g = b = l; // achromatic + } + else + { + float q = l < 0.5f ? l * (1 + s) : l + s - l * s; + float p = 2 * l - q; + r = HueToRgb (p, q, h + 1 / 3f); + g = HueToRgb (p, q, h); + b = HueToRgb (p, q, h - 1 / 3f); + } + + var adjustedColor = $"{(int)(r * 255):X2}{(int)(g * 255):X2}{(int)(b * 255):X2}"; + return new Color (adjustedColor); + } + + private float EaseAnimation (EasingFunction easingFunc) + { + if (ActiveScene == null) + { + return 0; + } + float elapsedStepRatio = ActiveScene.EasingCurrentStep / (float)ActiveScene.EasingTotalSteps; + return easingFunc (elapsedStepRatio); + } + + public void StepAnimation () + { + if (ActiveScene != null && ActiveScene.Frames.Count > 0) + { + if (ActiveScene.Sync != null) + { + if (Character.Motion.ActivePath != null) + { + int sequenceIndex = 0; + if (ActiveScene.Sync == SyncMetric.Step) + { + sequenceIndex = (int)Math.Round ((ActiveScene.Frames.Count - 1) * + (Math.Max (Character.Motion.ActivePath.CurrentStep, 1) / + (float)Math.Max (Character.Motion.ActivePath.MaxSteps, 1))); + } + else if (ActiveScene.Sync == SyncMetric.Distance) + { + sequenceIndex = (int)Math.Round ((ActiveScene.Frames.Count - 1) * + (Math.Max (Math.Max (Character.Motion.ActivePath.TotalDistance, 1) - + Math.Max (Character.Motion.ActivePath.TotalDistance - + Character.Motion.ActivePath.LastDistanceReached, 1), 1) / + (float)Math.Max (Character.Motion.ActivePath.TotalDistance, 1))); + } + try + { + CurrentCharacterVisual = ActiveScene.Frames [sequenceIndex].CharacterVisual; + } + catch (IndexOutOfRangeException) + { + CurrentCharacterVisual = ActiveScene.Frames [^1].CharacterVisual; + } + } + else + { + CurrentCharacterVisual = ActiveScene.Frames [^1].CharacterVisual; + ActiveScene.PlayedFrames.AddRange (ActiveScene.Frames); + ActiveScene.Frames.Clear (); + } + } + else if (ActiveScene.Ease != null) + { + float easingFactor = EaseAnimation (ActiveScene.Ease); + int frameIndex = (int)Math.Round (easingFactor * Math.Max (ActiveScene.EasingTotalSteps - 1, 0)); + frameIndex = Math.Max (Math.Min (frameIndex, ActiveScene.EasingTotalSteps - 1), 0); + Frame frame = ActiveScene.FrameIndexMap [frameIndex]; + CurrentCharacterVisual = frame.CharacterVisual; + ActiveScene.EasingCurrentStep++; + if (ActiveScene.EasingCurrentStep == ActiveScene.EasingTotalSteps) + { + if (ActiveScene.IsLooping) + { + ActiveScene.EasingCurrentStep = 0; + } + else + { + ActiveScene.PlayedFrames.AddRange (ActiveScene.Frames); + ActiveScene.Frames.Clear (); + } + } + } + else + { + CurrentCharacterVisual = ActiveScene.GetNextVisual (); + } + if (ActiveSceneIsComplete ()) + { + var completedScene = ActiveScene; + if (!ActiveScene.IsLooping) + { + ActiveScene.ResetScene (); + ActiveScene = null; + } + Character.EventHandler.HandleEvent (Event.SceneComplete, completedScene); + } + } + } + + public void ActivateScene (Scene scene) + { + ActiveScene = scene; + ActiveSceneCurrentStep = 0; + CurrentCharacterVisual = ActiveScene.Activate (); + Character.EventHandler.HandleEvent (Event.SceneActivated, scene); + } + + public void DeactivateScene (Scene scene) + { + if (ActiveScene == scene) + { + ActiveScene = null; + } + } +} + +public class EffectCharacter +{ + public string InputSymbol { get; } + public CharacterVisual CharacterVisual { get; set; } + public Animation Animation { get; set; } + + public EffectCharacter (string inputSymbol) + { + InputSymbol = inputSymbol; + CharacterVisual = new CharacterVisual (inputSymbol); + Animation = new Animation (this); + } + + public void Animate (string sceneId) + { + Animation.ActivateScene (sceneId); + Animation.IncrementScene (); + CharacterVisual = Animation.CurrentCharacterVisual; + } + + public void ResetEffects () + { + CharacterVisual.DisableModes (); + } +} + +public class Color +{ + public string RgbColor { get; } + public string XtermColor { get; } + + public Color (string rgbColor, string xtermColor) + { + RgbColor = rgbColor; + XtermColor = xtermColor; + } +} + +public class Gradient +{ + public List Spectrum { get; } + + public Gradient (List spectrum) + { + Spectrum = spectrum; + } +} + + +// Dummy classes for Ansitools, Colorterm, and Hexterm as placeholders +public static class Ansitools +{ + public static string ApplyBold () => "\x1b[1m"; + public static string ApplyItalic () => "\x1b[3m"; + public static string ApplyUnderline () => "\x1b[4m"; + public static string ApplyBlink () => "\x1b[5m"; + public static string ApplyReverse () => "\x1b[7m"; + public static string ApplyHidden () => "\x1b[8m"; + public static string ApplyStrikethrough () => "\x1b[9m"; + public static string ResetAll () => "\x1b[0m"; +} + +public static class Colorterm +{ + public static string Fg (string colorCode) => $"\x1b[38;5;{colorCode}m"; +} + +public static class Hexterm +{ + public static string HexToXterm (string hex) + { + // Convert hex color to xterm color code (0-255) + return "15"; // Example output + } +} diff --git a/Terminal.Gui/TextEffects/Easing.cs b/Terminal.Gui/TextEffects/Easing.cs new file mode 100644 index 000000000..3b63ca032 --- /dev/null +++ b/Terminal.Gui/TextEffects/Easing.cs @@ -0,0 +1,303 @@ +namespace Terminal.Gui.TextEffects; +using System; + +public delegate float EasingFunction (float progressRatio); + +public static class Easing +{ + public static float Linear (float progressRatio) + { + return progressRatio; + } + + public static float InSine (float progressRatio) + { + return 1 - (float)Math.Cos ((progressRatio * Math.PI) / 2); + } + + public static float OutSine (float progressRatio) + { + return (float)Math.Sin ((progressRatio * Math.PI) / 2); + } + + public static float InOutSine (float progressRatio) + { + return -(float)(Math.Cos (Math.PI * progressRatio) - 1) / 2; + } + + public static float InQuad (float progressRatio) + { + return progressRatio * progressRatio; + } + + public static float OutQuad (float progressRatio) + { + return 1 - (1 - progressRatio) * (1 - progressRatio); + } + + public static float InOutQuad (float progressRatio) + { + if (progressRatio < 0.5) + { + return 2 * progressRatio * progressRatio; + } + else + { + return 1 - (float)Math.Pow (-2 * progressRatio + 2, 2) / 2; + } + } + + public static float InCubic (float progressRatio) + { + return progressRatio * progressRatio * progressRatio; + } + + public static float OutCubic (float progressRatio) + { + return 1 - (float)Math.Pow (1 - progressRatio, 3); + } + + public static float InOutCubic (float progressRatio) + { + if (progressRatio < 0.5) + { + return 4 * progressRatio * progressRatio * progressRatio; + } + else + { + return 1 - (float)Math.Pow (-2 * progressRatio + 2, 3) / 2; + } + } + + public static float InQuart (float progressRatio) + { + return progressRatio * progressRatio * progressRatio * progressRatio; + } + + public static float OutQuart (float progressRatio) + { + return 1 - (float)Math.Pow (1 - progressRatio, 4); + } + + public static float InOutQuart (float progressRatio) + { + if (progressRatio < 0.5) + { + return 8 * progressRatio * progressRatio * progressRatio * progressRatio; + } + else + { + return 1 - (float)Math.Pow (-2 * progressRatio + 2, 4) / 2; + } + } + + public static float InQuint (float progressRatio) + { + return progressRatio * progressRatio * progressRatio * progressRatio * progressRatio; + } + + public static float OutQuint (float progressRatio) + { + return 1 - (float)Math.Pow (1 - progressRatio, 5); + } + + public static float InOutQuint (float progressRatio) + { + if (progressRatio < 0.5) + { + return 16 * progressRatio * progressRatio * progressRatio * progressRatio * progressRatio; + } + else + { + return 1 - (float)Math.Pow (-2 * progressRatio + 2, 5) / 2; + } + } + + public static float InExpo (float progressRatio) + { + if (progressRatio == 0) + { + return 0; + } + else + { + return (float)Math.Pow (2, 10 * progressRatio - 10); + } + } + + public static float OutExpo (float progressRatio) + { + if (progressRatio == 1) + { + return 1; + } + else + { + return 1 - (float)Math.Pow (2, -10 * progressRatio); + } + } + + public static float InOutExpo (float progressRatio) + { + if (progressRatio == 0) + { + return 0; + } + else if (progressRatio == 1) + { + return 1; + } + else if (progressRatio < 0.5) + { + return (float)Math.Pow (2, 20 * progressRatio - 10) / 2; + } + else + { + return (2 - (float)Math.Pow (2, -20 * progressRatio + 10)) / 2; + } + } + + public static float InCirc (float progressRatio) + { + return 1 - (float)Math.Sqrt (1 - progressRatio * progressRatio); + } + + public static float OutCirc (float progressRatio) + { + return (float)Math.Sqrt (1 - (progressRatio - 1) * (progressRatio - 1)); + } + + public static float InOutCirc (float progressRatio) + { + if (progressRatio < 0.5) + { + return (1 - (float)Math.Sqrt (1 - (2 * progressRatio) * (2 * progressRatio))) / 2; + } + else + { + return ((float)Math.Sqrt (1 - (-2 * progressRatio + 2) * (-2 * progressRatio + 2)) + 1) / 2; + } + } + + public static float InBack (float progressRatio) + { + const float c1 = 1.70158f; + const float c3 = c1 + 1; + return c3 * progressRatio * progressRatio * progressRatio - c1 * progressRatio * progressRatio; + } + + public static float OutBack (float progressRatio) + { + const float c1 = 1.70158f; + const float c3 = c1 + 1; + return 1 + c3 * (progressRatio - 1) * (progressRatio - 1) * (progressRatio - 1) + c1 * (progressRatio - 1) * (progressRatio - 1); + } + + public static float InOutBack (float progressRatio) + { + const float c1 = 1.70158f; + const float c2 = c1 * 1.525f; + if (progressRatio < 0.5) + { + return ((2 * progressRatio) * (2 * progressRatio) * ((c2 + 1) * 2 * progressRatio - c2)) / 2; + } + else + { + return ((2 * progressRatio - 2) * (2 * progressRatio - 2) * ((c2 + 1) * (progressRatio * 2 - 2) + c2) + 2) / 2; + } + } + + public static float InElastic (float progressRatio) + { + const float c4 = (2 * (float)Math.PI) / 3; + if (progressRatio == 0) + { + return 0; + } + else if (progressRatio == 1) + { + return 1; + } + else + { + return -(float)Math.Pow (2, 10 * progressRatio - 10) * (float)Math.Sin ((progressRatio * 10 - 10.75) * c4); + } + } + + public static float OutElastic (float progressRatio) + { + const float c4 = (2 * (float)Math.PI) / 3; + if (progressRatio == 0) + { + return 0; + } + else if (progressRatio == 1) + { + return 1; + } + else + { + return (float)Math.Pow (2, -10 * progressRatio) * (float)Math.Sin ((progressRatio * 10 - 0.75) * c4) + 1; + } + } + + public static float InOutElastic (float progressRatio) + { + const float c5 = (2 * (float)Math.PI) / 4.5f; + if (progressRatio == 0) + { + return 0; + } + else if (progressRatio == 1) + { + return 1; + } + else if (progressRatio < 0.5) + { + return -(float)Math.Pow (2, 20 * progressRatio - 10) * (float)Math.Sin ((20 * progressRatio - 11.125) * c5) / 2; + } + else + { + return ((float)Math.Pow (2, -20 * progressRatio + 10) * (float)Math.Sin ((20 * progressRatio - 11.125) * c5)) / 2 + 1; + } + } + + public static float InBounce (float progressRatio) + { + return 1 - OutBounce (1 - progressRatio); + } + + public static float OutBounce (float progressRatio) + { + const float n1 = 7.5625f; + const float d1 = 2.75f; + if (progressRatio < 1 / d1) + { + return n1 * progressRatio * progressRatio; + } + else if (progressRatio < 2 / d1) + { + return n1 * (progressRatio - 1.5f / d1) * (progressRatio - 1.5f / d1) + 0.75f; + } + else if (progressRatio < 2.5 / d1) + { + return n1 * (progressRatio - 2.25f / d1) * (progressRatio - 2.25f / d1) + 0.9375f; + } + else + { + return n1 * (progressRatio - 2.625f / d1) * (progressRatio - 2.625f / d1) + 0.984375f; + } + } + + public static float InOutBounce (float progressRatio) + { + if (progressRatio < 0.5) + { + return (1 - OutBounce (1 - 2 * progressRatio)) / 2; + } + else + { + return (1 + OutBounce (2 * progressRatio - 1)) / 2; + } + } +} diff --git a/UnitTests/TextEffects/AnimationTests.cs b/UnitTests/TextEffects/AnimationTests.cs new file mode 100644 index 000000000..0a00b2e7c --- /dev/null +++ b/UnitTests/TextEffects/AnimationTests.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using Terminal.Gui.TextEffects; +using Xunit; + +namespace Terminal.Gui.TextEffectsTests; + +public class AnimationTests +{ + private EffectCharacter character; + + public AnimationTests () + { + character = new EffectCharacter (0, "a", 0, 0); + } + + [Fact] + public void TestCharacterVisualInit () + { + var visual = new CharacterVisual ( + symbol: "a", + bold: true, + dim: false, + italic: true, + underline: false, + blink: true, + reverse: false, + hidden: true, + strike: false, + color: new Color ("ffffff"), + colorCode: "ffffff" + ); + Assert.Equal ("\x1b[1m\x1b[3m\x1b[5m\x1b[8m\x1b[38;2;255;255;255ma\x1b[0m", visual.FormattedSymbol); + Assert.True (visual.Bold); + Assert.False (visual.Dim); + Assert.True (visual.Italic); + Assert.False (visual.Underline); + Assert.True (visual.Blink); + Assert.False (visual.Reverse); + Assert.True (visual.Hidden); + Assert.False (visual.Strike); + Assert.Equal (new Color ("ffffff"), visual.Color); + Assert.Equal ("ffffff", visual.ColorCode); + } + + [Fact] + public void TestFrameInit () + { + var visual = new CharacterVisual ( + symbol: "a", + bold: true, + dim: false, + italic: true, + underline: false, + blink: true, + reverse: false, + hidden: true, + strike: false, + color: new Color ("ffffff") + ); + var frame = new Frame (characterVisual: visual, duration: 5); + Assert.Equal (visual, frame.CharacterVisual); + Assert.Equal (5, frame.Duration); + Assert.Equal (0, frame.TicksElapsed); + } + + [Fact] + public void TestSceneInit () + { + var scene = new Scene (sceneId: "test_scene", isLooping: true, sync: SyncMetric.Step, ease: Easing.InSine); + Assert.Equal ("test_scene", scene.SceneId); + Assert.True (scene.IsLooping); + Assert.Equal (SyncMetric.Step, scene.Sync); + Assert.Equal (Easing.InSine, scene.Ease); + } + + [Fact] + public void TestSceneAddFrame () + { + var scene = new Scene (sceneId: "test_scene"); + scene.AddFrame (symbol: "a", duration: 5, color: new Color ("ffffff"), bold: true, italic: true, blink: true, hidden: true); + Assert.Single (scene.Frames); + var frame = scene.Frames [0]; + Assert.Equal ("\x1b[1m\x1b[3m\x1b[5m\x1b[8m\x1b[38;2;255;255;255ma\x1b[0m", frame.CharacterVisual.FormattedSymbol); + Assert.Equal (5, frame.Duration); + Assert.Equal (new Color ("ffffff"), frame.CharacterVisual.Color); + Assert.True (frame.CharacterVisual.Bold); + } + + [Fact] + public void TestSceneAddFrameInvalidDuration () + { + var scene = new Scene (sceneId: "test_scene"); + var exception = Assert.Throws (() => scene.AddFrame (symbol: "a", duration: 0, color: new Color ("ffffff"))); + Assert.Equal ("duration must be greater than 0", exception.Message); + } + + [Fact] + public void TestSceneApplyGradientToSymbolsEqualColorsAndSymbols () + { + var scene = new Scene (sceneId: "test_scene"); + var gradient = new Gradient (new Color ("000000"), new Color ("ffffff"), steps: 2); + var symbols = new List { "a", "b", "c" }; + scene.ApplyGradientToSymbols (gradient, symbols, duration: 1); + Assert.Equal (3, scene.Frames.Count); + for (int i = 0; i < scene.Frames.Count; i++) + { + Assert.Equal (1, scene.Frames [i].Duration); + Assert.Equal (gradient.Spectrum [i].RgbColor, scene.Frames [i].CharacterVisual.ColorCode); + } + } + + [Fact] + public void TestSceneApplyGradientToSymbolsUnequalColorsAndSymbols () + { + var scene = new Scene (sceneId: "test_scene"); + var gradient = new Gradient (new Color ("000000"), new Color ("ffffff"), steps: 4); + var symbols = new List { "q", "z" }; + scene.ApplyGradientToSymbols (gradient, symbols, duration: 1); + Assert.Equal (5, scene.Frames.Count); + Assert.Equal (gradient.Spectrum [0].RgbColor, scene.Frames [0].CharacterVisual.ColorCode); + Assert.Contains ("q", scene.Frames [0].CharacterVisual.Symbol); + Assert.Equal (gradient.Spectrum [^1].RgbColor, scene.Frames [^1].CharacterVisual.ColorCode); + Assert.Contains ("z", scene.Frames [^1].CharacterVisual.Symbol); + } + + [Fact] + public void TestAnimationInit () + { + var animation = character.Animation; + Assert.Equal (character, animation.Character); + Assert.Empty (animation.Scenes); + Assert.Null (animation.ActiveScene); + Assert.False (animation.UseXtermColors); + Assert.False (animation.NoColor); + Assert.Empty (animation.XtermColorMap); + Assert.Equal (0, animation.ActiveSceneCurrentStep); + } + + [Fact] + public void TestAnimationNewScene () + { + var animation = character.Animation; + var scene = animation.NewScene ("test_scene", isLooping: true); + Assert.IsType (scene); + Assert.Equal ("test_scene", scene.SceneId); + Assert.True (scene.IsLooping); + Assert.True (animation.Scenes.ContainsKey ("test_scene")); + } + + [Fact] + public void TestAnimationNewSceneWithoutId () + { + var animation = character.Animation; + var scene = animation.NewScene (); + Assert.IsType (scene); + Assert.Equal ("0", scene.SceneId); + Assert.True (animation.Scenes.ContainsKey ("0")); + } + + [Fact] + public void TestAnimationQueryScene () + { + var animation = character.Animation; + var scene = animation.NewScene ("test_scene", isLooping: true); + Assert.Equal (scene, animation.QueryScene ("test_scene")); + } + + [Fact] + public void TestAnimationLoopingActiveSceneIsComplete () + { + var animation = character.Animation; + var scene = animation.NewScene ("test_scene", isLooping: true); + scene.AddFrame (symbol: "a", duration: 2); + animation.ActivateScene (scene); + Assert.True (animation.ActiveSceneIsComplete ()); + } + + [Fact] + public void TestAnimationNonLoopingActiveSceneIsComplete () + { + var animation = character.Animation; + var scene = animation.NewScene ("test_scene"); + scene.AddFrame (symbol: "a", duration: 1); + animation.ActivateScene (scene); + Assert.False (animation.ActiveSceneIsComplete ()); + animation.StepAnimation (); + Assert.True (animation.ActiveSceneIsComplete ()); + } +} \ No newline at end of file From 7d62ad272a2dc3f1c0cb0f14ad1c0ce34775bb55 Mon Sep 17 00:00:00 2001 From: tznind Date: Fri, 5 Jul 2024 20:25:13 +0100 Subject: [PATCH 02/48] More random code ported --- Terminal.Gui/TextEffects/Animation.cs | 125 +++--------- Terminal.Gui/TextEffects/BaseCharacter.cs | 107 ++++++++++ Terminal.Gui/TextEffects/BaseEffect.cs | 74 +++++++ Terminal.Gui/TextEffects/EffectTemplate.cs | 58 ++++++ Terminal.Gui/TextEffects/Geometry.cs | 137 +++++++++++++ Terminal.Gui/TextEffects/Graphics.cs | 126 ++++++++++++ Terminal.Gui/TextEffects/Motion.cs | 216 +++++++++++++++++++++ Terminal.Gui/TextEffects/Terminal.cs | 106 ++++++++++ 8 files changed, 856 insertions(+), 93 deletions(-) create mode 100644 Terminal.Gui/TextEffects/BaseCharacter.cs create mode 100644 Terminal.Gui/TextEffects/BaseEffect.cs create mode 100644 Terminal.Gui/TextEffects/EffectTemplate.cs create mode 100644 Terminal.Gui/TextEffects/Geometry.cs create mode 100644 Terminal.Gui/TextEffects/Graphics.cs create mode 100644 Terminal.Gui/TextEffects/Motion.cs create mode 100644 Terminal.Gui/TextEffects/Terminal.cs diff --git a/Terminal.Gui/TextEffects/Animation.cs b/Terminal.Gui/TextEffects/Animation.cs index c7e17fff0..210329ce2 100644 --- a/Terminal.Gui/TextEffects/Animation.cs +++ b/Terminal.Gui/TextEffects/Animation.cs @@ -109,6 +109,8 @@ public class Scene Ease = ease; NoColor = noColor; UseXtermColors = useXtermColors; + EasingTotalSteps = 0; + EasingCurrentStep = 0; } public void AddFrame (string symbol, int duration, Color color = null, bool bold = false, bool dim = false, bool italic = false, bool underline = false, bool blink = false, bool reverse = false, bool hidden = false, bool strike = false) @@ -120,23 +122,13 @@ public class Scene { charVisColor = null; } - else if (UseXtermColors) + else if (UseXtermColors && color.XtermColor.HasValue) { - if (color.XtermColor != null) - { - charVisColor = color.XtermColor; - } - else if (XtermColorMap.ContainsKey (color.RgbColor)) - { - // Build error says Error CS0029 Cannot implicitly convert type 'int' to 'string' Terminal.Gui (net8.0) D:\Repos\TerminalGuiDesigner\gui.cs\Terminal.Gui\TextEffects\Animation.cs 120 Active - charVisColor = XtermColorMap [color.RgbColor].ToString (); - } - else - { - var xtermColor = Hexterm.HexToXterm (color.RgbColor); - XtermColorMap [color.RgbColor] = int.Parse (xtermColor); - charVisColor = xtermColor; - } + charVisColor = color.XtermColor.Value.ToString (); + } + else if (color.RgbColor != null && XtermColorMap.ContainsKey (color.RgbColor)) + { + charVisColor = XtermColorMap [color.RgbColor].ToString (); } else { @@ -145,12 +137,10 @@ public class Scene } if (duration < 1) - { - throw new ArgumentException ("duration must be greater than 0"); - } + throw new ArgumentException ("Duration must be greater than 0."); - var charVis = new CharacterVisual (symbol, bold, dim, italic, underline, blink, reverse, hidden, strike, color, charVisColor); - var frame = new Frame (charVis, duration); + var characterVisual = new CharacterVisual (symbol, bold, dim, italic, underline, blink, reverse, hidden, strike, color, charVisColor); + var frame = new Frame (characterVisual, duration); Frames.Add (frame); for (int i = 0; i < frame.Duration; i++) { @@ -161,33 +151,32 @@ public class Scene public CharacterVisual Activate () { - if (Frames.Count > 0) - { - return Frames [0].CharacterVisual; - } - else - { + if (Frames.Count == 0) throw new InvalidOperationException ("Scene has no frames."); - } + EasingCurrentStep = 0; + return Frames [0].CharacterVisual; } public CharacterVisual GetNextVisual () { - var currentFrame = Frames [0]; - var nextVisual = currentFrame.CharacterVisual; - currentFrame.IncrementTicks (); - if (currentFrame.TicksElapsed == currentFrame.Duration) + if (Frames.Count == 0) + return null; + + var frame = Frames [0]; + if (++EasingCurrentStep >= frame.Duration) { - currentFrame.TicksElapsed = 0; - PlayedFrames.Add (Frames [0]); + EasingCurrentStep = 0; + PlayedFrames.Add (frame); Frames.RemoveAt (0); if (IsLooping && Frames.Count == 0) { Frames.AddRange (PlayedFrames); PlayedFrames.Clear (); } + if (Frames.Count > 0) + return Frames [0].CharacterVisual; } - return nextVisual; + return frame.CharacterVisual; } public void ApplyGradientToSymbols (Gradient gradient, IList symbols, int duration) @@ -208,11 +197,7 @@ public class Scene public void ResetScene () { - foreach (var sequence in Frames) - { - sequence.TicksElapsed = 0; - PlayedFrames.Add (sequence); - } + EasingCurrentStep = 0; Frames.Clear (); Frames.AddRange (PlayedFrames); PlayedFrames.Clear (); @@ -220,11 +205,7 @@ public class Scene public override bool Equals (object obj) { - if (obj is Scene other) - { - return SceneId == other.SceneId; - } - return false; + return obj is Scene other && SceneId == other.SceneId; } public override int GetHashCode () @@ -306,14 +287,14 @@ public class Animation } else if (UseXtermColors) { - charVisColor = color.XtermColor; + charVisColor = color.XtermColor.ToString(); } else { charVisColor = color.RgbColor; } } - CurrentCharacterVisual = new CharacterVisual (symbol, color: color, _colorCode: charVisColor); + CurrentCharacterVisual = new CharacterVisual (symbol, color: color, colorCode: charVisColor); } public static Color RandomColor () @@ -489,55 +470,13 @@ public class Animation } } -public class EffectCharacter +// Dummy Enum for Event handling +public enum Event { - public string InputSymbol { get; } - public CharacterVisual CharacterVisual { get; set; } - public Animation Animation { get; set; } - - public EffectCharacter (string inputSymbol) - { - InputSymbol = inputSymbol; - CharacterVisual = new CharacterVisual (inputSymbol); - Animation = new Animation (this); - } - - public void Animate (string sceneId) - { - Animation.ActivateScene (sceneId); - Animation.IncrementScene (); - CharacterVisual = Animation.CurrentCharacterVisual; - } - - public void ResetEffects () - { - CharacterVisual.DisableModes (); - } + SceneComplete, + SceneActivated } -public class Color -{ - public string RgbColor { get; } - public string XtermColor { get; } - - public Color (string rgbColor, string xtermColor) - { - RgbColor = rgbColor; - XtermColor = xtermColor; - } -} - -public class Gradient -{ - public List Spectrum { get; } - - public Gradient (List spectrum) - { - Spectrum = spectrum; - } -} - - // Dummy classes for Ansitools, Colorterm, and Hexterm as placeholders public static class Ansitools { diff --git a/Terminal.Gui/TextEffects/BaseCharacter.cs b/Terminal.Gui/TextEffects/BaseCharacter.cs new file mode 100644 index 000000000..329775478 --- /dev/null +++ b/Terminal.Gui/TextEffects/BaseCharacter.cs @@ -0,0 +1,107 @@ +namespace Terminal.Gui.TextEffects; + +public class EffectCharacter +{ + public int CharacterId { get; } + public string InputSymbol { get; } + public Coord InputCoord { get; } + public bool IsVisible { get; set; } + public Animation Animation { get; } + public Motion Motion { get; } + public EventHandler EventHandler { get; } + public int Layer { get; set; } + public bool IsFillCharacter { get; set; } + + public EffectCharacter (int characterId, string symbol, int inputColumn, int inputRow) + { + CharacterId = characterId; + InputSymbol = symbol; + InputCoord = new Coord (inputColumn, inputRow); + IsVisible = false; + Animation = new Animation (this); + Motion = new Motion (this); + EventHandler = new EventHandler (this); + Layer = 0; + IsFillCharacter = false; + } + + public bool IsActive => !Animation.ActiveSceneIsComplete() || !Motion.MovementIsComplete (); + + public void Tick () + { + Motion.Move (); + Animation.StepAnimation (); + } +} + +public class EventHandler +{ + public EffectCharacter Character { get; } + public Dictionary<(Event, object), List<(Action, object)>> RegisteredEvents { get; } + + public EventHandler (EffectCharacter character) + { + Character = character; + RegisteredEvents = new Dictionary<(Event, object), List<(Action, object)>> (); + } + + public void RegisterEvent (Event @event, object caller, Action action, object target) + { + var key = (@event, caller); + if (!RegisteredEvents.ContainsKey (key)) + RegisteredEvents [key] = new List<(Action, object)> (); + + RegisteredEvents [key].Add ((action, target)); + } + + public void HandleEvent (Event @event, object caller) + { + var key = (@event, caller); + if (!RegisteredEvents.ContainsKey (key)) + return; + + foreach (var (action, target) in RegisteredEvents [key]) + { + switch (action) + { + case Action.ActivatePath: + Character.Motion.ActivatePath (target as Path); + break; + case Action.DeactivatePath: + Character.Motion.DeactivatePath (target as Path); + break; + case Action.SetLayer: + Character.Layer = (int)target; + break; + case Action.SetCoordinate: + Character.Motion.CurrentCoord = (Coord)target; + break; + case Action.Callback: + (target as Action)?.Invoke (); + break; + default: + throw new ArgumentOutOfRangeException (nameof (action), "Unhandled action."); + } + } + } + + public enum Event + { + SegmentEntered, + SegmentExited, + PathActivated, + PathComplete, + PathHolding, + SceneActivated, + SceneComplete + } + + public enum Action + { + ActivatePath, + DeactivatePath, + SetLayer, + SetCoordinate, + Callback + } +} diff --git a/Terminal.Gui/TextEffects/BaseEffect.cs b/Terminal.Gui/TextEffects/BaseEffect.cs new file mode 100644 index 000000000..5855f8979 --- /dev/null +++ b/Terminal.Gui/TextEffects/BaseEffect.cs @@ -0,0 +1,74 @@ +namespace Terminal.Gui.TextEffects; + +public abstract class BaseEffectIterator : IEnumerable where T : EffectConfig +{ + protected T Config { get; set; } + protected Terminal Terminal { get; set; } + protected List ActiveCharacters { get; set; } = new List (); + + public BaseEffectIterator (BaseEffect effect) + { + Config = effect.EffectConfig; + Terminal = new Terminal (effect.InputData, effect.TerminalConfig); + } + + public string Frame => Terminal.GetFormattedOutputString (); + + public void Update () + { + foreach (var character in ActiveCharacters) + { + character.Tick (); + } + ActiveCharacters.RemoveAll (character => !character.IsActive); + } + + public IEnumerator GetEnumerator () + { + return this; + } + + IEnumerator IEnumerable.GetEnumerator () + { + return GetEnumerator (); + } + + public abstract string Next (); +} + +public abstract class BaseEffect where T : EffectConfig, new() +{ + public string InputData { get; set; } + public T EffectConfig { get; set; } + public TerminalConfig TerminalConfig { get; set; } + + protected BaseEffect (string inputData) + { + InputData = inputData; + EffectConfig = new T (); + TerminalConfig = new TerminalConfig (); + } + + public abstract Type IteratorClass { get; } + + public IEnumerator GetEnumerator () + { + var iterator = (BaseEffectIterator)Activator.CreateInstance (IteratorClass, this); + return iterator; + } + + public IDisposable TerminalOutput (string endSymbol = "\n") + { + var terminal = new Terminal (InputData, TerminalConfig); + terminal.PrepCanvas (); + try + { + return terminal; + } + finally + { + terminal.RestoreCursor (endSymbol); + } + } +} + diff --git a/Terminal.Gui/TextEffects/EffectTemplate.cs b/Terminal.Gui/TextEffects/EffectTemplate.cs new file mode 100644 index 000000000..c16dbccb9 --- /dev/null +++ b/Terminal.Gui/TextEffects/EffectTemplate.cs @@ -0,0 +1,58 @@ +namespace Terminal.Gui.TextEffects; + +public class EffectConfig +{ + public Color ColorSingle { get; set; } + public List ColorList { get; set; } + public Color FinalColor { get; set; } + public List FinalGradientStops { get; set; } + public List FinalGradientSteps { get; set; } + public int FinalGradientFrames { get; set; } + public float MovementSpeed { get; set; } + public EasingFunction Easing { get; set; } +} + +public class NamedEffectIterator : BaseEffectIterator +{ + public NamedEffectIterator (NamedEffect effect) : base (effect) + { + Build (); + } + + private void Build () + { + var finalGradient = new Gradient (Config.FinalGradientStops, Config.FinalGradientSteps); + foreach (var character in Terminal.GetCharacters ()) + { + CharacterFinalColorMap [character] = finalGradient.GetColorAtFraction ( + character.InputCoord.Row / (float)Terminal.Canvas.Top + ); + } + } + + public override string Next () + { + if (PendingChars.Any () || ActiveCharacters.Any ()) + { + Update (); + return Frame; + } + else + { + throw new InvalidOperationException ("No more elements in effect iterator."); + } + } + + private List PendingChars = new List (); + private Dictionary CharacterFinalColorMap = new Dictionary (); +} + +public class NamedEffect : BaseEffect +{ + public NamedEffect (string inputData) : base (inputData) + { + } + + protected override Type ConfigCls => typeof (EffectConfig); + protected override Type IteratorCls => typeof (NamedEffectIterator); +} diff --git a/Terminal.Gui/TextEffects/Geometry.cs b/Terminal.Gui/TextEffects/Geometry.cs new file mode 100644 index 000000000..5de2f7073 --- /dev/null +++ b/Terminal.Gui/TextEffects/Geometry.cs @@ -0,0 +1,137 @@ +namespace Terminal.Gui.TextEffects; + + +public static class GeometryUtils +{ + + public static List FindCoordsOnCircle (Coord origin, int radius, int coordsLimit = 0, bool unique = true) + { + var points = new List (); + var seenPoints = new HashSet (); + if (coordsLimit == 0) + coordsLimit = (int)Math.Ceiling (2 * Math.PI * radius); + double angleStep = 2 * Math.PI / coordsLimit; + + for (int i = 0; i < coordsLimit; i++) + { + double angle = i * angleStep; + int x = (int)(origin.Column + radius * Math.Cos (angle)); + int y = (int)(origin.Row + radius * Math.Sin (angle)); + var coord = new Coord (x, y); + + if (unique && !seenPoints.Contains (coord)) + { + points.Add (coord); + seenPoints.Add (coord); + } + else if (!unique) + { + points.Add (coord); + } + } + + return points; + } + + public static List FindCoordsInCircle (Coord center, int diameter) + { + var coordsInEllipse = new List (); + int radius = diameter / 2; + for (int x = center.Column - radius; x <= center.Column + radius; x++) + { + for (int y = center.Row - radius; y <= center.Row + radius; y++) + { + if (Math.Pow (x - center.Column, 2) + Math.Pow (y - center.Row, 2) <= Math.Pow (radius, 2)) + coordsInEllipse.Add (new Coord (x, y)); + } + } + return coordsInEllipse; + } + + public static List FindCoordsInRect (Coord origin, int distance) + { + var coords = new List (); + for (int column = origin.Column - distance; column <= origin.Column + distance; column++) + { + for (int row = origin.Row - distance; row <= origin.Row + distance; row++) + { + coords.Add (new Coord (column, row)); + } + } + return coords; + } + + public static Coord FindCoordAtDistance (Coord origin, Coord target, double distance) + { + double totalDistance = FindLengthOfLine (origin, target) + distance; + double t = distance / totalDistance; + int nextColumn = (int)((1 - t) * origin.Column + t * target.Column); + int nextRow = (int)((1 - t) * origin.Row + t * target.Row); + return new Coord (nextColumn, nextRow); + } + + public static Coord FindCoordOnBezierCurve (Coord start, List controlPoints, Coord end, double t) + { + // Implementing De Casteljau's algorithm for Bezier curve + if (controlPoints.Count == 1) // Quadratic + { + double x = Math.Pow (1 - t, 2) * start.Column + + 2 * (1 - t) * t * controlPoints [0].Column + + Math.Pow (t, 2) * end.Column; + double y = Math.Pow (1 - t, 2) * start.Row + + 2 * (1 - t) * t * controlPoints [0].Row + + Math.Pow (t, 2) * end.Row; + return new Coord ((int)x, (int)y); + } + else if (controlPoints.Count == 2) // Cubic + { + double x = Math.Pow (1 - t, 3) * start.Column + + 3 * Math.Pow (1 - t, 2) * t * controlPoints [0].Column + + 3 * (1 - t) * Math.Pow (t, 2) * controlPoints [1].Column + + Math.Pow (t, 3) * end.Column; + double y = Math.Pow (1 - t, 3) * start.Row + + 3 * Math.Pow (1 - t, 2) * t * controlPoints [0].Row + + 3 * (1 - t) * Math.Pow (t, 2) * controlPoints [1].Row + + Math.Pow (t, 3) * end.Row; + return new Coord ((int)x, (int)y); + } + throw new ArgumentException ("Invalid number of control points for bezier curve"); + } + + public static Coord FindCoordOnLine (Coord start, Coord end, double t) + { + int x = (int)((1 - t) * start.Column + t * end.Column); + int y = (int)((1 - t) * start.Row + t * end.Row); + return new Coord (x, y); + } + + public static double FindLengthOfBezierCurve (Coord start, List controlPoints, Coord end) + { + double length = 0.0; + Coord prevCoord = start; + for (int i = 1; i <= 10; i++) + { + double t = i / 10.0; + Coord coord = FindCoordOnBezierCurve (start, controlPoints, end, t); + length += FindLengthOfLine (prevCoord, coord); + prevCoord = coord; + } + return length; + } + + public static double FindLengthOfLine (Coord coord1, Coord coord2) + { + return Math.Sqrt (Math.Pow (coord2.Column - coord1.Column, 2) + + Math.Pow (coord2.Row - coord1.Row, 2)); + } + + public static double FindNormalizedDistanceFromCenter (int maxRow, int maxColumn, Coord otherCoord) + { + double center_x = maxColumn / 2.0; + double center_y = maxRow / 2.0; + double maxDistance = Math.Sqrt (Math.Pow (maxColumn, 2) + Math.Pow (maxRow, 2)); + double distance = Math.Sqrt (Math.Pow (otherCoord.Column - center_x, 2) + + Math.Pow (otherCoord.Row - center_y, 2)); + return distance / (maxDistance / 2); + } +} \ No newline at end of file diff --git a/Terminal.Gui/TextEffects/Graphics.cs b/Terminal.Gui/TextEffects/Graphics.cs new file mode 100644 index 000000000..e03063a72 --- /dev/null +++ b/Terminal.Gui/TextEffects/Graphics.cs @@ -0,0 +1,126 @@ +namespace Terminal.Gui.TextEffects; +using System; +using System.Collections.Generic; +using System.Linq; + +public class Color +{ + public string RgbColor { get; } + public int? XtermColor { get; } + + public Color (string rgbColor) + { + if (!IsValidHexColor (rgbColor)) + throw new ArgumentException ("Invalid RGB hex color format."); + + RgbColor = rgbColor.StartsWith ("#") ? rgbColor.Substring (1) : rgbColor; + XtermColor = null; // Convert RGB to XTerm-256 here if necessary + } + + public Color (int xtermColor) + { + XtermColor = xtermColor; + RgbColor = ConvertXtermToHex (xtermColor); // Implement conversion logic + } + + private bool IsValidHexColor (string color) + { + if (color.StartsWith ("#")) + { + color = color.Substring (1); + } + return color.Length == 6 && int.TryParse (color, System.Globalization.NumberStyles.HexNumber, null, out _); + } + + private string ConvertXtermToHex (int xtermColor) + { + // Dummy conversion for the sake of example + return "000000"; // Actual conversion logic needed + } + + public (int, int, int) GetRgbInts () + { + int r = Convert.ToInt32 (RgbColor.Substring (0, 2), 16); + int g = Convert.ToInt32 (RgbColor.Substring (2, 2), 16); + int b = Convert.ToInt32 (RgbColor.Substring (4, 2), 16); + return (r, g, b); + } + + public override string ToString () + { + return $"#{RgbColor.ToUpper ()}"; + } + + public override bool Equals (object obj) + { + return obj is Color other && RgbColor == other.RgbColor; + } + + public override int GetHashCode () + { + return HashCode.Combine (RgbColor); + } +} +public class Gradient +{ + public List Spectrum { get; private set; } + + public Gradient (IEnumerable stops, IEnumerable steps, bool loop = false) + { + if (stops == null || stops.Count () < 2) + throw new ArgumentException ("At least two color stops are required to create a gradient."); + if (steps == null || !steps.Any ()) + throw new ArgumentException ("Steps are required to define the transitions between colors."); + + Spectrum = GenerateGradient (stops.ToList (), steps.ToList (), loop); + } + + private List GenerateGradient (List stops, List steps, bool loop) + { + List gradient = new List (); + if (loop) + stops.Add (stops [0]); // Loop the gradient back to the first color. + + for (int i = 0; i < stops.Count - 1; i++) + { + gradient.AddRange (InterpolateColors (stops [i], stops [i + 1], i < steps.Count ? steps [i] : steps.Last ())); + } + + return gradient; + } + + private IEnumerable InterpolateColors (Color start, Color end, int steps) + { + for (int step = 0; step <= steps; step++) + { + int r = Interpolate (start.GetRgbInts ().Item1, end.GetRgbInts ().Item1, steps, step); + int g = Interpolate (start.GetRgbInts ().Item2, end.GetRgbInts ().Item2, steps, step); + int b = Interpolate (start.GetRgbInts ().Item3, end.GetRgbInts ().Item3, steps, step); + yield return new Color ($"#{r:X2}{g:X2}{b:X2}"); + } + } + + private int Interpolate (int start, int end, int steps, int currentStep) + { + return start + (end - start) * currentStep / steps; + } + + public Color GetColorAtFraction (double fraction) + { + if (fraction < 0 || fraction > 1) + throw new ArgumentOutOfRangeException (nameof (fraction), "Fraction must be between 0 and 1."); + int index = (int)(fraction * (Spectrum.Count - 1)); + return Spectrum [index]; + } + + public IEnumerable GetRange (int startIndex, int count) + { + return Spectrum.Skip (startIndex).Take (count); + } + + public override string ToString () + { + return $"Gradient with {Spectrum.Count} colors."; + } +} + diff --git a/Terminal.Gui/TextEffects/Motion.cs b/Terminal.Gui/TextEffects/Motion.cs new file mode 100644 index 000000000..ae8c06188 --- /dev/null +++ b/Terminal.Gui/TextEffects/Motion.cs @@ -0,0 +1,216 @@ +namespace Terminal.Gui.TextEffects; + +public class Coord +{ + public int Column { get; set; } + public int Row { get; set; } + + public Coord (int column, int row) + { + Column = column; + Row = row; + } + + public override string ToString () => $"({Column}, {Row})"; +} + +public class Waypoint +{ + public string WaypointId { get; set; } + public Coord Coord { get; set; } + public List BezierControl { get; set; } + + public Waypoint (string waypointId, Coord coord, List bezierControl = null) + { + WaypointId = waypointId; + Coord = coord; + BezierControl = bezierControl ?? new List (); + } +} + +public class Segment +{ + public Waypoint Start { get; private set; } + public Waypoint End { get; private set; } + public double Distance { get; private set; } + public bool EnterEventTriggered { get; set; } + public bool ExitEventTriggered { get; set; } + + public Segment (Waypoint start, Waypoint end) + { + Start = start; + End = end; + Distance = CalculateDistance (start, end); + } + + private double CalculateDistance (Waypoint start, Waypoint end) + { + // Add bezier control point distance calculation if needed + return Math.Sqrt (Math.Pow (end.Coord.Column - start.Coord.Column, 2) + Math.Pow (end.Coord.Row - start.Coord.Row, 2)); + } + + public Coord GetCoordOnSegment (double distanceFactor) + { + int column = (int)(Start.Coord.Column + (End.Coord.Column - Start.Coord.Column) * distanceFactor); + int row = (int)(Start.Coord.Row + (End.Coord.Row - Start.Coord.Row) * distanceFactor); + return new Coord (column, row); + } +} + +public class Path +{ + public string PathId { get; private set; } + public double Speed { get; set; } + public Func EaseFunction { get; set; } + public int Layer { get; set; } + public int HoldTime { get; set; } + public bool Loop { get; set; } + public List Segments { get; private set; } = new List (); + public int CurrentStep { get; set; } + public double TotalDistance { get; set; } + public double LastDistanceReached { get; set; } + + public Path (string pathId, double speed, Func easeFunction = null, int layer = 0, int holdTime = 0, bool loop = false) + { + PathId = pathId; + Speed = speed; + EaseFunction = easeFunction; + Layer = layer; + HoldTime = holdTime; + Loop = loop; + } + + public void AddWaypoint (Waypoint waypoint) + { + if (Segments.Count > 0) + { + var lastSegment = Segments.Last (); + var newSegment = new Segment (lastSegment.End, waypoint); + Segments.Add (newSegment); + TotalDistance += newSegment.Distance; + } + else + { + var originWaypoint = new Waypoint ("origin", new Coord (0, 0)); // Assuming the path starts at origin + var initialSegment = new Segment (originWaypoint, waypoint); + Segments.Add (initialSegment); + TotalDistance = initialSegment.Distance; + } + } + + public Coord Step () + { + if (EaseFunction != null && CurrentStep <= TotalDistance) + { + double progress = EaseFunction ((double)CurrentStep / TotalDistance); + double distanceTravelled = TotalDistance * progress; + LastDistanceReached = distanceTravelled; + + foreach (var segment in Segments) + { + if (distanceTravelled <= segment.Distance) + { + double segmentProgress = distanceTravelled / segment.Distance; + return segment.GetCoordOnSegment (segmentProgress); + } + + distanceTravelled -= segment.Distance; + } + } + + return Segments.Last ().End.Coord; // Return the end of the last segment if out of bounds + } +} + +public class Motion +{ + public Dictionary Paths { get; private set; } = new Dictionary (); + public Path ActivePath { get; private set; } + public Coord CurrentCoord { get; set; } + public Coord PreviousCoord { get; set; } + public EffectCharacter Character { get; private set; } // Assuming EffectCharacter is similar to base_character.EffectCharacter + + public Motion (EffectCharacter character) + { + Character = character; + CurrentCoord = new Coord (character.InputCoord.Column, character.InputCoord.Row); // Assuming similar properties + PreviousCoord = new Coord (-1, -1); + } + + public void SetCoordinate (Coord coord) + { + CurrentCoord = coord; + } + + public Path CreatePath (string pathId, double speed, Func easeFunction = null, int layer = 0, int holdTime = 0, bool loop = false) + { + if (Paths.ContainsKey (pathId)) + throw new ArgumentException ($"A path with ID {pathId} already exists."); + + var path = new Path (pathId, speed, easeFunction, layer, holdTime, loop); + Paths [pathId] = path; + return path; + } + + public Path QueryPath (string pathId) + { + if (!Paths.TryGetValue (pathId, out var path)) + throw new KeyNotFoundException ($"No path found with ID {pathId}."); + + return path; + } + + public bool MovementIsComplete () + { + return ActivePath == null || ActivePath.CurrentStep >= ActivePath.TotalDistance; + } + + public void ActivatePath (Path path) + { + if (path == null) + throw new ArgumentNullException (nameof (path), "Path cannot be null when activating."); + + ActivePath = path; + ActivePath.CurrentStep = 0; // Reset the path's progress + } + + public void DeactivatePath () + { + ActivePath = null; + } + + public void Move () + { + if (ActivePath != null) + { + PreviousCoord = CurrentCoord; + CurrentCoord = ActivePath.Step (); + ActivePath.CurrentStep++; + + if (ActivePath.CurrentStep >= ActivePath.TotalDistance) + { + if (ActivePath.Loop) + ActivePath.CurrentStep = 0; // Reset the path for looping + else + DeactivatePath (); // Deactivate the path if it is not set to loop + } + } + } + + public void ChainPaths (IEnumerable paths, bool loop = false) + { + var pathList = paths.ToList (); + for (int i = 0; i < pathList.Count; i++) + { + var currentPath = pathList [i]; + var nextPath = i + 1 < pathList.Count ? pathList [i + 1] : pathList.FirstOrDefault (); + + // Here we could define an event system to trigger path activation when another completes + // For example, you could listen for a "path complete" event and then activate the next path + if (loop && nextPath != null) + { + // Implementation depends on your event system + } + } + } +} diff --git a/Terminal.Gui/TextEffects/Terminal.cs b/Terminal.Gui/TextEffects/Terminal.cs new file mode 100644 index 000000000..9302f0d47 --- /dev/null +++ b/Terminal.Gui/TextEffects/Terminal.cs @@ -0,0 +1,106 @@ +namespace Terminal.Gui.TextEffects; +public class TerminalConfig +{ + public int TabWidth { get; set; } = 4; + public bool XtermColors { get; set; } = false; + public bool NoColor { get; set; } = false; + public bool WrapText { get; set; } = false; + public float FrameRate { get; set; } = 100.0f; + public int CanvasWidth { get; set; } = -1; + public int CanvasHeight { get; set; } = -1; + public string AnchorCanvas { get; set; } = "sw"; + public string AnchorText { get; set; } = "sw"; + public bool IgnoreTerminalDimensions { get; set; } = false; +} + +public class Canvas +{ + public int Top { get; private set; } + public int Right { get; private set; } + public int Bottom { get; private set; } = 1; + public int Left { get; private set; } = 1; + + public int CenterRow => (Top + Bottom) / 2; + public int CenterColumn => (Right + Left) / 2; + public Coord Center => new Coord (CenterColumn, CenterRow); + public int Width => Right - Left + 1; + public int Height => Top - Bottom + 1; + + public Canvas (int top, int right) + { + Top = top; + Right = right; + } + + public bool IsCoordInCanvas (Coord coord) + { + return coord.Column >= Left && coord.Column <= Right && + coord.Row >= Bottom && coord.Row <= Top; + } + + public Coord GetRandomCoord (bool outsideScope = false) + { + var random = new Random (); + if (outsideScope) + { + switch (random.Next (4)) + { + case 0: return new Coord (random.Next (Left, Right + 1), Top + 1); + case 1: return new Coord (random.Next (Left, Right + 1), Bottom - 1); + case 2: return new Coord (Left - 1, random.Next (Bottom, Top + 1)); + case 3: return new Coord (Right + 1, random.Next (Bottom, Top + 1)); + } + } + return new Coord ( + random.Next (Left, Right + 1), + random.Next (Bottom, Top + 1)); + } +} + +public class Terminal +{ + public TerminalConfig Config { get; } + public Canvas Canvas { get; private set; } + private Dictionary CharacterByInputCoord = new Dictionary (); + + public Terminal (string input, TerminalConfig config = null) + { + Config = config ?? new TerminalConfig (); + var dimensions = GetTerminalDimensions (); + Canvas = new Canvas (dimensions.height, dimensions.width); + ProcessInput (input); + } + + private void ProcessInput (string input) + { + // Handling input processing logic similar to Python's version + } + + public string GetPipedInput () + { + // C# way to get piped input or indicate there's none + return Console.IsInputRedirected ? Console.In.ReadToEnd () : string.Empty; + } + + public void Print (string output, bool enforceFrameRate = true) + { + if (enforceFrameRate) + EnforceFrameRate (); + + // Move cursor to top and clear the current console line + Console.SetCursorPosition (0, 0); + Console.Write (output); + Console.ResetColor (); + } + + private void EnforceFrameRate () + { + // Limit the printing speed based on the Config.FrameRate + } + + private (int width, int height) GetTerminalDimensions () + { + // Return terminal dimensions or defaults if not determinable + return (Console.WindowWidth, Console.WindowHeight); + } +} From 8fbf4d5da99a70de307ab10efe7e740b1d75ad2a Mon Sep 17 00:00:00 2001 From: tznind Date: Sat, 6 Jul 2024 02:57:15 +0100 Subject: [PATCH 03/48] Building --- Terminal.Gui/TextEffects/Animation.cs | 23 +++-- Terminal.Gui/TextEffects/BaseCharacter.cs | 4 +- Terminal.Gui/TextEffects/BaseEffect.cs | 26 +----- Terminal.Gui/TextEffects/EffectTemplate.cs | 45 ---------- Terminal.Gui/TextEffects/Graphics.cs | 97 ++++++++++------------ Terminal.Gui/TextEffects/HexTerm.cs | 94 +++++++++++++++++++++ Terminal.Gui/TextEffects/Motion.cs | 18 +++- UnitTests/TextEffects/AnimationTests.cs | 22 ++--- 8 files changed, 177 insertions(+), 152 deletions(-) create mode 100644 Terminal.Gui/TextEffects/HexTerm.cs diff --git a/Terminal.Gui/TextEffects/Animation.cs b/Terminal.Gui/TextEffects/Animation.cs index 210329ce2..a96f08a9e 100644 --- a/Terminal.Gui/TextEffects/Animation.cs +++ b/Terminal.Gui/TextEffects/Animation.cs @@ -1,4 +1,5 @@ -using static Unix.Terminal.Curses; + +using static Terminal.Gui.TextEffects.EventHandler; namespace Terminal.Gui.TextEffects; @@ -7,7 +8,6 @@ public enum SyncMetric Distance, Step } - public class CharacterVisual { public string Symbol { get; set; } @@ -21,7 +21,9 @@ public class CharacterVisual public bool Strike { get; set; } public Color Color { get; set; } public string FormattedSymbol { get; private set; } - private string _colorCode; + private string _colorCode; // Holds the ANSI color code or similar string directly + + public string ColorCode => _colorCode; public CharacterVisual (string symbol, bool bold = false, bool dim = false, bool italic = false, bool underline = false, bool blink = false, bool reverse = false, bool hidden = false, bool strike = false, Color color = null, string colorCode = null) { @@ -35,7 +37,7 @@ public class CharacterVisual Hidden = hidden; Strike = strike; Color = color; - _colorCode = colorCode; + _colorCode = colorCode; // Initialize _colorCode from the constructor argument FormattedSymbol = FormatSymbol (); } @@ -49,7 +51,7 @@ public class CharacterVisual if (Reverse) formattingString += Ansitools.ApplyReverse (); if (Hidden) formattingString += Ansitools.ApplyHidden (); if (Strike) formattingString += Ansitools.ApplyStrikethrough (); - if (_colorCode != null) formattingString += Colorterm.Fg (_colorCode); + if (_colorCode != null) formattingString += Colorterm.Fg (_colorCode); // Use the direct color code return $"{formattingString}{Symbol}{(formattingString != "" ? Ansitools.ResetAll () : "")}"; } @@ -67,6 +69,7 @@ public class CharacterVisual } } + public class Frame { public CharacterVisual CharacterVisual { get; } @@ -97,8 +100,8 @@ public class Scene public List Frames { get; } = new List (); public List PlayedFrames { get; } = new List (); public Dictionary FrameIndexMap { get; } = new Dictionary (); - public int EasingTotalSteps { get; private set; } - public int EasingCurrentStep { get; private set; } + public int EasingTotalSteps { get; set; } + public int EasingCurrentStep { get; set; } public static Dictionary XtermColorMap { get; } = new Dictionary (); public Scene (string sceneId, bool isLooping = false, SyncMetric? sync = null, EasingFunction ease = null, bool noColor = false, bool useXtermColors = false) @@ -470,12 +473,6 @@ public class Animation } } -// Dummy Enum for Event handling -public enum Event -{ - SceneComplete, - SceneActivated -} // Dummy classes for Ansitools, Colorterm, and Hexterm as placeholders public static class Ansitools diff --git a/Terminal.Gui/TextEffects/BaseCharacter.cs b/Terminal.Gui/TextEffects/BaseCharacter.cs index 329775478..886cd6b59 100644 --- a/Terminal.Gui/TextEffects/BaseCharacter.cs +++ b/Terminal.Gui/TextEffects/BaseCharacter.cs @@ -77,7 +77,9 @@ public class EventHandler Character.Motion.CurrentCoord = (Coord)target; break; case Action.Callback: - (target as Action)?.Invoke (); + + // TODO: + throw new NotImplementedException ("TODO, port (target as Action)?.Invoke ()"); break; default: throw new ArgumentOutOfRangeException (nameof (action), "Unhandled action."); diff --git a/Terminal.Gui/TextEffects/BaseEffect.cs b/Terminal.Gui/TextEffects/BaseEffect.cs index 5855f8979..09a9059ba 100644 --- a/Terminal.Gui/TextEffects/BaseEffect.cs +++ b/Terminal.Gui/TextEffects/BaseEffect.cs @@ -1,6 +1,6 @@ namespace Terminal.Gui.TextEffects; -public abstract class BaseEffectIterator : IEnumerable where T : EffectConfig +public abstract class BaseEffectIterator where T : EffectConfig, new() { protected T Config { get; set; } protected Terminal Terminal { get; set; } @@ -12,8 +12,6 @@ public abstract class BaseEffectIterator : IEnumerable where T : Effe Terminal = new Terminal (effect.InputData, effect.TerminalConfig); } - public string Frame => Terminal.GetFormattedOutputString (); - public void Update () { foreach (var character in ActiveCharacters) @@ -23,17 +21,6 @@ public abstract class BaseEffectIterator : IEnumerable where T : Effe ActiveCharacters.RemoveAll (character => !character.IsActive); } - public IEnumerator GetEnumerator () - { - return this; - } - - IEnumerator IEnumerable.GetEnumerator () - { - return GetEnumerator (); - } - - public abstract string Next (); } public abstract class BaseEffect where T : EffectConfig, new() @@ -49,14 +36,7 @@ public abstract class BaseEffect where T : EffectConfig, new() TerminalConfig = new TerminalConfig (); } - public abstract Type IteratorClass { get; } - - public IEnumerator GetEnumerator () - { - var iterator = (BaseEffectIterator)Activator.CreateInstance (IteratorClass, this); - return iterator; - } - + /* public IDisposable TerminalOutput (string endSymbol = "\n") { var terminal = new Terminal (InputData, TerminalConfig); @@ -69,6 +49,6 @@ public abstract class BaseEffect where T : EffectConfig, new() { terminal.RestoreCursor (endSymbol); } - } + }*/ } diff --git a/Terminal.Gui/TextEffects/EffectTemplate.cs b/Terminal.Gui/TextEffects/EffectTemplate.cs index c16dbccb9..4b0fddcb7 100644 --- a/Terminal.Gui/TextEffects/EffectTemplate.cs +++ b/Terminal.Gui/TextEffects/EffectTemplate.cs @@ -11,48 +11,3 @@ public class EffectConfig public float MovementSpeed { get; set; } public EasingFunction Easing { get; set; } } - -public class NamedEffectIterator : BaseEffectIterator -{ - public NamedEffectIterator (NamedEffect effect) : base (effect) - { - Build (); - } - - private void Build () - { - var finalGradient = new Gradient (Config.FinalGradientStops, Config.FinalGradientSteps); - foreach (var character in Terminal.GetCharacters ()) - { - CharacterFinalColorMap [character] = finalGradient.GetColorAtFraction ( - character.InputCoord.Row / (float)Terminal.Canvas.Top - ); - } - } - - public override string Next () - { - if (PendingChars.Any () || ActiveCharacters.Any ()) - { - Update (); - return Frame; - } - else - { - throw new InvalidOperationException ("No more elements in effect iterator."); - } - } - - private List PendingChars = new List (); - private Dictionary CharacterFinalColorMap = new Dictionary (); -} - -public class NamedEffect : BaseEffect -{ - public NamedEffect (string inputData) : base (inputData) - { - } - - protected override Type ConfigCls => typeof (EffectConfig); - protected override Type IteratorCls => typeof (NamedEffectIterator); -} diff --git a/Terminal.Gui/TextEffects/Graphics.cs b/Terminal.Gui/TextEffects/Graphics.cs index e03063a72..73cd02263 100644 --- a/Terminal.Gui/TextEffects/Graphics.cs +++ b/Terminal.Gui/TextEffects/Graphics.cs @@ -5,69 +5,64 @@ using System.Linq; public class Color { - public string RgbColor { get; } - public int? XtermColor { get; } + public string RgbColor { get; private set; } + public int? XtermColor { get; private set; } public Color (string rgbColor) { - if (!IsValidHexColor (rgbColor)) + if (!ColorUtils.IsValidHexColor (rgbColor)) throw new ArgumentException ("Invalid RGB hex color format."); - RgbColor = rgbColor.StartsWith ("#") ? rgbColor.Substring (1) : rgbColor; - XtermColor = null; // Convert RGB to XTerm-256 here if necessary + RgbColor = rgbColor.StartsWith ("#") ? rgbColor.Substring (1).ToUpper () : rgbColor.ToUpper (); + XtermColor = ColorUtils.HexToXterm (RgbColor); // Convert RGB to XTerm-256 } public Color (int xtermColor) { + if (!ColorUtils.IsValidXtermColor (xtermColor)) + throw new ArgumentException ("Invalid XTerm-256 color code."); + XtermColor = xtermColor; - RgbColor = ConvertXtermToHex (xtermColor); // Implement conversion logic + RgbColor = ColorUtils.XtermToHex (xtermColor); // Perform the actual conversion + } + public int R => Convert.ToInt32 (RgbColor.Substring (0, 2), 16); + public int G => Convert.ToInt32 (RgbColor.Substring (2, 2), 16); + public int B => Convert.ToInt32 (RgbColor.Substring (4, 2), 16); + + public (int R, int G, int B) GetRgbInts () + { + return ( + Convert.ToInt32 (RgbColor.Substring (0, 2), 16), + Convert.ToInt32 (RgbColor.Substring (2, 2), 16), + Convert.ToInt32 (RgbColor.Substring (4, 2), 16) + ); } - private bool IsValidHexColor (string color) - { - if (color.StartsWith ("#")) - { - color = color.Substring (1); - } - return color.Length == 6 && int.TryParse (color, System.Globalization.NumberStyles.HexNumber, null, out _); - } + public override string ToString () => $"#{RgbColor}"; - private string ConvertXtermToHex (int xtermColor) + public static Color FromRgb (int r, int g, int b) { - // Dummy conversion for the sake of example - return "000000"; // Actual conversion logic needed - } + // Validate the RGB values to ensure they are within the 0-255 range + if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) + throw new ArgumentOutOfRangeException ("RGB values must be between 0 and 255."); - public (int, int, int) GetRgbInts () - { - int r = Convert.ToInt32 (RgbColor.Substring (0, 2), 16); - int g = Convert.ToInt32 (RgbColor.Substring (2, 2), 16); - int b = Convert.ToInt32 (RgbColor.Substring (4, 2), 16); - return (r, g, b); - } + // Convert RGB values to a hexadecimal string + string rgbColor = $"#{r:X2}{g:X2}{b:X2}"; - public override string ToString () - { - return $"#{RgbColor.ToUpper ()}"; - } - - public override bool Equals (object obj) - { - return obj is Color other && RgbColor == other.RgbColor; - } - - public override int GetHashCode () - { - return HashCode.Combine (RgbColor); + // Create and return a new Color instance using the hexadecimal string + return new Color (rgbColor); } } + + public class Gradient { public List Spectrum { get; private set; } + // Constructor now accepts IEnumerable for steps. public Gradient (IEnumerable stops, IEnumerable steps, bool loop = false) { - if (stops == null || stops.Count () < 2) + if (stops == null || !stops.Any () || stops.Count () < 2) throw new ArgumentException ("At least two color stops are required to create a gradient."); if (steps == null || !steps.Any ()) throw new ArgumentException ("Steps are required to define the transitions between colors."); @@ -83,7 +78,8 @@ public class Gradient for (int i = 0; i < stops.Count - 1; i++) { - gradient.AddRange (InterpolateColors (stops [i], stops [i + 1], i < steps.Count ? steps [i] : steps.Last ())); + int currentSteps = i < steps.Count ? steps [i] : steps.Last (); + gradient.AddRange (InterpolateColors (stops [i], stops [i + 1], currentSteps)); } return gradient; @@ -93,16 +89,16 @@ public class Gradient { for (int step = 0; step <= steps; step++) { - int r = Interpolate (start.GetRgbInts ().Item1, end.GetRgbInts ().Item1, steps, step); - int g = Interpolate (start.GetRgbInts ().Item2, end.GetRgbInts ().Item2, steps, step); - int b = Interpolate (start.GetRgbInts ().Item3, end.GetRgbInts ().Item3, steps, step); - yield return new Color ($"#{r:X2}{g:X2}{b:X2}"); + int r = Interpolate (start.R, end.R, steps, step); + int g = Interpolate (start.G, end.G, steps, step); + int b = Interpolate (start.B, end.B, steps, step); + yield return Color.FromRgb (r, g, b); } } private int Interpolate (int start, int end, int steps, int currentStep) { - return start + (end - start) * currentStep / steps; + return start + (int)((end - start) * (double)currentStep / steps); } public Color GetColorAtFraction (double fraction) @@ -112,15 +108,6 @@ public class Gradient int index = (int)(fraction * (Spectrum.Count - 1)); return Spectrum [index]; } - - public IEnumerable GetRange (int startIndex, int count) - { - return Spectrum.Skip (startIndex).Take (count); - } - - public override string ToString () - { - return $"Gradient with {Spectrum.Count} colors."; - } } + diff --git a/Terminal.Gui/TextEffects/HexTerm.cs b/Terminal.Gui/TextEffects/HexTerm.cs new file mode 100644 index 000000000..b3b96c4d3 --- /dev/null +++ b/Terminal.Gui/TextEffects/HexTerm.cs @@ -0,0 +1,94 @@ +using System.Text.RegularExpressions; + +namespace Terminal.Gui.TextEffects; +using System; +using System.Collections.Generic; +using System.Linq; + +public static class ColorUtils +{ + private static readonly Dictionary xtermToHexMap = new Dictionary + { + {0, "#000000"}, {1, "#800000"}, {2, "#008000"}, {3, "#808000"}, {4, "#000080"}, {5, "#800080"}, {6, "#008080"}, {7, "#c0c0c0"}, + {8, "#808080"}, {9, "#ff0000"}, {10, "#00ff00"}, {11, "#ffff00"}, {12, "#0000ff"}, {13, "#ff00ff"}, {14, "#00ffff"}, {15, "#ffffff"}, + {16, "#000000"}, {17, "#00005f"}, {18, "#000087"}, {19, "#0000af"}, {20, "#0000d7"}, {21, "#0000ff"}, {22, "#005f00"}, {23, "#005f5f"}, + {24, "#005f87"}, {25, "#005faf"}, {26, "#005fd7"}, {27, "#005fff"}, {28, "#008700"}, {29, "#00875f"}, {30, "#008787"}, {31, "#0087af"}, + {32, "#0087d7"}, {33, "#0087ff"}, {34, "#00af00"}, {35, "#00af5f"}, {36, "#00af87"}, {37, "#00afaf"}, {38, "#00afd7"}, {39, "#00afff"}, + {40, "#00d700"}, {41, "#00d75f"}, {42, "#00d787"}, {43, "#00d7af"}, {44, "#00d7d7"}, {45, "#00d7ff"}, {46, "#00ff00"}, {47, "#00ff5f"}, + {48, "#00ff87"}, {49, "#00ffaf"}, {50, "#00ffd7"}, {51, "#00ffff"}, {52, "#5f0000"}, {53, "#5f005f"}, {54, "#5f0087"}, {55, "#5f00af"}, + {56, "#5f00d7"}, {57, "#5f00ff"}, {58, "#5f5f00"}, {59, "#5f5f5f"}, {60, "#5f5f87"}, {61, "#5f5faf"}, {62, "#5f5fd7"}, {63, "#5f5fff"}, + {64, "#5f8700"}, {65, "#5f875f"}, {66, "#5f8787"}, {67, "#5f87af"}, {68, "#5f87d7"}, {69, "#5f87ff"}, {70, "#5faf00"}, {71, "#5faf5f"}, + {72, "#5faf87"}, {73, "#5fafaf"}, {74, "#5fafd7"}, {75, "#5fafff"}, {76, "#5fd700"}, {77, "#5fd75f"}, {78, "#5fd787"}, {79, "#5fd7af"}, + {80, "#5fd7d7"}, {81, "#5fd7ff"}, {82, "#5fff00"}, {83, "#5fff5f"}, {84, "#5fff87"}, {85, "#5fffaf"}, {86, "#5fffd7"}, {87, "#5fffff"}, + {88, "#870000"}, {89, "#87005f"}, {90, "#870087"}, {91, "#8700af"}, {92, "#8700d7"}, {93, "#8700ff"}, {94, "#875f00"}, {95, "#875f5f"}, + {96, "#875f87"}, {97, "#875faf"}, {98, "#875fd7"}, {99, "#875fff"}, {100, "#878700"}, {101, "#87875f"}, {102, "#878787"}, {103, "#8787af"}, + {104, "#8787d7"}, {105, "#8787ff"}, {106, "#87af00"}, {107, "#87af5f"}, {108, "#87af87"}, {109, "#87afaf"}, {110, "#87afd7"}, {111, "#87afff"}, + {112, "#87d700"}, {113, "#87d75f"}, {114, "#87d787"}, {115, "#87d7af"}, {116, "#87d7d7"}, {117, "#87d7ff"}, {118, "#87ff00"}, {119, "#87ff5f"}, + {120, "#87ff87"}, {121, "#87ffaf"}, {122, "#87ffd7"}, {123, "#87ffff"}, {124, "#af0000"}, {125, "#af005f"}, {126, "#af0087"}, {127, "#af00af"}, + {128, "#af00d7"}, {129, "#af00ff"}, {130, "#af5f00"}, {131, "#af5f5f"}, {132, "#af5f87"}, {133, "#af5faf"}, {134, "#af5fd7"}, {135, "#af5fff"}, + {136, "#af8700"}, {137, "#af875f"}, {138, "#af8787"}, {139, "#af87af"}, {140, "#af87d7"}, {141, "#af87ff"}, {142, "#afaf00"}, {143, "#afaf5f"}, + {144, "#afaf87"}, {145, "#afafaf"}, {146, "#afafd7"}, {147, "#afafff"}, {148, "#afd700"}, {149, "#afd75f"}, {150, "#afd787"}, {151, "#afd7af"}, + {152, "#afd7d7"}, {153, "#afd7ff"}, {154, "#afff00"}, {155, "#afff5f"}, {156, "#afff87"}, {157, "#afffaf"}, {158, "#afffd7"}, {159, "#afffff"}, + {160, "#d70000"}, {161, "#d7005f"}, {162, "#d70087"}, {163, "#d700af"}, {164, "#d700d7"}, {165, "#d700ff"}, {166, "#d75f00"}, {167, "#d75f5f"}, + {168, "#d75f87"}, {169, "#d75faf"}, {170, "#d75fd7"}, {171, "#d75fff"}, {172, "#d78700"}, {173, "#d7875f"}, {174, "#d78787"}, {175, "#d787af"}, + {176, "#d787d7"}, {177, "#d787ff"}, {178, "#d7af00"}, {179, "#d7af5f"}, {180, "#d7af87"}, {181, "#d7afaf"}, {182, "#d7afd7"}, {183, "#d7afff"}, + {184, "#d7d700"}, {185, "#d7d75f"}, {186, "#d7d787"}, {187, "#d7d7af"}, {188, "#d7d7d7"}, {189, "#d7d7ff"}, {190, "#d7ff00"}, {191, "#d7ff5f"}, + {192, "#d7ff87"}, {193, "#d7ffaf"}, {194, "#d7ffd7"}, {195, "#d7ffff"}, {196, "#ff0000"}, {197, "#ff005f"}, {198, "#ff0087"}, {199, "#ff00af"}, + {200, "#ff00d7"}, {201, "#ff00ff"}, {202, "#ff5f00"}, {203, "#ff5f5f"}, {204, "#ff5f87"}, {205, "#ff5faf"}, {206, "#ff5fd7"}, {207, "#ff5fff"}, + {208, "#ff8700"}, {209, "#ff875f"}, {210, "#ff8787"}, {211, "#ff87af"}, {212, "#ff87d7"}, {213, "#ff87ff"}, {214, "#ffaf00"}, {215, "#ffaf5f"}, + {216, "#ffaf87"}, {217, "#ffafaf"}, {218, "#ffafd7"}, {219, "#ffafff"}, {220, "#ffd700"}, {221, "#ffd75f"}, {222, "#ffd787"}, {223, "#ffd7af"}, + {224, "#ffd7d7"}, {225, "#ffd7ff"}, {226, "#ffff00"}, {227, "#ffff5f"}, {228, "#ffff87"}, {229, "#ffffaf"}, {230, "#ffffd7"}, {231, "#ffffff"}, + {232, "#080808"}, {233, "#121212"}, {234, "#1c1c1c"}, {235, "#262626"}, {236, "#303030"}, {237, "#3a3a3a"}, {238, "#444444"}, {239, "#4e4e4e"}, + {240, "#585858"}, {241, "#626262"}, {242, "#6c6c6c"}, {243, "#767676"}, {244, "#808080"}, {245, "#8a8a8a"}, {246, "#949494"}, {247, "#9e9e9e"}, + {248, "#a8a8a8"}, {249, "#b2b2b2"}, {250, "#bcbcbc"}, {251, "#c6c6c6"}, {252, "#d0d0d0"}, {253, "#dadada"}, {254, "#e4e4e4"}, {255, "#eeeeee"} + }; + + private static readonly Dictionary xtermToRgbMap = xtermToHexMap.ToDictionary ( + item => item.Key, + item => ( + R: Convert.ToInt32 (item.Value.Substring (1, 2), 16), + G: Convert.ToInt32 (item.Value.Substring (3, 2), 16), + B: Convert.ToInt32 (item.Value.Substring (5, 2), 16) + )); + private static readonly Regex hexColorRegex = new Regex ("^#?[0-9A-Fa-f]{6}$"); + + public static bool IsValidHexColor (string hexColor) + { + return hexColorRegex.IsMatch (hexColor); + } + + public static bool IsValidXtermColor (int xtermColor) + { + return xtermColor >= 0 && xtermColor <= 255; + } + + public static string XtermToHex (int xtermColor) + { + if (xtermToHexMap.TryGetValue (xtermColor, out string hex)) + { + return hex; + } + throw new ArgumentException ($"Invalid XTerm-256 color code: {xtermColor}"); + } + + public static int HexToXterm (string hexColor) + { + if (!IsValidHexColor (hexColor)) + throw new ArgumentException ("Invalid RGB hex color format."); + + hexColor = hexColor.StartsWith ("#") ? hexColor.Substring (1) : hexColor; + var rgb = ( + R: Convert.ToInt32 (hexColor.Substring (0, 2), 16), + G: Convert.ToInt32 (hexColor.Substring (2, 2), 16), + B: Convert.ToInt32 (hexColor.Substring (4, 2), 16) + ); + + return xtermToRgbMap.Aggregate ((current, next) => + ColorDifference (current.Value, rgb) < ColorDifference (next.Value, rgb) ? current : next).Key; + } + + private static double ColorDifference ((int R, int G, int B) c1, (int R, int G, int B) c2) + { + return Math.Sqrt (Math.Pow (c1.R - c2.R, 2) + Math.Pow (c1.G - c2.G, 2) + Math.Pow (c1.B - c2.B, 2)); + } +} diff --git a/Terminal.Gui/TextEffects/Motion.cs b/Terminal.Gui/TextEffects/Motion.cs index ae8c06188..e2431cd49 100644 --- a/Terminal.Gui/TextEffects/Motion.cs +++ b/Terminal.Gui/TextEffects/Motion.cs @@ -56,7 +56,6 @@ public class Segment return new Coord (column, row); } } - public class Path { public string PathId { get; private set; } @@ -69,6 +68,7 @@ public class Path public int CurrentStep { get; set; } public double TotalDistance { get; set; } public double LastDistanceReached { get; set; } + public int MaxSteps => (int)Math.Ceiling (TotalDistance / Speed); // Calculates max steps based on total distance and speed public Path (string pathId, double speed, Func easeFunction = null, int layer = 0, int holdTime = 0, bool loop = false) { @@ -100,9 +100,9 @@ public class Path public Coord Step () { - if (EaseFunction != null && CurrentStep <= TotalDistance) + if (CurrentStep <= MaxSteps) { - double progress = EaseFunction ((double)CurrentStep / TotalDistance); + double progress = EaseFunction?.Invoke ((double)CurrentStep / TotalDistance) ?? (double)CurrentStep / TotalDistance; double distanceTravelled = TotalDistance * progress; LastDistanceReached = distanceTravelled; @@ -174,9 +174,19 @@ public class Motion ActivePath.CurrentStep = 0; // Reset the path's progress } + /// + /// Set the active path to None if the active path is the given path. + /// + public void DeactivatePath (Path p) + { + if (p == ActivePath) + { + ActivePath = null; + } + } public void DeactivatePath () { - ActivePath = null; + ActivePath = null; } public void Move () diff --git a/UnitTests/TextEffects/AnimationTests.cs b/UnitTests/TextEffects/AnimationTests.cs index 0a00b2e7c..84de80f70 100644 --- a/UnitTests/TextEffects/AnimationTests.cs +++ b/UnitTests/TextEffects/AnimationTests.cs @@ -1,10 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using Terminal.Gui.TextEffects; -using Xunit; +using Terminal.Gui.TextEffects; namespace Terminal.Gui.TextEffectsTests; +using Color = Terminal.Gui.TextEffects.Color; public class AnimationTests { @@ -100,7 +97,8 @@ public class AnimationTests public void TestSceneApplyGradientToSymbolsEqualColorsAndSymbols () { var scene = new Scene (sceneId: "test_scene"); - var gradient = new Gradient (new Color ("000000"), new Color ("ffffff"), steps: 2); + var gradient = new Gradient (new [] { new Color ("000000"), new Color ("ffffff") }, + steps: new [] { 2 }); var symbols = new List { "a", "b", "c" }; scene.ApplyGradientToSymbols (gradient, symbols, duration: 1); Assert.Equal (3, scene.Frames.Count); @@ -115,7 +113,9 @@ public class AnimationTests public void TestSceneApplyGradientToSymbolsUnequalColorsAndSymbols () { var scene = new Scene (sceneId: "test_scene"); - var gradient = new Gradient (new Color ("000000"), new Color ("ffffff"), steps: 4); + var gradient = new Gradient ( + new [] { new Color ("000000"), new Color ("ffffff") }, + steps: new [] { 4 }); var symbols = new List { "q", "z" }; scene.ApplyGradientToSymbols (gradient, symbols, duration: 1); Assert.Equal (5, scene.Frames.Count); @@ -142,7 +142,7 @@ public class AnimationTests public void TestAnimationNewScene () { var animation = character.Animation; - var scene = animation.NewScene ("test_scene", isLooping: true); + var scene = animation.NewScene (id:"test_scene", isLooping: true); Assert.IsType (scene); Assert.Equal ("test_scene", scene.SceneId); Assert.True (scene.IsLooping); @@ -163,7 +163,7 @@ public class AnimationTests public void TestAnimationQueryScene () { var animation = character.Animation; - var scene = animation.NewScene ("test_scene", isLooping: true); + var scene = animation.NewScene (id:"test_scene", isLooping: true); Assert.Equal (scene, animation.QueryScene ("test_scene")); } @@ -171,7 +171,7 @@ public class AnimationTests public void TestAnimationLoopingActiveSceneIsComplete () { var animation = character.Animation; - var scene = animation.NewScene ("test_scene", isLooping: true); + var scene = animation.NewScene (id: "test_scene", isLooping: true); scene.AddFrame (symbol: "a", duration: 2); animation.ActivateScene (scene); Assert.True (animation.ActiveSceneIsComplete ()); @@ -181,7 +181,7 @@ public class AnimationTests public void TestAnimationNonLoopingActiveSceneIsComplete () { var animation = character.Animation; - var scene = animation.NewScene ("test_scene"); + var scene = animation.NewScene (id: "test_scene"); scene.AddFrame (symbol: "a", duration: 1); animation.ActivateScene (scene); Assert.False (animation.ActiveSceneIsComplete ()); From b82eda4ae5436e8b7bd6d2f2be7f1ecbc932e282 Mon Sep 17 00:00:00 2001 From: tznind Date: Sat, 6 Jul 2024 03:24:27 +0100 Subject: [PATCH 04/48] Rainbow gradient! --- UICatalog/Scenarios/TextEffectsScenario.cs | 113 +++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 UICatalog/Scenarios/TextEffectsScenario.cs diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs new file mode 100644 index 000000000..00cb4cb5f --- /dev/null +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Terminal.Gui; +using Terminal.Gui.TextEffects; +using static UICatalog.Scenario; + + +using Color = Terminal.Gui.TextEffects.Color; + +namespace UICatalog.Scenarios; + + + +[ScenarioMetadata ("Text Effects", "Text Effects.")] +[ScenarioCategory ("Colors")] +public class TextEffectsScenario : Scenario +{ + public override void Main () + { + Application.Init (); + var top = Application.Top; + + // Creates a window that occupies the entire terminal with a title. + var window = new Window () + { + X = 0, + Y = 1, // Leaves one row for the toplevel menu + + // By using Dim.Fill(), it will automatically resize without manual intervention + Width = Dim.Fill (), + Height = Dim.Fill (), + Title = "Text Effects Scenario" + }; + + // Create a large empty view. + var emptyView = new TextEffectsExampleView () + { + X = 0, + Y = 0, + Width = Dim.Fill (), + Height = Dim.Fill () , + }; + + window.Add (emptyView); + + // Create a label in the center of the window. + var label = new Label () + { + X = Pos.Center (), + Y = Pos.Center (), + Width = 10, + Height = 1, + Title = "Hello" + }; + window.Add (label); + + Application.Run (window); + Application.Shutdown (); + } +} + +internal class TextEffectsExampleView : View +{ + public override void OnDrawContent (Rectangle viewport) + { + base.OnDrawContent (viewport); + + // Define the colors of the rainbow + var stops = new List + { + Color.FromRgb(255, 0, 0), // Red + Color.FromRgb(255, 165, 0), // Orange + Color.FromRgb(255, 255, 0), // Yellow + Color.FromRgb(0, 128, 0), // Green + Color.FromRgb(0, 0, 255), // Blue + Color.FromRgb(75, 0, 130), // Indigo + Color.FromRgb(238, 130, 238) // Violet + }; + + // Define the number of steps between each color + var steps = new List + { + 20, // between Red and Orange + 20, // between Orange and Yellow + 20, // between Yellow and Green + 20, // between Green and Blue + 20, // between Blue and Indigo + 20 // between Indigo and Violet + }; + + // Create the gradient + var rainbowGradient = new Gradient (stops, steps, loop: true); + + + for (int x = 0 ; x < viewport.Width; x++) + { + double fraction = (double)x / (viewport.Width - 1); + Color color = rainbowGradient.GetColorAtFraction (fraction); + + // Assuming AddRune is a method you have for drawing at specific positions + Application.Driver.SetAttribute ( + + new Attribute ( + new Terminal.Gui.Color(color.R, color.G, color.B), + new Terminal.Gui.Color (color.R, color.G, color.B) + )); // Setting color based on RGB + + + AddRune (x, 0, new Rune ('█')); + } + } +} \ No newline at end of file From 5cac6597dd878617a3c485c95093cf5c96aadcdc Mon Sep 17 00:00:00 2001 From: tznind Date: Sat, 6 Jul 2024 09:23:06 +0100 Subject: [PATCH 05/48] Investigate adding a bouncing ball animation --- UICatalog/Scenarios/TextEffectsScenario.cs | 68 +++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index 00cb4cb5f..8a28901b0 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; using System.Text; +using System.Threading; using Terminal.Gui; using Terminal.Gui.TextEffects; using static UICatalog.Scenario; using Color = Terminal.Gui.TextEffects.Color; +using Animation = Terminal.Gui.TextEffects.Animation; namespace UICatalog.Scenarios; @@ -110,4 +112,68 @@ internal class TextEffectsExampleView : View AddRune (x, 0, new Rune ('█')); } } -} \ No newline at end of file + public class Ball + { + public Animation Animation { get; private set; } + public Scene BouncingScene { get; private set; } + public View Viewport { get; private set; } + public EffectCharacter Character { get; private set; } + + public Ball (View viewport) + { + Viewport = viewport; + Character = new EffectCharacter (1, "O", 0, 0); + Animation = Character.Animation; + CreateBouncingScene (); + } + + private void CreateBouncingScene () + { + BouncingScene = Animation.NewScene (isLooping: true); + int width = Viewport.Frame.Width; + int height = Viewport.Frame.Height; + double frequency = 2 * Math.PI / width; + + for (int x = 0; x < width; x++) + { + int y = (int)((height - 1) / 2 * (1 + Math.Sin (frequency * x))); + BouncingScene.AddFrame ("O", 1); + BouncingScene.Frames [BouncingScene.Frames.Count - 1].CharacterVisual.Position = new Coord (x, y); + } + + for (int x = width - 1; x >= 0; x--) + { + int y = (int)((height - 1) / 2 * (1 + Math.Sin (frequency * x))); + BouncingScene.AddFrame ("O", 1); + BouncingScene.Frames [BouncingScene.Frames.Count - 1].CharacterVisual.Position = new Coord (x, y); + } + } + + public void Start () + { + Animation.ActivateScene (BouncingScene); + new Thread (() => + { + while (true) + { + Draw (); + Thread.Sleep (100); // Adjust the speed of animation + Animation.StepAnimation (); + } + }) + { IsBackground = true }.Start (); + } + + private void Draw () + { + var characterVisual = Animation.CurrentCharacterVisual; + var coord = characterVisual.Position; + Application.MainLoop.Invoke (() => + { + Viewport.Clear (); + Viewport.AddRune (coord.X, coord.Y, new Rune ('O')); + Application.Refresh (); + }); + } + } +} From d7a4e0e7c17b2d5eeb99d60d3a699a5ff8d64fb8 Mon Sep 17 00:00:00 2001 From: tznind Date: Sat, 6 Jul 2024 20:01:49 +0100 Subject: [PATCH 06/48] Ball bouncing properly --- .../{Animation.cs => AnimationScenario.cs} | 2 +- UICatalog/Scenarios/TextEffectsScenario.cs | 89 +++++++++++++------ 2 files changed, 62 insertions(+), 29 deletions(-) rename UICatalog/Scenarios/{Animation.cs => AnimationScenario.cs} (99%) diff --git a/UICatalog/Scenarios/Animation.cs b/UICatalog/Scenarios/AnimationScenario.cs similarity index 99% rename from UICatalog/Scenarios/Animation.cs rename to UICatalog/Scenarios/AnimationScenario.cs index 42ad540c1..f924f8f08 100644 --- a/UICatalog/Scenarios/Animation.cs +++ b/UICatalog/Scenarios/AnimationScenario.cs @@ -13,7 +13,7 @@ namespace UICatalog.Scenarios; [ScenarioMetadata ("Animation", "Demonstration of how to render animated images with threading.")] [ScenarioCategory ("Threading")] [ScenarioCategory ("Drawing")] -public class Animation : Scenario +public class AnimationScenario : Scenario { private bool _isDisposed; diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index 8a28901b0..2652c5249 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -6,14 +6,11 @@ using Terminal.Gui; using Terminal.Gui.TextEffects; using static UICatalog.Scenario; - using Color = Terminal.Gui.TextEffects.Color; using Animation = Terminal.Gui.TextEffects.Animation; namespace UICatalog.Scenarios; - - [ScenarioMetadata ("Text Effects", "Text Effects.")] [ScenarioCategory ("Colors")] public class TextEffectsScenario : Scenario @@ -41,7 +38,7 @@ public class TextEffectsScenario : Scenario X = 0, Y = 0, Width = Dim.Fill (), - Height = Dim.Fill () , + Height = Dim.Fill (), }; window.Add (emptyView); @@ -53,7 +50,7 @@ public class TextEffectsScenario : Scenario Y = Pos.Center (), Width = 10, Height = 1, - Title = "Hello" + Text = "Hello" }; window.Add (label); @@ -64,10 +61,29 @@ public class TextEffectsScenario : Scenario internal class TextEffectsExampleView : View { + Ball? _ball; + private bool resized; + + protected override void OnViewportChanged (DrawEventArgs e) + { + base.OnViewportChanged (e); + resized = true; + } + public override void OnDrawContent (Rectangle viewport) { base.OnDrawContent (viewport); + if ( + // First time + (_ball == null && viewport.Width > 0 && viewport.Height > 0) + || resized) + { + _ball = new Ball (this); + _ball.Start (); + resized = false; + } + // Define the colors of the rainbow var stops = new List { @@ -94,24 +110,24 @@ internal class TextEffectsExampleView : View // Create the gradient var rainbowGradient = new Gradient (stops, steps, loop: true); - - for (int x = 0 ; x < viewport.Width; x++) + for (int x = 0; x < viewport.Width; x++) { double fraction = (double)x / (viewport.Width - 1); Color color = rainbowGradient.GetColorAtFraction (fraction); // Assuming AddRune is a method you have for drawing at specific positions Application.Driver.SetAttribute ( - new Attribute ( - new Terminal.Gui.Color(color.R, color.G, color.B), + new Terminal.Gui.Color (color.R, color.G, color.B), new Terminal.Gui.Color (color.R, color.G, color.B) - )); // Setting color based on RGB - + )); // Setting color based on RGB AddRune (x, 0, new Rune ('█')); } + + _ball?.Draw (); } + public class Ball { public Animation Animation { get; private set; } @@ -125,6 +141,7 @@ internal class TextEffectsExampleView : View Character = new EffectCharacter (1, "O", 0, 0); Animation = Character.Animation; CreateBouncingScene (); + CreateMotionPath (); } private void CreateBouncingScene () @@ -132,23 +149,44 @@ internal class TextEffectsExampleView : View BouncingScene = Animation.NewScene (isLooping: true); int width = Viewport.Frame.Width; int height = Viewport.Frame.Height; - double frequency = 2 * Math.PI / width; + double frequency = 4 * Math.PI / width; // Double the frequency for (int x = 0; x < width; x++) { - int y = (int)((height - 1) / 2 * (1 + Math.Sin (frequency * x))); + int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude BouncingScene.AddFrame ("O", 1); - BouncingScene.Frames [BouncingScene.Frames.Count - 1].CharacterVisual.Position = new Coord (x, y); } for (int x = width - 1; x >= 0; x--) { - int y = (int)((height - 1) / 2 * (1 + Math.Sin (frequency * x))); + int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude BouncingScene.AddFrame ("O", 1); - BouncingScene.Frames [BouncingScene.Frames.Count - 1].CharacterVisual.Position = new Coord (x, y); } } + private void CreateMotionPath () + { + int width = Viewport.Frame.Width; + int height = Viewport.Frame.Height; + double frequency = 4 * Math.PI / width; // Double the frequency + + var path = Character.Motion.CreatePath ("sineWavePath", speed: 1, loop: true); + + for (int x = 0; x < width; x++) + { + int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude + path.AddWaypoint (new Waypoint ($"waypoint_{x}", new Coord (x, y))); + } + + for (int x = width - 1; x >= 0; x--) + { + int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude + path.AddWaypoint (new Waypoint ($"waypoint_{x}", new Coord (x, y))); + } + + Character.Motion.ActivatePath (path); + } + public void Start () { Animation.ActivateScene (BouncingScene); @@ -156,24 +194,19 @@ internal class TextEffectsExampleView : View { while (true) { - Draw (); - Thread.Sleep (100); // Adjust the speed of animation - Animation.StepAnimation (); + Thread.Sleep (10); // Adjust the speed of animation + Character.Tick (); + + Application.Invoke (() => Viewport.SetNeedsDisplay ()); } }) { IsBackground = true }.Start (); } - private void Draw () + public void Draw () { - var characterVisual = Animation.CurrentCharacterVisual; - var coord = characterVisual.Position; - Application.MainLoop.Invoke (() => - { - Viewport.Clear (); - Viewport.AddRune (coord.X, coord.Y, new Rune ('O')); - Application.Refresh (); - }); + Driver.SetAttribute (Viewport.ColorScheme.Normal); + Viewport.AddRune (Character.Motion.CurrentCoord.Column, Character.Motion.CurrentCoord.Row, new Rune ('O')); } } } From ab07f53bd29dbfc21612b720748c7c86eceddf4a Mon Sep 17 00:00:00 2001 From: tznind Date: Sat, 6 Jul 2024 20:39:01 +0100 Subject: [PATCH 07/48] Radial gradient --- Terminal.Gui/TextEffects/ArgValidators.cs | 273 +++++++++++++++++++++ Terminal.Gui/TextEffects/BaseEffect.cs | 6 + Terminal.Gui/TextEffects/Effects/Beams.cs | 241 ++++++++++++++++++ Terminal.Gui/TextEffects/Graphics.cs | 165 ++++++++++--- Terminal.Gui/TextEffects/Motion.cs | 29 ++- UICatalog/Scenarios/TextEffectsScenario.cs | 65 ++++- 6 files changed, 733 insertions(+), 46 deletions(-) create mode 100644 Terminal.Gui/TextEffects/ArgValidators.cs create mode 100644 Terminal.Gui/TextEffects/Effects/Beams.cs diff --git a/Terminal.Gui/TextEffects/ArgValidators.cs b/Terminal.Gui/TextEffects/ArgValidators.cs new file mode 100644 index 000000000..4e31e7a51 --- /dev/null +++ b/Terminal.Gui/TextEffects/ArgValidators.cs @@ -0,0 +1,273 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using Terminal.Gui.TextEffects; + +using Color = Terminal.Gui.TextEffects.Color; + +public static class PositiveInt +{ + public static int Parse (string arg) + { + if (int.TryParse (arg, out int value) && value > 0) + { + return value; + } + else + { + throw new ArgumentException ($"invalid value: '{arg}' is not > 0."); + } + } +} + +public static class NonNegativeInt +{ + public static int Parse (string arg) + { + if (int.TryParse (arg, out int value) && value >= 0) + { + return value; + } + else + { + throw new ArgumentException ($"invalid value: '{arg}' Argument must be int >= 0."); + } + } +} + +public static class IntRange +{ + public static (int, int) Parse (string arg) + { + var parts = arg.Split ('-'); + if (parts.Length == 2 && int.TryParse (parts [0], out int start) && int.TryParse (parts [1], out int end) && start > 0 && start <= end) + { + return (start, end); + } + else + { + throw new ArgumentException ($"invalid range: '{arg}' is not a valid range. Must be start-end. Ex: 1-10"); + } + } +} + +public static class PositiveFloat +{ + public static float Parse (string arg) + { + if (float.TryParse (arg, out float value) && value > 0) + { + return value; + } + else + { + throw new ArgumentException ($"invalid value: '{arg}' is not a valid value. Argument must be a float > 0."); + } + } +} + +public static class NonNegativeFloat +{ + public static float Parse (string arg) + { + if (float.TryParse (arg, out float value) && value >= 0) + { + return value; + } + else + { + throw new ArgumentException ($"invalid argument value: '{arg}' is out of range. Must be float >= 0."); + } + } +} + +public static class PositiveFloatRange +{ + public static (float, float) Parse (string arg) + { + var parts = arg.Split ('-'); + if (parts.Length == 2 && float.TryParse (parts [0], out float start) && float.TryParse (parts [1], out float end) && start > 0 && start <= end) + { + return (start, end); + } + else + { + throw new ArgumentException ($"invalid range: '{arg}' is not a valid range. Must be start-end. Ex: 0.1-1.0"); + } + } +} + +public static class Ratio +{ + public static float Parse (string arg) + { + if (float.TryParse (arg, out float value) && value >= 0 && value <= 1) + { + return value; + } + else + { + throw new ArgumentException ($"invalid value: '{arg}' is not a float >= 0 and <= 1. Example: 0.5"); + } + } +} + +public enum GradientDirection +{ + Horizontal, + Vertical, + Diagonal, + Radial +} + +public static class GradientDirectionParser +{ + public static GradientDirection Parse (string arg) + { + return arg.ToLower () switch + { + "horizontal" => GradientDirection.Horizontal, + "vertical" => GradientDirection.Vertical, + "diagonal" => GradientDirection.Diagonal, + "radial" => GradientDirection.Radial, + _ => throw new ArgumentException ($"invalid gradient direction: '{arg}' is not a valid gradient direction. Choices are diagonal, horizontal, vertical, or radial."), + }; + } +} + +public static class ColorArg +{ + public static Color Parse (string arg) + { + if (int.TryParse (arg, out int xtermValue) && xtermValue >= 0 && xtermValue <= 255) + { + return new Color (xtermValue); + } + else if (arg.Length == 6 && int.TryParse (arg, NumberStyles.HexNumber, null, out int _)) + { + return new Color (arg); + } + else + { + throw new ArgumentException ($"invalid color value: '{arg}' is not a valid XTerm or RGB color. Must be in range 0-255 or 000000-FFFFFF."); + } + } +} + +public static class Symbol +{ + public static string Parse (string arg) + { + if (arg.Length == 1 && IsAsciiOrUtf8 (arg)) + { + return arg; + } + else + { + throw new ArgumentException ($"invalid symbol: '{arg}' is not a valid symbol. Must be a single ASCII/UTF-8 character."); + } + } + + private static bool IsAsciiOrUtf8 (string s) + { + try + { + Encoding.ASCII.GetBytes (s); + } + catch (EncoderFallbackException) + { + try + { + Encoding.UTF8.GetBytes (s); + } + catch (EncoderFallbackException) + { + return false; + } + } + return true; + } +} + +public static class CanvasDimension +{ + public static int Parse (string arg) + { + if (int.TryParse (arg, out int value) && value >= -1) + { + return value; + } + else + { + throw new ArgumentException ($"invalid value: '{arg}' is not >= -1."); + } + } +} + +public static class TerminalDimensions +{ + public static (int, int) Parse (string arg) + { + var parts = arg.Split (' '); + if (parts.Length == 2 && int.TryParse (parts [0], out int width) && int.TryParse (parts [1], out int height) && width >= 0 && height >= 0) + { + return (width, height); + } + else + { + throw new ArgumentException ($"invalid terminal dimensions: '{arg}' is not a valid terminal dimension. Must be >= 0."); + } + } +} + +public static class Ease +{ + private static readonly Dictionary easingFuncMap = new () + { + {"linear", Easing.Linear}, + {"in_sine", Easing.InSine}, + {"out_sine", Easing.OutSine}, + {"in_out_sine", Easing.InOutSine}, + {"in_quad", Easing.InQuad}, + {"out_quad", Easing.OutQuad}, + {"in_out_quad", Easing.InOutQuad}, + {"in_cubic", Easing.InCubic}, + {"out_cubic", Easing.OutCubic}, + {"in_out_cubic", Easing.InOutCubic}, + {"in_quart", Easing.InQuart}, + {"out_quart", Easing.OutQuart}, + {"in_out_quart", Easing.InOutQuart}, + {"in_quint", Easing.InQuint}, + {"out_quint", Easing.OutQuint}, + {"in_out_quint", Easing.InOutQuint}, + {"in_expo", Easing.InExpo}, + {"out_expo", Easing.OutExpo}, + {"in_out_expo", Easing.InOutExpo}, + {"in_circ", Easing.InCirc}, + {"out_circ", Easing.OutCirc}, + {"in_out_circ", Easing.InOutCirc}, + {"in_back", Easing.InBack}, + {"out_back", Easing.OutBack}, + {"in_out_back", Easing.InOutBack}, + {"in_elastic", Easing.InElastic}, + {"out_elastic", Easing.OutElastic}, + {"in_out_elastic", Easing.InOutElastic}, + {"in_bounce", Easing.InBounce}, + {"out_bounce", Easing.OutBounce}, + {"in_out_bounce", Easing.InOutBounce}, + }; + + public static EasingFunction Parse (string arg) + { + if (easingFuncMap.TryGetValue (arg.ToLower (), out var easingFunc)) + { + return easingFunc; + } + else + { + throw new ArgumentException ($"invalid ease value: '{arg}' is not a valid ease."); + } + } +} diff --git a/Terminal.Gui/TextEffects/BaseEffect.cs b/Terminal.Gui/TextEffects/BaseEffect.cs index 09a9059ba..e10699a9a 100644 --- a/Terminal.Gui/TextEffects/BaseEffect.cs +++ b/Terminal.Gui/TextEffects/BaseEffect.cs @@ -6,10 +6,16 @@ public abstract class BaseEffectIterator where T : EffectConfig, new() protected Terminal Terminal { get; set; } protected List ActiveCharacters { get; set; } = new List (); + protected BaseEffect Effect { get; } + + + public BaseEffectIterator (BaseEffect effect) { + Effect = effect; Config = effect.EffectConfig; Terminal = new Terminal (effect.InputData, effect.TerminalConfig); + } public void Update () diff --git a/Terminal.Gui/TextEffects/Effects/Beams.cs b/Terminal.Gui/TextEffects/Effects/Beams.cs new file mode 100644 index 000000000..b39ef9a91 --- /dev/null +++ b/Terminal.Gui/TextEffects/Effects/Beams.cs @@ -0,0 +1,241 @@ +/*namespace Terminal.Gui.TextEffects.Effects; + +public class BeamsConfig : EffectConfig +{ + public string [] BeamRowSymbols { get; set; } = { "▂", "▁", "_" }; + public string [] BeamColumnSymbols { get; set; } = { "▌", "▍", "▎", "▏" }; + public int BeamDelay { get; set; } = 10; + public (int, int) BeamRowSpeedRange { get; set; } = (10, 40); + public (int, int) BeamColumnSpeedRange { get; set; } = (6, 10); + public Color [] BeamGradientStops { get; set; } = { new Color ("ffffff"), new Color ("00D1FF"), new Color ("8A008A") }; + public int [] BeamGradientSteps { get; set; } = { 2, 8 }; + public int BeamGradientFrames { get; set; } = 2; + public Color [] FinalGradientStops { get; set; } = { new Color ("8A008A"), new Color ("00D1FF"), new Color ("ffffff") }; + public int [] FinalGradientSteps { get; set; } = { 12 }; + public int FinalGradientFrames { get; set; } = 5; + public GradientDirection FinalGradientDirection { get; set; } = GradientDirection.Vertical; + public int FinalWipeSpeed { get; set; } = 1; +} + +public class Beams : BaseEffect +{ + public Beams (string inputData) : base (inputData) + { + } + + protected override BaseEffectIterator CreateIterator () + { + return new BeamsIterator (this); + } +} + + +public class BeamsIterator : BaseEffectIterator +{ + private class Group + { + public List Characters { get; private set; } + public string Direction { get; private set; } + private Terminal Terminal; + private BeamsConfig Config; + private double Speed; + private float NextCharacterCounter; + private List SortedCharacters; + + public Group (List characters, string direction, Terminal terminal, BeamsConfig config) + { + Characters = characters; + Direction = direction; + Terminal = terminal; + Config = config; + Speed = new Random ().Next (config.BeamRowSpeedRange.Item1, config.BeamRowSpeedRange.Item2) * 0.1; + NextCharacterCounter = 0; + SortedCharacters = direction == "row" + ? characters.OrderBy (c => c.InputCoord.Column).ToList () + : characters.OrderBy (c => c.InputCoord.Row).ToList (); + + if (new Random ().Next (0, 2) == 0) + { + SortedCharacters.Reverse (); + } + } + + public void IncrementNextCharacterCounter () + { + NextCharacterCounter += (float)Speed; + } + + public EffectCharacter GetNextCharacter () + { + NextCharacterCounter -= 1; + var nextCharacter = SortedCharacters.First (); + SortedCharacters.RemoveAt (0); + if (nextCharacter.Animation.ActiveScene != null) + { + nextCharacter.Animation.ActiveScene.ResetScene (); + return null; + } + + Terminal.SetCharacterVisibility (nextCharacter, true); + nextCharacter.Animation.ActivateScene (nextCharacter.Animation.QueryScene ("beam_" + Direction)); + return nextCharacter; + } + + public bool Complete () + { + return !SortedCharacters.Any (); + } + } + + private List PendingGroups = new List (); + private Dictionary CharacterFinalColorMap = new Dictionary (); + private List ActiveGroups = new List (); + private int Delay = 0; + private string Phase = "beams"; + private List> FinalWipeGroups; + + public BeamsIterator (Beams effect) : base (effect) + { + Build (); + } + + private void Build () + { + var finalGradient = new Gradient (Effect.Config.FinalGradientStops, Effect.Config.FinalGradientSteps); + var finalGradientMapping = finalGradient.BuildCoordinateColorMapping ( + Effect.Terminal.Canvas.Top, + Effect.Terminal.Canvas.Right, + Effect.Config.FinalGradientDirection + ); + + foreach (var character in Effect.Terminal.GetCharacters (fillChars: true)) + { + CharacterFinalColorMap [character] = finalGradientMapping [character.InputCoord]; + } + + var beamGradient = new Gradient (Effect.Config.BeamGradientStops, Effect.Config.BeamGradientSteps); + var groups = new List (); + + foreach (var row in Effect.Terminal.GetCharactersGrouped (Terminal.CharacterGroup.RowTopToBottom, fillChars: true)) + { + groups.Add (new Group (row, "row", Effect.Terminal, Effect.Config)); + } + + foreach (var column in Effect.Terminal.GetCharactersGrouped (Terminal.CharacterGroup.ColumnLeftToRight, fillChars: true)) + { + groups.Add (new Group (column, "column", Effect.Terminal, Effect.Config)); + } + + foreach (var group in groups) + { + foreach (var character in group.Characters) + { + var beamRowScene = character.Animation.NewScene (id: "beam_row"); + var beamColumnScene = character.Animation.NewScene (id: "beam_column"); + beamRowScene.ApplyGradientToSymbols ( + beamGradient, Effect.Config.BeamRowSymbols, Effect.Config.BeamGradientFrames); + beamColumnScene.ApplyGradientToSymbols ( + beamGradient, Effect.Config.BeamColumnSymbols, Effect.Config.BeamGradientFrames); + + var fadedColor = character.Animation.AdjustColorBrightness (CharacterFinalColorMap [character], 0.3f); + var fadeGradient = new Gradient (CharacterFinalColorMap [character], fadedColor, steps: 10); + beamRowScene.ApplyGradientToSymbols (fadeGradient, character.InputSymbol, 5); + beamColumnScene.ApplyGradientToSymbols (fadeGradient, character.InputSymbol, 5); + + var brightenGradient = new Gradient (fadedColor, CharacterFinalColorMap [character], steps: 10); + var brightenScene = character.Animation.NewScene (id: "brighten"); + brightenScene.ApplyGradientToSymbols ( + brightenGradient, character.InputSymbol, Effect.Config.FinalGradientFrames); + } + } + + PendingGroups = groups; + new Random ().Shuffle (PendingGroups); + } + + public override bool MoveNext () + { + if (Phase != "complete" || ActiveCharacters.Any ()) + { + if (Phase == "beams") + { + if (Delay == 0) + { + if (PendingGroups.Any ()) + { + for (int i = 0; i < new Random ().Next (1, 6); i++) + { + if (PendingGroups.Any ()) + { + ActiveGroups.Add (PendingGroups.First ()); + PendingGroups.RemoveAt (0); + } + } + } + Delay = Effect.Config.BeamDelay; + } + else + { + Delay--; + } + + foreach (var group in ActiveGroups) + { + group.IncrementNextCharacterCounter (); + if ((int)group.NextCharacterCounter > 1) + { + for (int i = 0; i < (int)group.NextCharacterCounter; i++) + { + if (!group.Complete ()) + { + var nextChar = group.GetNextCharacter (); + if (nextChar != null) + { + ActiveCharacters.Add (nextChar); + } + } + } + } + } + + ActiveGroups = ActiveGroups.Where (g => !g.Complete ()).ToList (); + if (!PendingGroups.Any () && !ActiveGroups.Any () && !ActiveCharacters.Any ()) + { + Phase = "final_wipe"; + } + } + else if (Phase == "final_wipe") + { + if (FinalWipeGroups.Any ()) + { + for (int i = 0; i < Effect.Config.FinalWipeSpeed; i++) + { + if (!FinalWipeGroups.Any ()) break; + + var nextGroup = FinalWipeGroups.First (); + FinalWipeGroups.RemoveAt (0); + + foreach (var character in nextGroup) + { + character.Animation.ActivateScene (character.Animation.QueryScene ("brighten")); + Effect.Terminal.SetCharacterVisibility (character, true); + ActiveCharacters.Add (character); + } + } + } + else + { + Phase = "complete"; + } + } + + Update (); + return true; + } + else + { + return false; + } + } +} +*/ \ No newline at end of file diff --git a/Terminal.Gui/TextEffects/Graphics.cs b/Terminal.Gui/TextEffects/Graphics.cs index 73cd02263..03fb2059a 100644 --- a/Terminal.Gui/TextEffects/Graphics.cs +++ b/Terminal.Gui/TextEffects/Graphics.cs @@ -54,51 +54,33 @@ public class Color } } - public class Gradient { public List Spectrum { get; private set; } + private readonly bool _loop; + private readonly List _stops; + private readonly List _steps; + + public enum Direction + { + Vertical, + Horizontal, + Radial, + Diagonal + } - // Constructor now accepts IEnumerable for steps. public Gradient (IEnumerable stops, IEnumerable steps, bool loop = false) { - if (stops == null || !stops.Any () || stops.Count () < 2) - throw new ArgumentException ("At least two color stops are required to create a gradient."); - if (steps == null || !steps.Any ()) - throw new ArgumentException ("Steps are required to define the transitions between colors."); + _stops = stops.ToList (); + if (_stops.Count < 1) + throw new ArgumentException ("At least one color stop must be provided."); - Spectrum = GenerateGradient (stops.ToList (), steps.ToList (), loop); - } + _steps = steps.ToList (); + if (_steps.Any (step => step < 1)) + throw new ArgumentException ("Steps must be greater than 0."); - private List GenerateGradient (List stops, List steps, bool loop) - { - List gradient = new List (); - if (loop) - stops.Add (stops [0]); // Loop the gradient back to the first color. - - for (int i = 0; i < stops.Count - 1; i++) - { - int currentSteps = i < steps.Count ? steps [i] : steps.Last (); - gradient.AddRange (InterpolateColors (stops [i], stops [i + 1], currentSteps)); - } - - return gradient; - } - - private IEnumerable InterpolateColors (Color start, Color end, int steps) - { - for (int step = 0; step <= steps; step++) - { - int r = Interpolate (start.R, end.R, steps, step); - int g = Interpolate (start.G, end.G, steps, step); - int b = Interpolate (start.B, end.B, steps, step); - yield return Color.FromRgb (r, g, b); - } - } - - private int Interpolate (int start, int end, int steps, int currentStep) - { - return start + (int)((end - start) * (double)currentStep / steps); + _loop = loop; + Spectrum = GenerateGradient (_steps); } public Color GetColorAtFraction (double fraction) @@ -108,6 +90,113 @@ public class Gradient int index = (int)(fraction * (Spectrum.Count - 1)); return Spectrum [index]; } -} + private List GenerateGradient (IEnumerable steps) + { + List gradient = new List (); + if (_stops.Count == 1) + { + for (int i = 0; i < steps.Sum (); i++) + { + gradient.Add (_stops [0]); + } + return gradient; + } + if (_loop) + { + _stops.Add (_stops [0]); + } + + var colorPairs = _stops.Zip (_stops.Skip (1), (start, end) => new { start, end }); + var stepsList = _steps.ToList (); + + foreach (var (colorPair, thesteps) in colorPairs.Zip (stepsList, (pair, step) => (pair, step))) + { + gradient.AddRange (InterpolateColors (colorPair.start, colorPair.end, thesteps)); + } + + return gradient; + } + + private IEnumerable InterpolateColors (Color start, Color end, int steps) + { + for (int step = 0; step <= steps; step++) + { + double fraction = (double)step / steps; + int r = (int)(start.R + fraction * (end.R - start.R)); + int g = (int)(start.G + fraction * (end.G - start.G)); + int b = (int)(start.B + fraction * (end.B - start.B)); + yield return Color.FromRgb (r, g, b); + } + } + + public Dictionary BuildCoordinateColorMapping (int maxRow, int maxColumn, Direction direction) + { + var gradientMapping = new Dictionary (); + + switch (direction) + { + case Direction.Vertical: + for (int row = 0; row <= maxRow; row++) + { + double fraction = maxRow == 0 ? 1.0 : (double)row / maxRow; + Color color = GetColorAtFraction (fraction); + for (int col = 0; col <= maxColumn; col++) + { + gradientMapping [new Coord (col, row)] = color; + } + } + break; + + case Direction.Horizontal: + for (int col = 0; col <= maxColumn; col++) + { + double fraction = maxColumn == 0 ? 1.0 : (double)col / maxColumn; + Color color = GetColorAtFraction (fraction); + for (int row = 0; row <= maxRow; row++) + { + gradientMapping [new Coord (col, row)] = color; + } + } + break; + + case Direction.Radial: + for (int row = 0; row <= maxRow; row++) + { + for (int col = 0; col <= maxColumn; col++) + { + double distanceFromCenter = FindNormalizedDistanceFromCenter (maxRow, maxColumn, new Coord (col, row)); + Color color = GetColorAtFraction (distanceFromCenter); + gradientMapping [new Coord (col, row)] = color; + } + } + break; + + case Direction.Diagonal: + for (int row = 0; row <= maxRow; row++) + { + for (int col = 0; col <= maxColumn; col++) + { + double fraction = ((double)row * 2 + col) / ((maxRow * 2) + maxColumn); + Color color = GetColorAtFraction (fraction); + gradientMapping [new Coord (col, row)] = color; + } + } + break; + } + + return gradientMapping; + } + + private double FindNormalizedDistanceFromCenter (int maxRow, int maxColumn, Coord coord) + { + double centerX = maxColumn / 2.0; + double centerY = maxRow / 2.0; + double dx = coord.Column - centerX; + double dy = coord.Row - centerY; + double distance = Math.Sqrt (dx * dx + dy * dy); + double maxDistance = Math.Sqrt (centerX * centerX + centerY * centerY); + return distance / maxDistance; + } +} \ No newline at end of file diff --git a/Terminal.Gui/TextEffects/Motion.cs b/Terminal.Gui/TextEffects/Motion.cs index e2431cd49..9ed544f5e 100644 --- a/Terminal.Gui/TextEffects/Motion.cs +++ b/Terminal.Gui/TextEffects/Motion.cs @@ -1,5 +1,4 @@ namespace Terminal.Gui.TextEffects; - public class Coord { public int Column { get; set; } @@ -12,6 +11,34 @@ public class Coord } public override string ToString () => $"({Column}, {Row})"; + + public override bool Equals (object obj) + { + if (obj is Coord other) + { + return Column == other.Column && Row == other.Row; + } + return false; + } + + public override int GetHashCode () + { + return HashCode.Combine (Column, Row); + } + + public static bool operator == (Coord left, Coord right) + { + if (left is null) + { + return right is null; + } + return left.Equals (right); + } + + public static bool operator != (Coord left, Coord right) + { + return !(left == right); + } } public class Waypoint diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index 2652c5249..3cdd90eee 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -84,6 +84,53 @@ internal class TextEffectsExampleView : View resized = false; } + DrawTopLineGradient (viewport); + DrawRadialGradient (viewport); + + _ball?.Draw (); + } + + private void DrawRadialGradient (Rectangle viewport) + { + // Define the colors of the gradient stops + var stops = new List + { + Color.FromRgb(255, 0, 0), // Red + Color.FromRgb(0, 255, 0), // Green + Color.FromRgb(238, 130, 238) // Violet + }; + + // Define the number of steps between each color + var steps = new List { 10, 10 }; // 10 steps between Red -> Green, and Green -> Blue + + // Create the gradient + var radialGradient = new Gradient (stops, steps, loop: false); + + // Define the size of the rectangle + int maxRow = 20; + int maxColumn = 40; + + // Build the coordinate-color mapping for a radial gradient + var gradientMapping = radialGradient.BuildCoordinateColorMapping (maxRow, maxColumn, Gradient.Direction.Radial); + + // Print the gradient + for (int row = 0; row <= maxRow; row++) + { + for (int col = 0; col <= maxColumn; col++) + { + var coord = new Coord (col, row); + var color = gradientMapping [coord]; + + SetColor (color); + + AddRune (col+2, row+3, new Rune ('█')); + } + } + } + + private void DrawTopLineGradient (Rectangle viewport) + { + // Define the colors of the rainbow var stops = new List { @@ -115,17 +162,21 @@ internal class TextEffectsExampleView : View double fraction = (double)x / (viewport.Width - 1); Color color = rainbowGradient.GetColorAtFraction (fraction); - // Assuming AddRune is a method you have for drawing at specific positions - Application.Driver.SetAttribute ( - new Attribute ( - new Terminal.Gui.Color (color.R, color.G, color.B), - new Terminal.Gui.Color (color.R, color.G, color.B) - )); // Setting color based on RGB + SetColor (color); AddRune (x, 0, new Rune ('█')); } + } + + private void SetColor (Color color) + { + // Assuming AddRune is a method you have for drawing at specific positions + Application.Driver.SetAttribute ( + new Attribute ( + new Terminal.Gui.Color (color.R, color.G, color.B), + new Terminal.Gui.Color (color.R, color.G, color.B) + )); // Setting color based on RGB - _ball?.Draw (); } public class Ball From 9672de726261eb83385c001e75f3461acb0ce51e Mon Sep 17 00:00:00 2001 From: tznind Date: Sat, 6 Jul 2024 20:43:23 +0100 Subject: [PATCH 08/48] Add other gradients --- UICatalog/Scenarios/TextEffectsScenario.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index 3cdd90eee..21347766a 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -85,12 +85,17 @@ internal class TextEffectsExampleView : View } DrawTopLineGradient (viewport); - DrawRadialGradient (viewport); + + int x = 2; + DrawGradientArea (Gradient.Direction.Horizontal,x); + DrawGradientArea (Gradient.Direction.Vertical, x += 41); + DrawGradientArea (Gradient.Direction.Radial, x += 41); + DrawGradientArea (Gradient.Direction.Diagonal, x += 41); _ball?.Draw (); } - private void DrawRadialGradient (Rectangle viewport) + private void DrawGradientArea (Gradient.Direction direction, int xOffset) { // Define the colors of the gradient stops var stops = new List @@ -111,7 +116,7 @@ internal class TextEffectsExampleView : View int maxColumn = 40; // Build the coordinate-color mapping for a radial gradient - var gradientMapping = radialGradient.BuildCoordinateColorMapping (maxRow, maxColumn, Gradient.Direction.Radial); + var gradientMapping = radialGradient.BuildCoordinateColorMapping (maxRow, maxColumn, direction); // Print the gradient for (int row = 0; row <= maxRow; row++) @@ -123,7 +128,7 @@ internal class TextEffectsExampleView : View SetColor (color); - AddRune (col+2, row+3, new Rune ('█')); + AddRune (col+ xOffset, row+3, new Rune ('█')); } } } From 1f13ec5ab783183411cc6a62bd7b6bde3c00dfb2 Mon Sep 17 00:00:00 2001 From: tznind Date: Sat, 6 Jul 2024 20:57:15 +0100 Subject: [PATCH 09/48] Streamline gradients example and add tabs --- UICatalog/Scenarios/TextEffectsScenario.cs | 231 +++++++++++++++------ 1 file changed, 169 insertions(+), 62 deletions(-) diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index 21347766a..d5e914c77 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -18,84 +18,83 @@ public class TextEffectsScenario : Scenario public override void Main () { Application.Init (); - var top = Application.Top; + var w = new Window + { + Width = Dim.Fill(), + Height = Dim.Fill (), + }; // Creates a window that occupies the entire terminal with a title. - var window = new Window () + var tabView = new TabView () { - X = 0, - Y = 1, // Leaves one row for the toplevel menu - - // By using Dim.Fill(), it will automatically resize without manual intervention - Width = Dim.Fill (), - Height = Dim.Fill (), - Title = "Text Effects Scenario" - }; - - // Create a large empty view. - var emptyView = new TextEffectsExampleView () - { - X = 0, - Y = 0, Width = Dim.Fill (), Height = Dim.Fill (), }; - window.Add (emptyView); - - // Create a label in the center of the window. - var label = new Label () + var t1 = new Tab () { - X = Pos.Center (), - Y = Pos.Center (), - Width = 10, - Height = 1, - Text = "Hello" + View = new GradientsView () + { + Width = Dim.Fill (), + Height = Dim.Fill (), + }, + DisplayText = "Gradients" + }; + var t2 = new Tab () + { + View = new BallsView () + { + Width = Dim.Fill (), + Height = Dim.Fill (), + }, + DisplayText = "Ball" }; - window.Add (label); - Application.Run (window); + tabView.AddTab (t1,false); + tabView.AddTab (t2,false); + + w.Add (tabView); + + Application.Run (w); + w.Dispose (); + Application.Shutdown (); + this.Dispose (); } } -internal class TextEffectsExampleView : View + +internal class GradientsView : View { - Ball? _ball; - private bool resized; - - protected override void OnViewportChanged (DrawEventArgs e) - { - base.OnViewportChanged (e); - resized = true; - } - public override void OnDrawContent (Rectangle viewport) { base.OnDrawContent (viewport); - if ( - // First time - (_ball == null && viewport.Width > 0 && viewport.Height > 0) - || resized) - { - _ball = new Ball (this); - _ball.Start (); - resized = false; - } - DrawTopLineGradient (viewport); int x = 2; - DrawGradientArea (Gradient.Direction.Horizontal,x); - DrawGradientArea (Gradient.Direction.Vertical, x += 41); - DrawGradientArea (Gradient.Direction.Radial, x += 41); - DrawGradientArea (Gradient.Direction.Diagonal, x += 41); + int y = 3; - _ball?.Draw (); + if (viewport.Height < 25) // Not enough space, render in a single line + { + DrawGradientArea (Gradient.Direction.Horizontal, x, y); + DrawGradientArea (Gradient.Direction.Vertical, x + 32, y); + DrawGradientArea (Gradient.Direction.Radial, x + 64, y); + DrawGradientArea (Gradient.Direction.Diagonal, x + 96, y); + } + else // Enough space, render in two lines + { + DrawGradientArea (Gradient.Direction.Horizontal, x, y); + DrawGradientArea (Gradient.Direction.Vertical, x + 32, y); + DrawGradientArea (Gradient.Direction.Radial, x, y + 17); + DrawGradientArea (Gradient.Direction.Diagonal, x + 32, y + 17); + } } - private void DrawGradientArea (Gradient.Direction direction, int xOffset) + + + + private void DrawGradientArea (Gradient.Direction direction, int xOffset, int yOffset) { // Define the colors of the gradient stops var stops = new List @@ -112,8 +111,8 @@ internal class TextEffectsExampleView : View var radialGradient = new Gradient (stops, steps, loop: false); // Define the size of the rectangle - int maxRow = 20; - int maxColumn = 40; + int maxRow = 15; // Adjusted to keep aspect ratio + int maxColumn = 30; // Build the coordinate-color mapping for a radial gradient var gradientMapping = radialGradient.BuildCoordinateColorMapping (maxRow, maxColumn, direction); @@ -125,17 +124,16 @@ internal class TextEffectsExampleView : View { var coord = new Coord (col, row); var color = gradientMapping [coord]; - + SetColor (color); - AddRune (col+ xOffset, row+3, new Rune ('█')); + AddRune (col + xOffset, row + yOffset, new Rune ('█')); } } } private void DrawTopLineGradient (Rectangle viewport) { - // Define the colors of the rainbow var stops = new List { @@ -181,7 +179,32 @@ internal class TextEffectsExampleView : View new Terminal.Gui.Color (color.R, color.G, color.B), new Terminal.Gui.Color (color.R, color.G, color.B) )); // Setting color based on RGB + } +} +internal class BallsView : View +{ + private Ball? _ball; + private bool _resized; + + protected override void OnViewportChanged (DrawEventArgs e) + { + base.OnViewportChanged (e); + _resized = true; + } + + public override void OnDrawContent (Rectangle viewport) + { + base.OnDrawContent (viewport); + + if ((_ball == null && viewport.Width > 0 && viewport.Height > 0) || _resized) + { + _ball = new Ball (this); + _ball.Start (); + _resized = false; + } + + _ball?.Draw (); } public class Ball @@ -209,13 +232,13 @@ internal class TextEffectsExampleView : View for (int x = 0; x < width; x++) { - int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude + int y = (int)((height - 1) / 2 * (1 + Math.Sin (frequency * x) * 0.8)); // Decrease amplitude BouncingScene.AddFrame ("O", 1); } for (int x = width - 1; x >= 0; x--) { - int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude + int y = (int)((height - 1) / 2 * (1 + Math.Sin (frequency * x) * 0.8)); // Decrease amplitude BouncingScene.AddFrame ("O", 1); } } @@ -230,13 +253,13 @@ internal class TextEffectsExampleView : View for (int x = 0; x < width; x++) { - int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude + int y = (int)((height - 1) / 2 * (1 + Math.Sin (frequency * x) * 0.8)); // Decrease amplitude path.AddWaypoint (new Waypoint ($"waypoint_{x}", new Coord (x, y))); } for (int x = width - 1; x >= 0; x--) { - int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude + int y = (int)((height - 1) / 2 * (1 + Math.Sin (frequency * x) * 0.8)); // Decrease amplitude path.AddWaypoint (new Waypoint ($"waypoint_{x}", new Coord (x, y))); } @@ -266,3 +289,87 @@ internal class TextEffectsExampleView : View } } } + + +public class Ball +{ + public Animation Animation { get; private set; } + public Scene BouncingScene { get; private set; } + public View Viewport { get; private set; } + public EffectCharacter Character { get; private set; } + + public Ball (View viewport) + { + Viewport = viewport; + Character = new EffectCharacter (1, "O", 0, 0); + Animation = Character.Animation; + CreateBouncingScene (); + CreateMotionPath (); + } + + private void CreateBouncingScene () + { + BouncingScene = Animation.NewScene (isLooping: true); + int width = Viewport.Frame.Width; + int height = Viewport.Frame.Height; + double frequency = 4 * Math.PI / width; // Double the frequency + + for (int x = 0; x < width; x++) + { + int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude + BouncingScene.AddFrame ("O", 1); + } + + for (int x = width - 1; x >= 0; x--) + { + int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude + BouncingScene.AddFrame ("O", 1); + } + } + + private void CreateMotionPath () + { + int width = Viewport.Frame.Width; + int height = Viewport.Frame.Height; + double frequency = 4 * Math.PI / width; // Double the frequency + + var path = Character.Motion.CreatePath ("sineWavePath", speed: 1, loop: true); + + for (int x = 0; x < width; x++) + { + int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude + path.AddWaypoint (new Waypoint ($"waypoint_{x}", new Coord (x, y))); + } + + for (int x = width - 1; x >= 0; x--) + { + int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude + path.AddWaypoint (new Waypoint ($"waypoint_{x}", new Coord (x, y))); + } + + Character.Motion.ActivatePath (path); + } + + public void Start () + { + Animation.ActivateScene (BouncingScene); + new Thread (() => + { + while (true) + { + Thread.Sleep (10); // Adjust the speed of animation + Character.Tick (); + + Application.Invoke (() => Viewport.SetNeedsDisplay ()); + } + }) + { IsBackground = true }.Start (); + } + + public void Draw () + { + Application.Driver.SetAttribute (Viewport.ColorScheme.Normal); + Viewport.AddRune (Character.Motion.CurrentCoord.Column, Character.Motion.CurrentCoord.Row, new Rune ('O')); + } +} + From 3171df8e0212c0a44a9676b490aa5d0351f227c7 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 7 Jul 2024 04:41:34 +0100 Subject: [PATCH 10/48] LineCanvas support for gradient fill --- Terminal.Gui/Drawing/FillPair.cs | 29 ++++++++++ Terminal.Gui/Drawing/IFill.cs | 15 +++++ Terminal.Gui/Drawing/LineCanvas.cs | 5 +- Terminal.Gui/TextEffects/ArgValidators.cs | 17 ++---- Terminal.Gui/TextEffects/BaseEffect.cs | 4 +- Terminal.Gui/TextEffects/New/GradientFill.cs | 31 +++++++++++ Terminal.Gui/TextEffects/New/SolidFill.cs | 19 +++++++ Terminal.Gui/TextEffects/Terminal.cs | 4 +- UICatalog/Scenarios/TextEffectsScenario.cs | 58 +++++++++++++++++++- 9 files changed, 164 insertions(+), 18 deletions(-) create mode 100644 Terminal.Gui/Drawing/FillPair.cs create mode 100644 Terminal.Gui/Drawing/IFill.cs create mode 100644 Terminal.Gui/TextEffects/New/GradientFill.cs create mode 100644 Terminal.Gui/TextEffects/New/SolidFill.cs diff --git a/Terminal.Gui/Drawing/FillPair.cs b/Terminal.Gui/Drawing/FillPair.cs new file mode 100644 index 000000000..41eb2b426 --- /dev/null +++ b/Terminal.Gui/Drawing/FillPair.cs @@ -0,0 +1,29 @@ + +using Terminal.Gui.TextEffects; + +namespace Terminal.Gui; + + +/// +/// Describes a pair of which cooperate in creating +/// . One gives foreground color while other gives background. +/// +public class FillPair +{ + public FillPair (GradientFill fore, SolidFill back) + { + Foreground = fore; + Background = back; + } + + IFill Foreground { get; set; } + IFill Background { get; set; } + + internal Attribute? GetAttribute (Point point) + { + return new Attribute ( + Foreground.GetColor (point), + Background.GetColor (point) + ); + } +} diff --git a/Terminal.Gui/Drawing/IFill.cs b/Terminal.Gui/Drawing/IFill.cs new file mode 100644 index 000000000..8f81d305a --- /dev/null +++ b/Terminal.Gui/Drawing/IFill.cs @@ -0,0 +1,15 @@ + +namespace Terminal.Gui; + +/// +/// Describes an area fill (e.g. solid color or gradient). +/// +public interface IFill +{ + /// + /// Returns the color that should be used at the given point + /// + /// + /// + Color GetColor (Point point); +} \ No newline at end of file diff --git a/Terminal.Gui/Drawing/LineCanvas.cs b/Terminal.Gui/Drawing/LineCanvas.cs index b1e0ced13..2fa0d8a9e 100644 --- a/Terminal.Gui/Drawing/LineCanvas.cs +++ b/Terminal.Gui/Drawing/LineCanvas.cs @@ -4,6 +4,7 @@ namespace Terminal.Gui; /// Facilitates box drawing and line intersection detection and rendering. Does not support diagonal lines. public class LineCanvas : IDisposable { + public FillPair? Fill { get; set; } private readonly List _lines = []; private readonly Dictionary _runeResolvers = new () @@ -324,7 +325,9 @@ public class LineCanvas : IDisposable /// private bool Exactly (HashSet intersects, params IntersectionType [] types) { return intersects.SetEquals (types); } - private Attribute? GetAttributeForIntersects (IntersectionDefinition? [] intersects) { return intersects [0]!.Line.Attribute; } + private Attribute? GetAttributeForIntersects (IntersectionDefinition? [] intersects) { + return Fill != null ? Fill.GetAttribute(intersects [0]!.Point): + intersects [0]!.Line.Attribute; } private Cell? GetCellForIntersects (ConsoleDriver driver, IntersectionDefinition? [] intersects) { diff --git a/Terminal.Gui/TextEffects/ArgValidators.cs b/Terminal.Gui/TextEffects/ArgValidators.cs index 4e31e7a51..4070a56ae 100644 --- a/Terminal.Gui/TextEffects/ArgValidators.cs +++ b/Terminal.Gui/TextEffects/ArgValidators.cs @@ -114,24 +114,17 @@ public static class Ratio } } -public enum GradientDirection -{ - Horizontal, - Vertical, - Diagonal, - Radial -} public static class GradientDirectionParser { - public static GradientDirection Parse (string arg) + public static Gradient.Direction Parse (string arg) { return arg.ToLower () switch { - "horizontal" => GradientDirection.Horizontal, - "vertical" => GradientDirection.Vertical, - "diagonal" => GradientDirection.Diagonal, - "radial" => GradientDirection.Radial, + "horizontal" => Gradient.Direction.Horizontal, + "vertical" => Gradient.Direction.Vertical, + "diagonal" => Gradient.Direction.Diagonal, + "radial" => Gradient.Direction.Radial, _ => throw new ArgumentException ($"invalid gradient direction: '{arg}' is not a valid gradient direction. Choices are diagonal, horizontal, vertical, or radial."), }; } diff --git a/Terminal.Gui/TextEffects/BaseEffect.cs b/Terminal.Gui/TextEffects/BaseEffect.cs index e10699a9a..f8a710302 100644 --- a/Terminal.Gui/TextEffects/BaseEffect.cs +++ b/Terminal.Gui/TextEffects/BaseEffect.cs @@ -3,7 +3,7 @@ public abstract class BaseEffectIterator where T : EffectConfig, new() { protected T Config { get; set; } - protected Terminal Terminal { get; set; } + protected TerminalA Terminal { get; set; } protected List ActiveCharacters { get; set; } = new List (); protected BaseEffect Effect { get; } @@ -14,7 +14,7 @@ public abstract class BaseEffectIterator where T : EffectConfig, new() { Effect = effect; Config = effect.EffectConfig; - Terminal = new Terminal (effect.InputData, effect.TerminalConfig); + Terminal = new TerminalA (effect.InputData, effect.TerminalConfig); } diff --git a/Terminal.Gui/TextEffects/New/GradientFill.cs b/Terminal.Gui/TextEffects/New/GradientFill.cs new file mode 100644 index 000000000..e17351469 --- /dev/null +++ b/Terminal.Gui/TextEffects/New/GradientFill.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Terminal.Gui.TextEffects; + +/// +/// Implementation of that uses a color gradient (including +/// radial, diagonal etc). +/// +public class GradientFill : IFill +{ + private Dictionary _map; + + public GradientFill (Rectangle area, Gradient gradient, Gradient.Direction direction) + { + _map = + gradient.BuildCoordinateColorMapping (area.Height, area.Width, direction) + .ToDictionary( + (k)=> new Point(k.Key.Column,k.Key.Row), + (v)=> new Terminal.Gui.Color (v.Value.R, v.Value.G, v.Value.B)); + } + + public Terminal.Gui.Color GetColor (Point point) + { + return _map [point]; + } +} diff --git a/Terminal.Gui/TextEffects/New/SolidFill.cs b/Terminal.Gui/TextEffects/New/SolidFill.cs new file mode 100644 index 000000000..4bcc174f5 --- /dev/null +++ b/Terminal.Gui/TextEffects/New/SolidFill.cs @@ -0,0 +1,19 @@ +namespace Terminal.Gui.TextEffects; + + +/// +/// implementation that uses a solid color for all points +/// +public class SolidFill : IFill +{ + readonly Terminal.Gui.Color _color; + + public SolidFill (Terminal.Gui.Color color) + { + _color = color; + } + public Gui.Color GetColor (Point point) + { + return _color; + } +} diff --git a/Terminal.Gui/TextEffects/Terminal.cs b/Terminal.Gui/TextEffects/Terminal.cs index 9302f0d47..08522069d 100644 --- a/Terminal.Gui/TextEffects/Terminal.cs +++ b/Terminal.Gui/TextEffects/Terminal.cs @@ -57,13 +57,13 @@ public class Canvas } } -public class Terminal +public class TerminalA { public TerminalConfig Config { get; } public Canvas Canvas { get; private set; } private Dictionary CharacterByInputCoord = new Dictionary (); - public Terminal (string input, TerminalConfig config = null) + public TerminalA (string input, TerminalConfig config = null) { Config = config ?? new TerminalConfig (); var dimensions = GetTerminalDimensions (); diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index d5e914c77..5cfd265f2 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -15,6 +15,8 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("Colors")] public class TextEffectsScenario : Scenario { + private TabView tabView; + public override void Main () { Application.Init (); @@ -24,8 +26,30 @@ public class TextEffectsScenario : Scenario Height = Dim.Fill (), }; + w.Loaded += (s, e) => + { + SetupGradientLineCanvas (w, w.Frame.Size); + // TODO: Does not work + // SetupGradientLineCanvas (tabView, tabView.Frame.Size); + }; + w.SizeChanging += (s,e)=> + { + SetupGradientLineCanvas (w, e.Size); + // TODO: Does not work + //SetupGradientLineCanvas (tabView, tabView.Frame.Size); + }; + + w.ColorScheme = new ColorScheme + { + Normal = new Terminal.Gui.Attribute (ColorName.White, ColorName.Black), + Focus = new Terminal.Gui.Attribute (ColorName.Black,ColorName.White), + HotNormal = new Terminal.Gui.Attribute (ColorName.White, ColorName.Black), + HotFocus = new Terminal.Gui.Attribute (ColorName.White, ColorName.Black), + Disabled = new Terminal.Gui.Attribute (ColorName.Gray, ColorName.Black) + }; + // Creates a window that occupies the entire terminal with a title. - var tabView = new TabView () + tabView = new TabView () { Width = Dim.Fill (), Height = Dim.Fill (), @@ -61,6 +85,38 @@ public class TextEffectsScenario : Scenario Application.Shutdown (); this.Dispose (); } + + + private void SetupGradientLineCanvas (View w, Size? size) + { + GetAppealingGradientColors (out var stops, out var steps); + + var g = new Gradient (stops, steps); + + var fore = new GradientFill ( + new Rectangle (0, 0, size.Value.Width, size.Value.Height), g, Gradient.Direction.Diagonal); + var back = new SolidFill (new Terminal.Gui.Color (ColorName.Black)); + + w.LineCanvas.Fill = new FillPair ( + fore, + back); + } + + private void GetAppealingGradientColors (out List stops, out List steps) + { + // Define the colors of the gradient stops with more appealing colors + stops = new List + { + Color.FromRgb(0, 128, 255), // Bright Blue + Color.FromRgb(0, 255, 128), // Bright Green + Color.FromRgb(255, 255, 0), // Bright Yellow + Color.FromRgb(255, 128, 0), // Bright Orange + Color.FromRgb(255, 0, 128) // Bright Pink + }; + + // Define the number of steps between each color for smoother transitions + steps = new List { 15, 15, 15, 15 }; // 15 steps between each color + } } From c1e82e63e4469824b5f1121c97ae9ef01ea1060b Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 7 Jul 2024 05:34:50 +0100 Subject: [PATCH 11/48] Add tests, corners not working properly for some reason --- Terminal.Gui/TextEffects/New/GradientFill.cs | 16 ++- UICatalog/Scenarios/TextEffectsScenario.cs | 22 ++-- .../TextEffects/New/GradientFillTests.cs | 111 ++++++++++++++++++ 3 files changed, 136 insertions(+), 13 deletions(-) create mode 100644 UnitTests/TextEffects/New/GradientFillTests.cs diff --git a/Terminal.Gui/TextEffects/New/GradientFill.cs b/Terminal.Gui/TextEffects/New/GradientFill.cs index e17351469..a6d69f4e3 100644 --- a/Terminal.Gui/TextEffects/New/GradientFill.cs +++ b/Terminal.Gui/TextEffects/New/GradientFill.cs @@ -17,15 +17,19 @@ public class GradientFill : IFill public GradientFill (Rectangle area, Gradient gradient, Gradient.Direction direction) { - _map = + _map = gradient.BuildCoordinateColorMapping (area.Height, area.Width, direction) - .ToDictionary( - (k)=> new Point(k.Key.Column,k.Key.Row), - (v)=> new Terminal.Gui.Color (v.Value.R, v.Value.G, v.Value.B)); + .ToDictionary ( + (k) => new Point (k.Key.Column, k.Key.Row), + (v) => new Terminal.Gui.Color (v.Value.R, v.Value.G, v.Value.B)); } public Terminal.Gui.Color GetColor (Point point) { - return _map [point]; + if (_map.TryGetValue (point, out var color)) + { + return color; + } + return new Terminal.Gui.Color (0, 0, 0); // Default to black if point not found } -} +} \ No newline at end of file diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index 5cfd265f2..b8e65b8c2 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -24,6 +24,7 @@ public class TextEffectsScenario : Scenario { Width = Dim.Fill(), Height = Dim.Fill (), + Title = "Text Effects Scenario" }; w.Loaded += (s, e) => @@ -34,7 +35,11 @@ public class TextEffectsScenario : Scenario }; w.SizeChanging += (s,e)=> { - SetupGradientLineCanvas (w, e.Size); + if(e.Size.HasValue) + { + SetupGradientLineCanvas (w, e.Size.Value); + } + // TODO: Does not work //SetupGradientLineCanvas (tabView, tabView.Frame.Size); }; @@ -87,14 +92,14 @@ public class TextEffectsScenario : Scenario } - private void SetupGradientLineCanvas (View w, Size? size) + private void SetupGradientLineCanvas (View w, Size size) { - GetAppealingGradientColors (out var stops, out var steps); + GetAppealingGradientColors (size, out var stops, out var steps); var g = new Gradient (stops, steps); var fore = new GradientFill ( - new Rectangle (0, 0, size.Value.Width, size.Value.Height), g, Gradient.Direction.Diagonal); + new Rectangle (0, 0, size.Width, size.Height), g, Gradient.Direction.Diagonal); var back = new SolidFill (new Terminal.Gui.Color (ColorName.Black)); w.LineCanvas.Fill = new FillPair ( @@ -102,7 +107,7 @@ public class TextEffectsScenario : Scenario back); } - private void GetAppealingGradientColors (out List stops, out List steps) + private void GetAppealingGradientColors (Size size, out List stops, out List steps) { // Define the colors of the gradient stops with more appealing colors stops = new List @@ -114,8 +119,11 @@ public class TextEffectsScenario : Scenario Color.FromRgb(255, 0, 128) // Bright Pink }; - // Define the number of steps between each color for smoother transitions - steps = new List { 15, 15, 15, 15 }; // 15 steps between each color + // Calculate the number of steps based on the size + int maxSteps = Math.Max (size.Width, size.Height); + + // Define the number of steps between each color for smoother transitions + steps = new List { maxSteps / 4, maxSteps / 4, maxSteps / 4, maxSteps / 4 }; } } diff --git a/UnitTests/TextEffects/New/GradientFillTests.cs b/UnitTests/TextEffects/New/GradientFillTests.cs new file mode 100644 index 000000000..400425dc5 --- /dev/null +++ b/UnitTests/TextEffects/New/GradientFillTests.cs @@ -0,0 +1,111 @@ +namespace Terminal.Gui.TextEffects.Tests; + +public class GradientFillTests +{ + private Gradient _gradient; + + public GradientFillTests () + { + // Define the colors of the gradient stops + var stops = new List + { + Color.FromRgb(255, 0, 0), // Red + Color.FromRgb(0, 0, 255) // Blue + }; + + // Define the number of steps between each color + var steps = new List { 10 }; // 10 steps between Red -> Blue + + _gradient = new Gradient (stops, steps, loop: false); + } + + [Fact] + public void TestGradientFillCorners () + { + var area = new Rectangle (0, 0, 10, 10); + var gradientFill = new GradientFill (area, _gradient, Gradient.Direction.Diagonal); + + // Test the corners + var topLeft = new Point (0, 0); + var topRight = new Point (area.Width - 1, 0); + var bottomLeft = new Point (0, area.Height - 1); + var bottomRight = new Point (area.Width - 1, area.Height - 1); + + var topLeftColor = gradientFill.GetColor (topLeft); + var topRightColor = gradientFill.GetColor (topRight); + var bottomLeftColor = gradientFill.GetColor (bottomLeft); + var bottomRightColor = gradientFill.GetColor (bottomRight); + + // Validate the colors at the corners + Assert.NotNull (topLeftColor); + Assert.NotNull (topRightColor); + Assert.NotNull (bottomLeftColor); + Assert.NotNull (bottomRightColor); + + // Expected colors + var expectedTopLeftColor = new Terminal.Gui.Color (255, 0, 0); // Red + var expectedBottomRightColor = new Terminal.Gui.Color (0, 0, 255); // Blue + + Assert.Equal (expectedTopLeftColor, topLeftColor); + Assert.Equal (expectedBottomRightColor, bottomRightColor); + + // Additional checks can be added to verify the exact expected colors if known + Console.WriteLine ($"Top-left: {topLeftColor}"); + Console.WriteLine ($"Top-right: {topRightColor}"); + Console.WriteLine ($"Bottom-left: {bottomLeftColor}"); + Console.WriteLine ($"Bottom-right: {bottomRightColor}"); + } + + [Fact] + public void TestGradientFillColorTransition () + { + var area = new Rectangle (0, 0, 10, 10); + var gradientFill = new GradientFill (area, _gradient, Gradient.Direction.Diagonal); + + for (int row = 0; row < area.Height; row++) + { + int previousRed = 255; + int previousBlue = 0; + + for (int col = 0; col < area.Width; col++) + { + var point = new Point (col, row); + var color = gradientFill.GetColor (point); + + // Ensure color is not null + Assert.NotNull (color); + + // Check if the current color is 'more blue' and 'less red' as it goes right and down + Assert.True (color.R <= previousRed, $"Failed at ({col}, {row}): {color.R} > {previousRed}"); + Assert.True (color.B >= previousBlue, $"Failed at ({col}, {row}): {color.B} < {previousBlue}"); + + // Update the previous color values for the next iteration + previousRed = color.R; + previousBlue = color.B; + } + } + + for (int col = 0; col < area.Width; col++) + { + int previousRed = 255; + int previousBlue = 0; + + for (int row = 0; row < area.Height; row++) + { + var point = new Point (col, row); + var color = gradientFill.GetColor (point); + + // Ensure color is not null + Assert.NotNull (color); + + // Check if the current color is 'more blue' and 'less red' as it goes right and down + Assert.True (color.R <= previousRed, $"Failed at ({col}, {row}): {color.R} > {previousRed}"); + Assert.True (color.B >= previousBlue, $"Failed at ({col}, {row}): {color.B} < {previousBlue}"); + + // Update the previous color values for the next iteration + previousRed = color.R; + previousBlue = color.B; + } + } + } +} From a5c1d73c55dca19452d5d8ecc75173bcf88452c0 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 7 Jul 2024 09:32:34 +0100 Subject: [PATCH 12/48] Fix bug in StraightLine as it calculates intersections --- Terminal.Gui/Drawing/StraightLine.cs | 8 +-- UICatalog/Scenarios/TextEffectsScenario.cs | 65 ++++++++++++++++++---- 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/Terminal.Gui/Drawing/StraightLine.cs b/Terminal.Gui/Drawing/StraightLine.cs index 9a2785f0f..f8dfb2d88 100644 --- a/Terminal.Gui/Drawing/StraightLine.cs +++ b/Terminal.Gui/Drawing/StraightLine.cs @@ -114,7 +114,7 @@ public class StraightLine if (StartsAt (x, y)) { return new IntersectionDefinition ( - Start, + new Point (x, y), GetTypeByLength ( IntersectionType.StartLeft, IntersectionType.PassOverHorizontal, @@ -127,7 +127,7 @@ public class StraightLine if (EndsAt (x, y)) { return new IntersectionDefinition ( - Start, + new Point (x, y), Length < 0 ? IntersectionType.StartRight : IntersectionType.StartLeft, this ); @@ -158,7 +158,7 @@ public class StraightLine if (StartsAt (x, y)) { return new IntersectionDefinition ( - Start, + new Point (x, y), GetTypeByLength ( IntersectionType.StartUp, IntersectionType.PassOverVertical, @@ -171,7 +171,7 @@ public class StraightLine if (EndsAt (x, y)) { return new IntersectionDefinition ( - Start, + new Point (x, y), Length < 0 ? IntersectionType.StartDown : IntersectionType.StartUp, this ); diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index b8e65b8c2..920f28879 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -4,7 +4,6 @@ using System.Text; using System.Threading; using Terminal.Gui; using Terminal.Gui.TextEffects; -using static UICatalog.Scenario; using Color = Terminal.Gui.TextEffects.Color; using Animation = Terminal.Gui.TextEffects.Animation; @@ -94,7 +93,7 @@ public class TextEffectsScenario : Scenario private void SetupGradientLineCanvas (View w, Size size) { - GetAppealingGradientColors (size, out var stops, out var steps); + GetAppealingGradientColors (out var stops, out var steps); var g = new Gradient (stops, steps); @@ -107,7 +106,7 @@ public class TextEffectsScenario : Scenario back); } - private void GetAppealingGradientColors (Size size, out List stops, out List steps) + public static void GetAppealingGradientColors (out List stops, out List steps) { // Define the colors of the gradient stops with more appealing colors stops = new List @@ -119,11 +118,8 @@ public class TextEffectsScenario : Scenario Color.FromRgb(255, 0, 128) // Bright Pink }; - // Calculate the number of steps based on the size - int maxSteps = Math.Max (size.Width, size.Height); - - // Define the number of steps between each color for smoother transitions - steps = new List { maxSteps / 4, maxSteps / 4, maxSteps / 4, maxSteps / 4 }; + // Define the number of steps between each color for smoother transitions + steps = new List { 15,15, 15, 15 }; } } @@ -144,14 +140,14 @@ internal class GradientsView : View DrawGradientArea (Gradient.Direction.Horizontal, x, y); DrawGradientArea (Gradient.Direction.Vertical, x + 32, y); DrawGradientArea (Gradient.Direction.Radial, x + 64, y); - DrawGradientArea (Gradient.Direction.Diagonal, x + 96, y); + //DrawGradientArea (Gradient.Direction.Diagonal, x + 96, y); } else // Enough space, render in two lines { DrawGradientArea (Gradient.Direction.Horizontal, x, y); DrawGradientArea (Gradient.Direction.Vertical, x + 32, y); DrawGradientArea (Gradient.Direction.Radial, x, y + 17); - DrawGradientArea (Gradient.Direction.Diagonal, x + 32, y + 17); + //DrawGradientArea (Gradient.Direction.Diagonal, x + 32, y + 17); } } @@ -250,11 +246,26 @@ internal class BallsView : View { private Ball? _ball; private bool _resized; + private LineCanvas lc; + private Gradient gradient; protected override void OnViewportChanged (DrawEventArgs e) { base.OnViewportChanged (e); _resized = true; + + lc = new LineCanvas (new []{ + new StraightLine(new System.Drawing.Point(0,0),10,Orientation.Horizontal,LineStyle.Single), + + }); + TextEffectsScenario.GetAppealingGradientColors (out var stops, out var steps); + gradient = new Gradient (stops, steps); + var fill = new FillPair ( + new GradientFill (new System.Drawing.Rectangle (0, 0, 10, 0), gradient , Gradient.Direction.Horizontal), + new SolidFill(Terminal.Gui.Color.Black) + ); + lc.Fill = fill; + } public override void OnDrawContent (Rectangle viewport) @@ -269,8 +280,42 @@ internal class BallsView : View } _ball?.Draw (); + + foreach(var map in lc.GetCellMap()) + { + Driver.SetAttribute (map.Value.Value.Attribute.Value); + AddRune (map.Key.X, map.Key.Y, map.Value.Value.Rune); + } + + for (int x = 0; x < 10; x++) + { + double fraction = (double)x / 10; + Color color = gradient.GetColorAtFraction (fraction); + + SetColor (color); + + AddRune (x, 2, new Rune ('█')); + } + + var map2 = gradient.BuildCoordinateColorMapping (0,10,Gradient.Direction.Horizontal); + + for (int x = 0; x < map2.Count; x++) + { + SetColor (map2[new Coord(x,0)]); + + AddRune (x, 3, new Rune ('█')); + } } + private void SetColor (Color color) + { + // Assuming AddRune is a method you have for drawing at specific positions + Application.Driver.SetAttribute ( + new Attribute ( + new Terminal.Gui.Color (color.R, color.G, color.B), + new Terminal.Gui.Color (color.R, color.G, color.B) + )); // Setting color based on RGB + } public class Ball { public Animation Animation { get; private set; } From e80f61b17137c46a193a57f5baae3cc46cbcd822 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 7 Jul 2024 09:34:08 +0100 Subject: [PATCH 13/48] Restore diagonal demo --- UICatalog/Scenarios/TextEffectsScenario.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index 920f28879..2df1131e9 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -140,14 +140,14 @@ internal class GradientsView : View DrawGradientArea (Gradient.Direction.Horizontal, x, y); DrawGradientArea (Gradient.Direction.Vertical, x + 32, y); DrawGradientArea (Gradient.Direction.Radial, x + 64, y); - //DrawGradientArea (Gradient.Direction.Diagonal, x + 96, y); + DrawGradientArea (Gradient.Direction.Diagonal, x + 96, y); } else // Enough space, render in two lines { DrawGradientArea (Gradient.Direction.Horizontal, x, y); DrawGradientArea (Gradient.Direction.Vertical, x + 32, y); DrawGradientArea (Gradient.Direction.Radial, x, y + 17); - //DrawGradientArea (Gradient.Direction.Diagonal, x + 32, y + 17); + DrawGradientArea (Gradient.Direction.Diagonal, x + 32, y + 17); } } From cbcf4b5186e2be1cebfb2b96589c7a1cf59c40f3 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 7 Jul 2024 10:18:28 +0100 Subject: [PATCH 14/48] Remove everything except gradient --- Terminal.Gui/Drawing/FillPair.cs | 2 +- .../Graphics.cs => Drawing/Gradient.cs} | 77 +-- Terminal.Gui/Drawing/GradientFill.cs | 24 + .../{TextEffects/New => Drawing}/SolidFill.cs | 8 +- Terminal.Gui/TextEffects/Animation.cs | 502 ------------------ Terminal.Gui/TextEffects/ArgValidators.cs | 266 ---------- Terminal.Gui/TextEffects/BaseCharacter.cs | 109 ---- Terminal.Gui/TextEffects/BaseEffect.cs | 60 --- Terminal.Gui/TextEffects/Easing.cs | 303 ----------- Terminal.Gui/TextEffects/EffectTemplate.cs | 13 - Terminal.Gui/TextEffects/Effects/Beams.cs | 241 --------- Terminal.Gui/TextEffects/Geometry.cs | 137 ----- Terminal.Gui/TextEffects/HexTerm.cs | 94 ---- Terminal.Gui/TextEffects/Motion.cs | 253 --------- Terminal.Gui/TextEffects/New/GradientFill.cs | 35 -- Terminal.Gui/TextEffects/Terminal.cs | 106 ---- UICatalog/Scenarios/TextEffectsScenario.cs | 289 +--------- UnitTests/TextEffects/AnimationTests.cs | 191 ------- .../TextEffects/New/GradientFillTests.cs | 8 +- 19 files changed, 65 insertions(+), 2653 deletions(-) rename Terminal.Gui/{TextEffects/Graphics.cs => Drawing/Gradient.cs} (63%) create mode 100644 Terminal.Gui/Drawing/GradientFill.cs rename Terminal.Gui/{TextEffects/New => Drawing}/SolidFill.cs (55%) delete mode 100644 Terminal.Gui/TextEffects/Animation.cs delete mode 100644 Terminal.Gui/TextEffects/ArgValidators.cs delete mode 100644 Terminal.Gui/TextEffects/BaseCharacter.cs delete mode 100644 Terminal.Gui/TextEffects/BaseEffect.cs delete mode 100644 Terminal.Gui/TextEffects/Easing.cs delete mode 100644 Terminal.Gui/TextEffects/EffectTemplate.cs delete mode 100644 Terminal.Gui/TextEffects/Effects/Beams.cs delete mode 100644 Terminal.Gui/TextEffects/Geometry.cs delete mode 100644 Terminal.Gui/TextEffects/HexTerm.cs delete mode 100644 Terminal.Gui/TextEffects/Motion.cs delete mode 100644 Terminal.Gui/TextEffects/New/GradientFill.cs delete mode 100644 Terminal.Gui/TextEffects/Terminal.cs delete mode 100644 UnitTests/TextEffects/AnimationTests.cs diff --git a/Terminal.Gui/Drawing/FillPair.cs b/Terminal.Gui/Drawing/FillPair.cs index 41eb2b426..f51ceec65 100644 --- a/Terminal.Gui/Drawing/FillPair.cs +++ b/Terminal.Gui/Drawing/FillPair.cs @@ -1,5 +1,5 @@  -using Terminal.Gui.TextEffects; +using Terminal.Gui.Drawing; namespace Terminal.Gui; diff --git a/Terminal.Gui/TextEffects/Graphics.cs b/Terminal.Gui/Drawing/Gradient.cs similarity index 63% rename from Terminal.Gui/TextEffects/Graphics.cs rename to Terminal.Gui/Drawing/Gradient.cs index 03fb2059a..b5d58e34a 100644 --- a/Terminal.Gui/TextEffects/Graphics.cs +++ b/Terminal.Gui/Drawing/Gradient.cs @@ -1,59 +1,8 @@ -namespace Terminal.Gui.TextEffects; +namespace Terminal.Gui; using System; using System.Collections.Generic; using System.Linq; -public class Color -{ - public string RgbColor { get; private set; } - public int? XtermColor { get; private set; } - - public Color (string rgbColor) - { - if (!ColorUtils.IsValidHexColor (rgbColor)) - throw new ArgumentException ("Invalid RGB hex color format."); - - RgbColor = rgbColor.StartsWith ("#") ? rgbColor.Substring (1).ToUpper () : rgbColor.ToUpper (); - XtermColor = ColorUtils.HexToXterm (RgbColor); // Convert RGB to XTerm-256 - } - - public Color (int xtermColor) - { - if (!ColorUtils.IsValidXtermColor (xtermColor)) - throw new ArgumentException ("Invalid XTerm-256 color code."); - - XtermColor = xtermColor; - RgbColor = ColorUtils.XtermToHex (xtermColor); // Perform the actual conversion - } - public int R => Convert.ToInt32 (RgbColor.Substring (0, 2), 16); - public int G => Convert.ToInt32 (RgbColor.Substring (2, 2), 16); - public int B => Convert.ToInt32 (RgbColor.Substring (4, 2), 16); - - public (int R, int G, int B) GetRgbInts () - { - return ( - Convert.ToInt32 (RgbColor.Substring (0, 2), 16), - Convert.ToInt32 (RgbColor.Substring (2, 2), 16), - Convert.ToInt32 (RgbColor.Substring (4, 2), 16) - ); - } - - public override string ToString () => $"#{RgbColor}"; - - public static Color FromRgb (int r, int g, int b) - { - // Validate the RGB values to ensure they are within the 0-255 range - if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) - throw new ArgumentOutOfRangeException ("RGB values must be between 0 and 255."); - - // Convert RGB values to a hexadecimal string - string rgbColor = $"#{r:X2}{g:X2}{b:X2}"; - - // Create and return a new Color instance using the hexadecimal string - return new Color (rgbColor); - } -} - public class Gradient { public List Spectrum { get; private set; } @@ -127,13 +76,13 @@ public class Gradient int r = (int)(start.R + fraction * (end.R - start.R)); int g = (int)(start.G + fraction * (end.G - start.G)); int b = (int)(start.B + fraction * (end.B - start.B)); - yield return Color.FromRgb (r, g, b); + yield return new Color (r, g, b); } } - public Dictionary BuildCoordinateColorMapping (int maxRow, int maxColumn, Direction direction) + public Dictionary BuildCoordinateColorMapping (int maxRow, int maxColumn, Direction direction) { - var gradientMapping = new Dictionary (); + var gradientMapping = new Dictionary (); switch (direction) { @@ -144,7 +93,7 @@ public class Gradient Color color = GetColorAtFraction (fraction); for (int col = 0; col <= maxColumn; col++) { - gradientMapping [new Coord (col, row)] = color; + gradientMapping [new Point (col, row)] = color; } } break; @@ -156,7 +105,7 @@ public class Gradient Color color = GetColorAtFraction (fraction); for (int row = 0; row <= maxRow; row++) { - gradientMapping [new Coord (col, row)] = color; + gradientMapping [new Point (col, row)] = color; } } break; @@ -166,9 +115,9 @@ public class Gradient { for (int col = 0; col <= maxColumn; col++) { - double distanceFromCenter = FindNormalizedDistanceFromCenter (maxRow, maxColumn, new Coord (col, row)); + double distanceFromCenter = FindNormalizedDistanceFromCenter (maxRow, maxColumn, new Point (col, row)); Color color = GetColorAtFraction (distanceFromCenter); - gradientMapping [new Coord (col, row)] = color; + gradientMapping [new Point (col, row)] = color; } } break; @@ -178,9 +127,9 @@ public class Gradient { for (int col = 0; col <= maxColumn; col++) { - double fraction = ((double)row * 2 + col) / ((maxRow * 2) + maxColumn); + double fraction = ((double)row * 2 + col) / (maxRow * 2 + maxColumn); Color color = GetColorAtFraction (fraction); - gradientMapping [new Coord (col, row)] = color; + gradientMapping [new Point (col, row)] = color; } } break; @@ -189,12 +138,12 @@ public class Gradient return gradientMapping; } - private double FindNormalizedDistanceFromCenter (int maxRow, int maxColumn, Coord coord) + private double FindNormalizedDistanceFromCenter (int maxRow, int maxColumn, Point coord) { double centerX = maxColumn / 2.0; double centerY = maxRow / 2.0; - double dx = coord.Column - centerX; - double dy = coord.Row - centerY; + double dx = coord.X - centerX; + double dy = coord.Y - centerY; double distance = Math.Sqrt (dx * dx + dy * dy); double maxDistance = Math.Sqrt (centerX * centerX + centerY * centerY); return distance / maxDistance; diff --git a/Terminal.Gui/Drawing/GradientFill.cs b/Terminal.Gui/Drawing/GradientFill.cs new file mode 100644 index 000000000..d0bf163da --- /dev/null +++ b/Terminal.Gui/Drawing/GradientFill.cs @@ -0,0 +1,24 @@ +namespace Terminal.Gui; + +/// +/// Implementation of that uses a color gradient (including +/// radial, diagonal etc). +/// +public class GradientFill : IFill +{ + private Dictionary _map; + + public GradientFill (Rectangle area, Gradient gradient, Gradient.Direction direction) + { + _map = gradient.BuildCoordinateColorMapping (area.Height, area.Width, direction); + } + + public Color GetColor (Point point) + { + if (_map.TryGetValue (point, out var color)) + { + return color; + } + return new Color (0, 0, 0); // Default to black if point not found + } +} \ No newline at end of file diff --git a/Terminal.Gui/TextEffects/New/SolidFill.cs b/Terminal.Gui/Drawing/SolidFill.cs similarity index 55% rename from Terminal.Gui/TextEffects/New/SolidFill.cs rename to Terminal.Gui/Drawing/SolidFill.cs index 4bcc174f5..202cec57c 100644 --- a/Terminal.Gui/TextEffects/New/SolidFill.cs +++ b/Terminal.Gui/Drawing/SolidFill.cs @@ -1,4 +1,4 @@ -namespace Terminal.Gui.TextEffects; +namespace Terminal.Gui.Drawing; /// @@ -6,13 +6,13 @@ /// public class SolidFill : IFill { - readonly Terminal.Gui.Color _color; + readonly Color _color; - public SolidFill (Terminal.Gui.Color color) + public SolidFill (Color color) { _color = color; } - public Gui.Color GetColor (Point point) + public Color GetColor (Point point) { return _color; } diff --git a/Terminal.Gui/TextEffects/Animation.cs b/Terminal.Gui/TextEffects/Animation.cs deleted file mode 100644 index a96f08a9e..000000000 --- a/Terminal.Gui/TextEffects/Animation.cs +++ /dev/null @@ -1,502 +0,0 @@ - -using static Terminal.Gui.TextEffects.EventHandler; - -namespace Terminal.Gui.TextEffects; - -public enum SyncMetric -{ - Distance, - Step -} -public class CharacterVisual -{ - public string Symbol { get; set; } - public bool Bold { get; set; } - public bool Dim { get; set; } - public bool Italic { get; set; } - public bool Underline { get; set; } - public bool Blink { get; set; } - public bool Reverse { get; set; } - public bool Hidden { get; set; } - public bool Strike { get; set; } - public Color Color { get; set; } - public string FormattedSymbol { get; private set; } - private string _colorCode; // Holds the ANSI color code or similar string directly - - public string ColorCode => _colorCode; - - public CharacterVisual (string symbol, bool bold = false, bool dim = false, bool italic = false, bool underline = false, bool blink = false, bool reverse = false, bool hidden = false, bool strike = false, Color color = null, string colorCode = null) - { - Symbol = symbol; - Bold = bold; - Dim = dim; - Italic = italic; - Underline = underline; - Blink = blink; - Reverse = reverse; - Hidden = hidden; - Strike = strike; - Color = color; - _colorCode = colorCode; // Initialize _colorCode from the constructor argument - FormattedSymbol = FormatSymbol (); - } - - private string FormatSymbol () - { - string formattingString = ""; - if (Bold) formattingString += Ansitools.ApplyBold (); - if (Italic) formattingString += Ansitools.ApplyItalic (); - if (Underline) formattingString += Ansitools.ApplyUnderline (); - if (Blink) formattingString += Ansitools.ApplyBlink (); - if (Reverse) formattingString += Ansitools.ApplyReverse (); - if (Hidden) formattingString += Ansitools.ApplyHidden (); - if (Strike) formattingString += Ansitools.ApplyStrikethrough (); - if (_colorCode != null) formattingString += Colorterm.Fg (_colorCode); // Use the direct color code - - return $"{formattingString}{Symbol}{(formattingString != "" ? Ansitools.ResetAll () : "")}"; - } - - public void DisableModes () - { - Bold = false; - Dim = false; - Italic = false; - Underline = false; - Blink = false; - Reverse = false; - Hidden = false; - Strike = false; - } -} - - -public class Frame -{ - public CharacterVisual CharacterVisual { get; } - public int Duration { get; } - public int TicksElapsed { get; set; } - - public Frame (CharacterVisual characterVisual, int duration) - { - CharacterVisual = characterVisual; - Duration = duration; - TicksElapsed = 0; - } - - public void IncrementTicks () - { - TicksElapsed++; - } -} - -public class Scene -{ - public string SceneId { get; } - public bool IsLooping { get; } - public SyncMetric? Sync { get; } - public EasingFunction Ease { get; } - public bool NoColor { get; set; } - public bool UseXtermColors { get; set; } - public List Frames { get; } = new List (); - public List PlayedFrames { get; } = new List (); - public Dictionary FrameIndexMap { get; } = new Dictionary (); - public int EasingTotalSteps { get; set; } - public int EasingCurrentStep { get; set; } - public static Dictionary XtermColorMap { get; } = new Dictionary (); - - public Scene (string sceneId, bool isLooping = false, SyncMetric? sync = null, EasingFunction ease = null, bool noColor = false, bool useXtermColors = false) - { - SceneId = sceneId; - IsLooping = isLooping; - Sync = sync; - Ease = ease; - NoColor = noColor; - UseXtermColors = useXtermColors; - EasingTotalSteps = 0; - EasingCurrentStep = 0; - } - - public void AddFrame (string symbol, int duration, Color color = null, bool bold = false, bool dim = false, bool italic = false, bool underline = false, bool blink = false, bool reverse = false, bool hidden = false, bool strike = false) - { - string charVisColor = null; - if (color != null) - { - if (NoColor) - { - charVisColor = null; - } - else if (UseXtermColors && color.XtermColor.HasValue) - { - charVisColor = color.XtermColor.Value.ToString (); - } - else if (color.RgbColor != null && XtermColorMap.ContainsKey (color.RgbColor)) - { - charVisColor = XtermColorMap [color.RgbColor].ToString (); - } - else - { - charVisColor = color.RgbColor; - } - } - - if (duration < 1) - throw new ArgumentException ("Duration must be greater than 0."); - - var characterVisual = new CharacterVisual (symbol, bold, dim, italic, underline, blink, reverse, hidden, strike, color, charVisColor); - var frame = new Frame (characterVisual, duration); - Frames.Add (frame); - for (int i = 0; i < frame.Duration; i++) - { - FrameIndexMap [EasingTotalSteps] = frame; - EasingTotalSteps++; - } - } - - public CharacterVisual Activate () - { - if (Frames.Count == 0) - throw new InvalidOperationException ("Scene has no frames."); - EasingCurrentStep = 0; - return Frames [0].CharacterVisual; - } - - public CharacterVisual GetNextVisual () - { - if (Frames.Count == 0) - return null; - - var frame = Frames [0]; - if (++EasingCurrentStep >= frame.Duration) - { - EasingCurrentStep = 0; - PlayedFrames.Add (frame); - Frames.RemoveAt (0); - if (IsLooping && Frames.Count == 0) - { - Frames.AddRange (PlayedFrames); - PlayedFrames.Clear (); - } - if (Frames.Count > 0) - return Frames [0].CharacterVisual; - } - return frame.CharacterVisual; - } - - public void ApplyGradientToSymbols (Gradient gradient, IList symbols, int duration) - { - int lastIndex = 0; - for (int symbolIndex = 0; symbolIndex < symbols.Count; symbolIndex++) - { - var symbol = symbols [symbolIndex]; - double symbolProgress = (symbolIndex + 1) / (double)symbols.Count; - int gradientIndex = (int)(symbolProgress * gradient.Spectrum.Count); - foreach (var color in gradient.Spectrum.GetRange (lastIndex, Math.Max (gradientIndex - lastIndex, 1))) - { - AddFrame (symbol, duration, color); - } - lastIndex = gradientIndex; - } - } - - public void ResetScene () - { - EasingCurrentStep = 0; - Frames.Clear (); - Frames.AddRange (PlayedFrames); - PlayedFrames.Clear (); - } - - public override bool Equals (object obj) - { - return obj is Scene other && SceneId == other.SceneId; - } - - public override int GetHashCode () - { - return SceneId.GetHashCode (); - } -} - -public class Animation -{ - public Dictionary Scenes { get; } = new Dictionary (); - public EffectCharacter Character { get; } - public Scene ActiveScene { get; private set; } - public bool UseXtermColors { get; set; } = false; - public bool NoColor { get; set; } = false; - public Dictionary XtermColorMap { get; } = new Dictionary (); - public int ActiveSceneCurrentStep { get; private set; } = 0; - public CharacterVisual CurrentCharacterVisual { get; private set; } - - public Animation (EffectCharacter character) - { - Character = character; - CurrentCharacterVisual = new CharacterVisual (character.InputSymbol); - } - - public Scene NewScene (bool isLooping = false, SyncMetric? sync = null, EasingFunction ease = null, string id = "") - { - if (string.IsNullOrEmpty (id)) - { - bool foundUnique = false; - int currentId = Scenes.Count; - while (!foundUnique) - { - id = $"{Scenes.Count}"; - if (!Scenes.ContainsKey (id)) - { - foundUnique = true; - } - else - { - currentId++; - } - } - } - - var newScene = new Scene (id, isLooping, sync, ease); - Scenes [id] = newScene; - newScene.NoColor = NoColor; - newScene.UseXtermColors = UseXtermColors; - return newScene; - } - - public Scene QueryScene (string sceneId) - { - if (!Scenes.TryGetValue (sceneId, out var scene)) - { - throw new ArgumentException ($"Scene {sceneId} does not exist."); - } - return scene; - } - - public bool ActiveSceneIsComplete () - { - if (ActiveScene == null) - { - return true; - } - return ActiveScene.Frames.Count == 0 && !ActiveScene.IsLooping; - } - - public void SetAppearance (string symbol, Color? color = null) - { - string charVisColor = null; - if (color != null) - { - if (NoColor) - { - charVisColor = null; - } - else if (UseXtermColors) - { - charVisColor = color.XtermColor.ToString(); - } - else - { - charVisColor = color.RgbColor; - } - } - CurrentCharacterVisual = new CharacterVisual (symbol, color: color, colorCode: charVisColor); - } - - public static Color RandomColor () - { - var random = new Random (); - var colorHex = random.Next (0, 0xFFFFFF).ToString ("X6"); - return new Color (colorHex); - } - - public static Color AdjustColorBrightness (Color color, float brightness) - { - float HueToRgb (float p, float q, float t) - { - if (t < 0) t += 1; - if (t > 1) t -= 1; - if (t < 1 / 6f) return p + (q - p) * 6 * t; - if (t < 1 / 2f) return q; - if (t < 2 / 3f) return p + (q - p) * (2 / 3f - t) * 6; - return p; - } - - float r = int.Parse (color.RgbColor.Substring (0, 2), System.Globalization.NumberStyles.HexNumber) / 255f; - float g = int.Parse (color.RgbColor.Substring (2, 2), System.Globalization.NumberStyles.HexNumber) / 255f; - float b = int.Parse (color.RgbColor.Substring (4, 2), System.Globalization.NumberStyles.HexNumber) / 255f; - - float max = Math.Max (r, Math.Max (g, b)); - float min = Math.Min (r, Math.Min (g, b)); - float h, s, l = (max + min) / 2f; - - if (max == min) - { - h = s = 0; // achromatic - } - else - { - float d = max - min; - s = l > 0.5f ? d / (2f - max - min) : d / (max + min); - if (max == r) - { - h = (g - b) / d + (g < b ? 6 : 0); - } - else if (max == g) - { - h = (b - r) / d + 2; - } - else - { - h = (r - g) / d + 4; - } - h /= 6; - } - - l = Math.Max (Math.Min (l * brightness, 1), 0); - - if (s == 0) - { - r = g = b = l; // achromatic - } - else - { - float q = l < 0.5f ? l * (1 + s) : l + s - l * s; - float p = 2 * l - q; - r = HueToRgb (p, q, h + 1 / 3f); - g = HueToRgb (p, q, h); - b = HueToRgb (p, q, h - 1 / 3f); - } - - var adjustedColor = $"{(int)(r * 255):X2}{(int)(g * 255):X2}{(int)(b * 255):X2}"; - return new Color (adjustedColor); - } - - private float EaseAnimation (EasingFunction easingFunc) - { - if (ActiveScene == null) - { - return 0; - } - float elapsedStepRatio = ActiveScene.EasingCurrentStep / (float)ActiveScene.EasingTotalSteps; - return easingFunc (elapsedStepRatio); - } - - public void StepAnimation () - { - if (ActiveScene != null && ActiveScene.Frames.Count > 0) - { - if (ActiveScene.Sync != null) - { - if (Character.Motion.ActivePath != null) - { - int sequenceIndex = 0; - if (ActiveScene.Sync == SyncMetric.Step) - { - sequenceIndex = (int)Math.Round ((ActiveScene.Frames.Count - 1) * - (Math.Max (Character.Motion.ActivePath.CurrentStep, 1) / - (float)Math.Max (Character.Motion.ActivePath.MaxSteps, 1))); - } - else if (ActiveScene.Sync == SyncMetric.Distance) - { - sequenceIndex = (int)Math.Round ((ActiveScene.Frames.Count - 1) * - (Math.Max (Math.Max (Character.Motion.ActivePath.TotalDistance, 1) - - Math.Max (Character.Motion.ActivePath.TotalDistance - - Character.Motion.ActivePath.LastDistanceReached, 1), 1) / - (float)Math.Max (Character.Motion.ActivePath.TotalDistance, 1))); - } - try - { - CurrentCharacterVisual = ActiveScene.Frames [sequenceIndex].CharacterVisual; - } - catch (IndexOutOfRangeException) - { - CurrentCharacterVisual = ActiveScene.Frames [^1].CharacterVisual; - } - } - else - { - CurrentCharacterVisual = ActiveScene.Frames [^1].CharacterVisual; - ActiveScene.PlayedFrames.AddRange (ActiveScene.Frames); - ActiveScene.Frames.Clear (); - } - } - else if (ActiveScene.Ease != null) - { - float easingFactor = EaseAnimation (ActiveScene.Ease); - int frameIndex = (int)Math.Round (easingFactor * Math.Max (ActiveScene.EasingTotalSteps - 1, 0)); - frameIndex = Math.Max (Math.Min (frameIndex, ActiveScene.EasingTotalSteps - 1), 0); - Frame frame = ActiveScene.FrameIndexMap [frameIndex]; - CurrentCharacterVisual = frame.CharacterVisual; - ActiveScene.EasingCurrentStep++; - if (ActiveScene.EasingCurrentStep == ActiveScene.EasingTotalSteps) - { - if (ActiveScene.IsLooping) - { - ActiveScene.EasingCurrentStep = 0; - } - else - { - ActiveScene.PlayedFrames.AddRange (ActiveScene.Frames); - ActiveScene.Frames.Clear (); - } - } - } - else - { - CurrentCharacterVisual = ActiveScene.GetNextVisual (); - } - if (ActiveSceneIsComplete ()) - { - var completedScene = ActiveScene; - if (!ActiveScene.IsLooping) - { - ActiveScene.ResetScene (); - ActiveScene = null; - } - Character.EventHandler.HandleEvent (Event.SceneComplete, completedScene); - } - } - } - - public void ActivateScene (Scene scene) - { - ActiveScene = scene; - ActiveSceneCurrentStep = 0; - CurrentCharacterVisual = ActiveScene.Activate (); - Character.EventHandler.HandleEvent (Event.SceneActivated, scene); - } - - public void DeactivateScene (Scene scene) - { - if (ActiveScene == scene) - { - ActiveScene = null; - } - } -} - - -// Dummy classes for Ansitools, Colorterm, and Hexterm as placeholders -public static class Ansitools -{ - public static string ApplyBold () => "\x1b[1m"; - public static string ApplyItalic () => "\x1b[3m"; - public static string ApplyUnderline () => "\x1b[4m"; - public static string ApplyBlink () => "\x1b[5m"; - public static string ApplyReverse () => "\x1b[7m"; - public static string ApplyHidden () => "\x1b[8m"; - public static string ApplyStrikethrough () => "\x1b[9m"; - public static string ResetAll () => "\x1b[0m"; -} - -public static class Colorterm -{ - public static string Fg (string colorCode) => $"\x1b[38;5;{colorCode}m"; -} - -public static class Hexterm -{ - public static string HexToXterm (string hex) - { - // Convert hex color to xterm color code (0-255) - return "15"; // Example output - } -} diff --git a/Terminal.Gui/TextEffects/ArgValidators.cs b/Terminal.Gui/TextEffects/ArgValidators.cs deleted file mode 100644 index 4070a56ae..000000000 --- a/Terminal.Gui/TextEffects/ArgValidators.cs +++ /dev/null @@ -1,266 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; -using Terminal.Gui.TextEffects; - -using Color = Terminal.Gui.TextEffects.Color; - -public static class PositiveInt -{ - public static int Parse (string arg) - { - if (int.TryParse (arg, out int value) && value > 0) - { - return value; - } - else - { - throw new ArgumentException ($"invalid value: '{arg}' is not > 0."); - } - } -} - -public static class NonNegativeInt -{ - public static int Parse (string arg) - { - if (int.TryParse (arg, out int value) && value >= 0) - { - return value; - } - else - { - throw new ArgumentException ($"invalid value: '{arg}' Argument must be int >= 0."); - } - } -} - -public static class IntRange -{ - public static (int, int) Parse (string arg) - { - var parts = arg.Split ('-'); - if (parts.Length == 2 && int.TryParse (parts [0], out int start) && int.TryParse (parts [1], out int end) && start > 0 && start <= end) - { - return (start, end); - } - else - { - throw new ArgumentException ($"invalid range: '{arg}' is not a valid range. Must be start-end. Ex: 1-10"); - } - } -} - -public static class PositiveFloat -{ - public static float Parse (string arg) - { - if (float.TryParse (arg, out float value) && value > 0) - { - return value; - } - else - { - throw new ArgumentException ($"invalid value: '{arg}' is not a valid value. Argument must be a float > 0."); - } - } -} - -public static class NonNegativeFloat -{ - public static float Parse (string arg) - { - if (float.TryParse (arg, out float value) && value >= 0) - { - return value; - } - else - { - throw new ArgumentException ($"invalid argument value: '{arg}' is out of range. Must be float >= 0."); - } - } -} - -public static class PositiveFloatRange -{ - public static (float, float) Parse (string arg) - { - var parts = arg.Split ('-'); - if (parts.Length == 2 && float.TryParse (parts [0], out float start) && float.TryParse (parts [1], out float end) && start > 0 && start <= end) - { - return (start, end); - } - else - { - throw new ArgumentException ($"invalid range: '{arg}' is not a valid range. Must be start-end. Ex: 0.1-1.0"); - } - } -} - -public static class Ratio -{ - public static float Parse (string arg) - { - if (float.TryParse (arg, out float value) && value >= 0 && value <= 1) - { - return value; - } - else - { - throw new ArgumentException ($"invalid value: '{arg}' is not a float >= 0 and <= 1. Example: 0.5"); - } - } -} - - -public static class GradientDirectionParser -{ - public static Gradient.Direction Parse (string arg) - { - return arg.ToLower () switch - { - "horizontal" => Gradient.Direction.Horizontal, - "vertical" => Gradient.Direction.Vertical, - "diagonal" => Gradient.Direction.Diagonal, - "radial" => Gradient.Direction.Radial, - _ => throw new ArgumentException ($"invalid gradient direction: '{arg}' is not a valid gradient direction. Choices are diagonal, horizontal, vertical, or radial."), - }; - } -} - -public static class ColorArg -{ - public static Color Parse (string arg) - { - if (int.TryParse (arg, out int xtermValue) && xtermValue >= 0 && xtermValue <= 255) - { - return new Color (xtermValue); - } - else if (arg.Length == 6 && int.TryParse (arg, NumberStyles.HexNumber, null, out int _)) - { - return new Color (arg); - } - else - { - throw new ArgumentException ($"invalid color value: '{arg}' is not a valid XTerm or RGB color. Must be in range 0-255 or 000000-FFFFFF."); - } - } -} - -public static class Symbol -{ - public static string Parse (string arg) - { - if (arg.Length == 1 && IsAsciiOrUtf8 (arg)) - { - return arg; - } - else - { - throw new ArgumentException ($"invalid symbol: '{arg}' is not a valid symbol. Must be a single ASCII/UTF-8 character."); - } - } - - private static bool IsAsciiOrUtf8 (string s) - { - try - { - Encoding.ASCII.GetBytes (s); - } - catch (EncoderFallbackException) - { - try - { - Encoding.UTF8.GetBytes (s); - } - catch (EncoderFallbackException) - { - return false; - } - } - return true; - } -} - -public static class CanvasDimension -{ - public static int Parse (string arg) - { - if (int.TryParse (arg, out int value) && value >= -1) - { - return value; - } - else - { - throw new ArgumentException ($"invalid value: '{arg}' is not >= -1."); - } - } -} - -public static class TerminalDimensions -{ - public static (int, int) Parse (string arg) - { - var parts = arg.Split (' '); - if (parts.Length == 2 && int.TryParse (parts [0], out int width) && int.TryParse (parts [1], out int height) && width >= 0 && height >= 0) - { - return (width, height); - } - else - { - throw new ArgumentException ($"invalid terminal dimensions: '{arg}' is not a valid terminal dimension. Must be >= 0."); - } - } -} - -public static class Ease -{ - private static readonly Dictionary easingFuncMap = new () - { - {"linear", Easing.Linear}, - {"in_sine", Easing.InSine}, - {"out_sine", Easing.OutSine}, - {"in_out_sine", Easing.InOutSine}, - {"in_quad", Easing.InQuad}, - {"out_quad", Easing.OutQuad}, - {"in_out_quad", Easing.InOutQuad}, - {"in_cubic", Easing.InCubic}, - {"out_cubic", Easing.OutCubic}, - {"in_out_cubic", Easing.InOutCubic}, - {"in_quart", Easing.InQuart}, - {"out_quart", Easing.OutQuart}, - {"in_out_quart", Easing.InOutQuart}, - {"in_quint", Easing.InQuint}, - {"out_quint", Easing.OutQuint}, - {"in_out_quint", Easing.InOutQuint}, - {"in_expo", Easing.InExpo}, - {"out_expo", Easing.OutExpo}, - {"in_out_expo", Easing.InOutExpo}, - {"in_circ", Easing.InCirc}, - {"out_circ", Easing.OutCirc}, - {"in_out_circ", Easing.InOutCirc}, - {"in_back", Easing.InBack}, - {"out_back", Easing.OutBack}, - {"in_out_back", Easing.InOutBack}, - {"in_elastic", Easing.InElastic}, - {"out_elastic", Easing.OutElastic}, - {"in_out_elastic", Easing.InOutElastic}, - {"in_bounce", Easing.InBounce}, - {"out_bounce", Easing.OutBounce}, - {"in_out_bounce", Easing.InOutBounce}, - }; - - public static EasingFunction Parse (string arg) - { - if (easingFuncMap.TryGetValue (arg.ToLower (), out var easingFunc)) - { - return easingFunc; - } - else - { - throw new ArgumentException ($"invalid ease value: '{arg}' is not a valid ease."); - } - } -} diff --git a/Terminal.Gui/TextEffects/BaseCharacter.cs b/Terminal.Gui/TextEffects/BaseCharacter.cs deleted file mode 100644 index 886cd6b59..000000000 --- a/Terminal.Gui/TextEffects/BaseCharacter.cs +++ /dev/null @@ -1,109 +0,0 @@ -namespace Terminal.Gui.TextEffects; - -public class EffectCharacter -{ - public int CharacterId { get; } - public string InputSymbol { get; } - public Coord InputCoord { get; } - public bool IsVisible { get; set; } - public Animation Animation { get; } - public Motion Motion { get; } - public EventHandler EventHandler { get; } - public int Layer { get; set; } - public bool IsFillCharacter { get; set; } - - public EffectCharacter (int characterId, string symbol, int inputColumn, int inputRow) - { - CharacterId = characterId; - InputSymbol = symbol; - InputCoord = new Coord (inputColumn, inputRow); - IsVisible = false; - Animation = new Animation (this); - Motion = new Motion (this); - EventHandler = new EventHandler (this); - Layer = 0; - IsFillCharacter = false; - } - - public bool IsActive => !Animation.ActiveSceneIsComplete() || !Motion.MovementIsComplete (); - - public void Tick () - { - Motion.Move (); - Animation.StepAnimation (); - } -} - -public class EventHandler -{ - public EffectCharacter Character { get; } - public Dictionary<(Event, object), List<(Action, object)>> RegisteredEvents { get; } - - public EventHandler (EffectCharacter character) - { - Character = character; - RegisteredEvents = new Dictionary<(Event, object), List<(Action, object)>> (); - } - - public void RegisterEvent (Event @event, object caller, Action action, object target) - { - var key = (@event, caller); - if (!RegisteredEvents.ContainsKey (key)) - RegisteredEvents [key] = new List<(Action, object)> (); - - RegisteredEvents [key].Add ((action, target)); - } - - public void HandleEvent (Event @event, object caller) - { - var key = (@event, caller); - if (!RegisteredEvents.ContainsKey (key)) - return; - - foreach (var (action, target) in RegisteredEvents [key]) - { - switch (action) - { - case Action.ActivatePath: - Character.Motion.ActivatePath (target as Path); - break; - case Action.DeactivatePath: - Character.Motion.DeactivatePath (target as Path); - break; - case Action.SetLayer: - Character.Layer = (int)target; - break; - case Action.SetCoordinate: - Character.Motion.CurrentCoord = (Coord)target; - break; - case Action.Callback: - - // TODO: - throw new NotImplementedException ("TODO, port (target as Action)?.Invoke ()"); - break; - default: - throw new ArgumentOutOfRangeException (nameof (action), "Unhandled action."); - } - } - } - - public enum Event - { - SegmentEntered, - SegmentExited, - PathActivated, - PathComplete, - PathHolding, - SceneActivated, - SceneComplete - } - - public enum Action - { - ActivatePath, - DeactivatePath, - SetLayer, - SetCoordinate, - Callback - } -} diff --git a/Terminal.Gui/TextEffects/BaseEffect.cs b/Terminal.Gui/TextEffects/BaseEffect.cs deleted file mode 100644 index f8a710302..000000000 --- a/Terminal.Gui/TextEffects/BaseEffect.cs +++ /dev/null @@ -1,60 +0,0 @@ -namespace Terminal.Gui.TextEffects; - -public abstract class BaseEffectIterator where T : EffectConfig, new() -{ - protected T Config { get; set; } - protected TerminalA Terminal { get; set; } - protected List ActiveCharacters { get; set; } = new List (); - - protected BaseEffect Effect { get; } - - - - public BaseEffectIterator (BaseEffect effect) - { - Effect = effect; - Config = effect.EffectConfig; - Terminal = new TerminalA (effect.InputData, effect.TerminalConfig); - - } - - public void Update () - { - foreach (var character in ActiveCharacters) - { - character.Tick (); - } - ActiveCharacters.RemoveAll (character => !character.IsActive); - } - -} - -public abstract class BaseEffect where T : EffectConfig, new() -{ - public string InputData { get; set; } - public T EffectConfig { get; set; } - public TerminalConfig TerminalConfig { get; set; } - - protected BaseEffect (string inputData) - { - InputData = inputData; - EffectConfig = new T (); - TerminalConfig = new TerminalConfig (); - } - - /* - public IDisposable TerminalOutput (string endSymbol = "\n") - { - var terminal = new Terminal (InputData, TerminalConfig); - terminal.PrepCanvas (); - try - { - return terminal; - } - finally - { - terminal.RestoreCursor (endSymbol); - } - }*/ -} - diff --git a/Terminal.Gui/TextEffects/Easing.cs b/Terminal.Gui/TextEffects/Easing.cs deleted file mode 100644 index 3b63ca032..000000000 --- a/Terminal.Gui/TextEffects/Easing.cs +++ /dev/null @@ -1,303 +0,0 @@ -namespace Terminal.Gui.TextEffects; -using System; - -public delegate float EasingFunction (float progressRatio); - -public static class Easing -{ - public static float Linear (float progressRatio) - { - return progressRatio; - } - - public static float InSine (float progressRatio) - { - return 1 - (float)Math.Cos ((progressRatio * Math.PI) / 2); - } - - public static float OutSine (float progressRatio) - { - return (float)Math.Sin ((progressRatio * Math.PI) / 2); - } - - public static float InOutSine (float progressRatio) - { - return -(float)(Math.Cos (Math.PI * progressRatio) - 1) / 2; - } - - public static float InQuad (float progressRatio) - { - return progressRatio * progressRatio; - } - - public static float OutQuad (float progressRatio) - { - return 1 - (1 - progressRatio) * (1 - progressRatio); - } - - public static float InOutQuad (float progressRatio) - { - if (progressRatio < 0.5) - { - return 2 * progressRatio * progressRatio; - } - else - { - return 1 - (float)Math.Pow (-2 * progressRatio + 2, 2) / 2; - } - } - - public static float InCubic (float progressRatio) - { - return progressRatio * progressRatio * progressRatio; - } - - public static float OutCubic (float progressRatio) - { - return 1 - (float)Math.Pow (1 - progressRatio, 3); - } - - public static float InOutCubic (float progressRatio) - { - if (progressRatio < 0.5) - { - return 4 * progressRatio * progressRatio * progressRatio; - } - else - { - return 1 - (float)Math.Pow (-2 * progressRatio + 2, 3) / 2; - } - } - - public static float InQuart (float progressRatio) - { - return progressRatio * progressRatio * progressRatio * progressRatio; - } - - public static float OutQuart (float progressRatio) - { - return 1 - (float)Math.Pow (1 - progressRatio, 4); - } - - public static float InOutQuart (float progressRatio) - { - if (progressRatio < 0.5) - { - return 8 * progressRatio * progressRatio * progressRatio * progressRatio; - } - else - { - return 1 - (float)Math.Pow (-2 * progressRatio + 2, 4) / 2; - } - } - - public static float InQuint (float progressRatio) - { - return progressRatio * progressRatio * progressRatio * progressRatio * progressRatio; - } - - public static float OutQuint (float progressRatio) - { - return 1 - (float)Math.Pow (1 - progressRatio, 5); - } - - public static float InOutQuint (float progressRatio) - { - if (progressRatio < 0.5) - { - return 16 * progressRatio * progressRatio * progressRatio * progressRatio * progressRatio; - } - else - { - return 1 - (float)Math.Pow (-2 * progressRatio + 2, 5) / 2; - } - } - - public static float InExpo (float progressRatio) - { - if (progressRatio == 0) - { - return 0; - } - else - { - return (float)Math.Pow (2, 10 * progressRatio - 10); - } - } - - public static float OutExpo (float progressRatio) - { - if (progressRatio == 1) - { - return 1; - } - else - { - return 1 - (float)Math.Pow (2, -10 * progressRatio); - } - } - - public static float InOutExpo (float progressRatio) - { - if (progressRatio == 0) - { - return 0; - } - else if (progressRatio == 1) - { - return 1; - } - else if (progressRatio < 0.5) - { - return (float)Math.Pow (2, 20 * progressRatio - 10) / 2; - } - else - { - return (2 - (float)Math.Pow (2, -20 * progressRatio + 10)) / 2; - } - } - - public static float InCirc (float progressRatio) - { - return 1 - (float)Math.Sqrt (1 - progressRatio * progressRatio); - } - - public static float OutCirc (float progressRatio) - { - return (float)Math.Sqrt (1 - (progressRatio - 1) * (progressRatio - 1)); - } - - public static float InOutCirc (float progressRatio) - { - if (progressRatio < 0.5) - { - return (1 - (float)Math.Sqrt (1 - (2 * progressRatio) * (2 * progressRatio))) / 2; - } - else - { - return ((float)Math.Sqrt (1 - (-2 * progressRatio + 2) * (-2 * progressRatio + 2)) + 1) / 2; - } - } - - public static float InBack (float progressRatio) - { - const float c1 = 1.70158f; - const float c3 = c1 + 1; - return c3 * progressRatio * progressRatio * progressRatio - c1 * progressRatio * progressRatio; - } - - public static float OutBack (float progressRatio) - { - const float c1 = 1.70158f; - const float c3 = c1 + 1; - return 1 + c3 * (progressRatio - 1) * (progressRatio - 1) * (progressRatio - 1) + c1 * (progressRatio - 1) * (progressRatio - 1); - } - - public static float InOutBack (float progressRatio) - { - const float c1 = 1.70158f; - const float c2 = c1 * 1.525f; - if (progressRatio < 0.5) - { - return ((2 * progressRatio) * (2 * progressRatio) * ((c2 + 1) * 2 * progressRatio - c2)) / 2; - } - else - { - return ((2 * progressRatio - 2) * (2 * progressRatio - 2) * ((c2 + 1) * (progressRatio * 2 - 2) + c2) + 2) / 2; - } - } - - public static float InElastic (float progressRatio) - { - const float c4 = (2 * (float)Math.PI) / 3; - if (progressRatio == 0) - { - return 0; - } - else if (progressRatio == 1) - { - return 1; - } - else - { - return -(float)Math.Pow (2, 10 * progressRatio - 10) * (float)Math.Sin ((progressRatio * 10 - 10.75) * c4); - } - } - - public static float OutElastic (float progressRatio) - { - const float c4 = (2 * (float)Math.PI) / 3; - if (progressRatio == 0) - { - return 0; - } - else if (progressRatio == 1) - { - return 1; - } - else - { - return (float)Math.Pow (2, -10 * progressRatio) * (float)Math.Sin ((progressRatio * 10 - 0.75) * c4) + 1; - } - } - - public static float InOutElastic (float progressRatio) - { - const float c5 = (2 * (float)Math.PI) / 4.5f; - if (progressRatio == 0) - { - return 0; - } - else if (progressRatio == 1) - { - return 1; - } - else if (progressRatio < 0.5) - { - return -(float)Math.Pow (2, 20 * progressRatio - 10) * (float)Math.Sin ((20 * progressRatio - 11.125) * c5) / 2; - } - else - { - return ((float)Math.Pow (2, -20 * progressRatio + 10) * (float)Math.Sin ((20 * progressRatio - 11.125) * c5)) / 2 + 1; - } - } - - public static float InBounce (float progressRatio) - { - return 1 - OutBounce (1 - progressRatio); - } - - public static float OutBounce (float progressRatio) - { - const float n1 = 7.5625f; - const float d1 = 2.75f; - if (progressRatio < 1 / d1) - { - return n1 * progressRatio * progressRatio; - } - else if (progressRatio < 2 / d1) - { - return n1 * (progressRatio - 1.5f / d1) * (progressRatio - 1.5f / d1) + 0.75f; - } - else if (progressRatio < 2.5 / d1) - { - return n1 * (progressRatio - 2.25f / d1) * (progressRatio - 2.25f / d1) + 0.9375f; - } - else - { - return n1 * (progressRatio - 2.625f / d1) * (progressRatio - 2.625f / d1) + 0.984375f; - } - } - - public static float InOutBounce (float progressRatio) - { - if (progressRatio < 0.5) - { - return (1 - OutBounce (1 - 2 * progressRatio)) / 2; - } - else - { - return (1 + OutBounce (2 * progressRatio - 1)) / 2; - } - } -} diff --git a/Terminal.Gui/TextEffects/EffectTemplate.cs b/Terminal.Gui/TextEffects/EffectTemplate.cs deleted file mode 100644 index 4b0fddcb7..000000000 --- a/Terminal.Gui/TextEffects/EffectTemplate.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Terminal.Gui.TextEffects; - -public class EffectConfig -{ - public Color ColorSingle { get; set; } - public List ColorList { get; set; } - public Color FinalColor { get; set; } - public List FinalGradientStops { get; set; } - public List FinalGradientSteps { get; set; } - public int FinalGradientFrames { get; set; } - public float MovementSpeed { get; set; } - public EasingFunction Easing { get; set; } -} diff --git a/Terminal.Gui/TextEffects/Effects/Beams.cs b/Terminal.Gui/TextEffects/Effects/Beams.cs deleted file mode 100644 index b39ef9a91..000000000 --- a/Terminal.Gui/TextEffects/Effects/Beams.cs +++ /dev/null @@ -1,241 +0,0 @@ -/*namespace Terminal.Gui.TextEffects.Effects; - -public class BeamsConfig : EffectConfig -{ - public string [] BeamRowSymbols { get; set; } = { "▂", "▁", "_" }; - public string [] BeamColumnSymbols { get; set; } = { "▌", "▍", "▎", "▏" }; - public int BeamDelay { get; set; } = 10; - public (int, int) BeamRowSpeedRange { get; set; } = (10, 40); - public (int, int) BeamColumnSpeedRange { get; set; } = (6, 10); - public Color [] BeamGradientStops { get; set; } = { new Color ("ffffff"), new Color ("00D1FF"), new Color ("8A008A") }; - public int [] BeamGradientSteps { get; set; } = { 2, 8 }; - public int BeamGradientFrames { get; set; } = 2; - public Color [] FinalGradientStops { get; set; } = { new Color ("8A008A"), new Color ("00D1FF"), new Color ("ffffff") }; - public int [] FinalGradientSteps { get; set; } = { 12 }; - public int FinalGradientFrames { get; set; } = 5; - public GradientDirection FinalGradientDirection { get; set; } = GradientDirection.Vertical; - public int FinalWipeSpeed { get; set; } = 1; -} - -public class Beams : BaseEffect -{ - public Beams (string inputData) : base (inputData) - { - } - - protected override BaseEffectIterator CreateIterator () - { - return new BeamsIterator (this); - } -} - - -public class BeamsIterator : BaseEffectIterator -{ - private class Group - { - public List Characters { get; private set; } - public string Direction { get; private set; } - private Terminal Terminal; - private BeamsConfig Config; - private double Speed; - private float NextCharacterCounter; - private List SortedCharacters; - - public Group (List characters, string direction, Terminal terminal, BeamsConfig config) - { - Characters = characters; - Direction = direction; - Terminal = terminal; - Config = config; - Speed = new Random ().Next (config.BeamRowSpeedRange.Item1, config.BeamRowSpeedRange.Item2) * 0.1; - NextCharacterCounter = 0; - SortedCharacters = direction == "row" - ? characters.OrderBy (c => c.InputCoord.Column).ToList () - : characters.OrderBy (c => c.InputCoord.Row).ToList (); - - if (new Random ().Next (0, 2) == 0) - { - SortedCharacters.Reverse (); - } - } - - public void IncrementNextCharacterCounter () - { - NextCharacterCounter += (float)Speed; - } - - public EffectCharacter GetNextCharacter () - { - NextCharacterCounter -= 1; - var nextCharacter = SortedCharacters.First (); - SortedCharacters.RemoveAt (0); - if (nextCharacter.Animation.ActiveScene != null) - { - nextCharacter.Animation.ActiveScene.ResetScene (); - return null; - } - - Terminal.SetCharacterVisibility (nextCharacter, true); - nextCharacter.Animation.ActivateScene (nextCharacter.Animation.QueryScene ("beam_" + Direction)); - return nextCharacter; - } - - public bool Complete () - { - return !SortedCharacters.Any (); - } - } - - private List PendingGroups = new List (); - private Dictionary CharacterFinalColorMap = new Dictionary (); - private List ActiveGroups = new List (); - private int Delay = 0; - private string Phase = "beams"; - private List> FinalWipeGroups; - - public BeamsIterator (Beams effect) : base (effect) - { - Build (); - } - - private void Build () - { - var finalGradient = new Gradient (Effect.Config.FinalGradientStops, Effect.Config.FinalGradientSteps); - var finalGradientMapping = finalGradient.BuildCoordinateColorMapping ( - Effect.Terminal.Canvas.Top, - Effect.Terminal.Canvas.Right, - Effect.Config.FinalGradientDirection - ); - - foreach (var character in Effect.Terminal.GetCharacters (fillChars: true)) - { - CharacterFinalColorMap [character] = finalGradientMapping [character.InputCoord]; - } - - var beamGradient = new Gradient (Effect.Config.BeamGradientStops, Effect.Config.BeamGradientSteps); - var groups = new List (); - - foreach (var row in Effect.Terminal.GetCharactersGrouped (Terminal.CharacterGroup.RowTopToBottom, fillChars: true)) - { - groups.Add (new Group (row, "row", Effect.Terminal, Effect.Config)); - } - - foreach (var column in Effect.Terminal.GetCharactersGrouped (Terminal.CharacterGroup.ColumnLeftToRight, fillChars: true)) - { - groups.Add (new Group (column, "column", Effect.Terminal, Effect.Config)); - } - - foreach (var group in groups) - { - foreach (var character in group.Characters) - { - var beamRowScene = character.Animation.NewScene (id: "beam_row"); - var beamColumnScene = character.Animation.NewScene (id: "beam_column"); - beamRowScene.ApplyGradientToSymbols ( - beamGradient, Effect.Config.BeamRowSymbols, Effect.Config.BeamGradientFrames); - beamColumnScene.ApplyGradientToSymbols ( - beamGradient, Effect.Config.BeamColumnSymbols, Effect.Config.BeamGradientFrames); - - var fadedColor = character.Animation.AdjustColorBrightness (CharacterFinalColorMap [character], 0.3f); - var fadeGradient = new Gradient (CharacterFinalColorMap [character], fadedColor, steps: 10); - beamRowScene.ApplyGradientToSymbols (fadeGradient, character.InputSymbol, 5); - beamColumnScene.ApplyGradientToSymbols (fadeGradient, character.InputSymbol, 5); - - var brightenGradient = new Gradient (fadedColor, CharacterFinalColorMap [character], steps: 10); - var brightenScene = character.Animation.NewScene (id: "brighten"); - brightenScene.ApplyGradientToSymbols ( - brightenGradient, character.InputSymbol, Effect.Config.FinalGradientFrames); - } - } - - PendingGroups = groups; - new Random ().Shuffle (PendingGroups); - } - - public override bool MoveNext () - { - if (Phase != "complete" || ActiveCharacters.Any ()) - { - if (Phase == "beams") - { - if (Delay == 0) - { - if (PendingGroups.Any ()) - { - for (int i = 0; i < new Random ().Next (1, 6); i++) - { - if (PendingGroups.Any ()) - { - ActiveGroups.Add (PendingGroups.First ()); - PendingGroups.RemoveAt (0); - } - } - } - Delay = Effect.Config.BeamDelay; - } - else - { - Delay--; - } - - foreach (var group in ActiveGroups) - { - group.IncrementNextCharacterCounter (); - if ((int)group.NextCharacterCounter > 1) - { - for (int i = 0; i < (int)group.NextCharacterCounter; i++) - { - if (!group.Complete ()) - { - var nextChar = group.GetNextCharacter (); - if (nextChar != null) - { - ActiveCharacters.Add (nextChar); - } - } - } - } - } - - ActiveGroups = ActiveGroups.Where (g => !g.Complete ()).ToList (); - if (!PendingGroups.Any () && !ActiveGroups.Any () && !ActiveCharacters.Any ()) - { - Phase = "final_wipe"; - } - } - else if (Phase == "final_wipe") - { - if (FinalWipeGroups.Any ()) - { - for (int i = 0; i < Effect.Config.FinalWipeSpeed; i++) - { - if (!FinalWipeGroups.Any ()) break; - - var nextGroup = FinalWipeGroups.First (); - FinalWipeGroups.RemoveAt (0); - - foreach (var character in nextGroup) - { - character.Animation.ActivateScene (character.Animation.QueryScene ("brighten")); - Effect.Terminal.SetCharacterVisibility (character, true); - ActiveCharacters.Add (character); - } - } - } - else - { - Phase = "complete"; - } - } - - Update (); - return true; - } - else - { - return false; - } - } -} -*/ \ No newline at end of file diff --git a/Terminal.Gui/TextEffects/Geometry.cs b/Terminal.Gui/TextEffects/Geometry.cs deleted file mode 100644 index 5de2f7073..000000000 --- a/Terminal.Gui/TextEffects/Geometry.cs +++ /dev/null @@ -1,137 +0,0 @@ -namespace Terminal.Gui.TextEffects; - - -public static class GeometryUtils -{ - - public static List FindCoordsOnCircle (Coord origin, int radius, int coordsLimit = 0, bool unique = true) - { - var points = new List (); - var seenPoints = new HashSet (); - if (coordsLimit == 0) - coordsLimit = (int)Math.Ceiling (2 * Math.PI * radius); - double angleStep = 2 * Math.PI / coordsLimit; - - for (int i = 0; i < coordsLimit; i++) - { - double angle = i * angleStep; - int x = (int)(origin.Column + radius * Math.Cos (angle)); - int y = (int)(origin.Row + radius * Math.Sin (angle)); - var coord = new Coord (x, y); - - if (unique && !seenPoints.Contains (coord)) - { - points.Add (coord); - seenPoints.Add (coord); - } - else if (!unique) - { - points.Add (coord); - } - } - - return points; - } - - public static List FindCoordsInCircle (Coord center, int diameter) - { - var coordsInEllipse = new List (); - int radius = diameter / 2; - for (int x = center.Column - radius; x <= center.Column + radius; x++) - { - for (int y = center.Row - radius; y <= center.Row + radius; y++) - { - if (Math.Pow (x - center.Column, 2) + Math.Pow (y - center.Row, 2) <= Math.Pow (radius, 2)) - coordsInEllipse.Add (new Coord (x, y)); - } - } - return coordsInEllipse; - } - - public static List FindCoordsInRect (Coord origin, int distance) - { - var coords = new List (); - for (int column = origin.Column - distance; column <= origin.Column + distance; column++) - { - for (int row = origin.Row - distance; row <= origin.Row + distance; row++) - { - coords.Add (new Coord (column, row)); - } - } - return coords; - } - - public static Coord FindCoordAtDistance (Coord origin, Coord target, double distance) - { - double totalDistance = FindLengthOfLine (origin, target) + distance; - double t = distance / totalDistance; - int nextColumn = (int)((1 - t) * origin.Column + t * target.Column); - int nextRow = (int)((1 - t) * origin.Row + t * target.Row); - return new Coord (nextColumn, nextRow); - } - - public static Coord FindCoordOnBezierCurve (Coord start, List controlPoints, Coord end, double t) - { - // Implementing De Casteljau's algorithm for Bezier curve - if (controlPoints.Count == 1) // Quadratic - { - double x = Math.Pow (1 - t, 2) * start.Column + - 2 * (1 - t) * t * controlPoints [0].Column + - Math.Pow (t, 2) * end.Column; - double y = Math.Pow (1 - t, 2) * start.Row + - 2 * (1 - t) * t * controlPoints [0].Row + - Math.Pow (t, 2) * end.Row; - return new Coord ((int)x, (int)y); - } - else if (controlPoints.Count == 2) // Cubic - { - double x = Math.Pow (1 - t, 3) * start.Column + - 3 * Math.Pow (1 - t, 2) * t * controlPoints [0].Column + - 3 * (1 - t) * Math.Pow (t, 2) * controlPoints [1].Column + - Math.Pow (t, 3) * end.Column; - double y = Math.Pow (1 - t, 3) * start.Row + - 3 * Math.Pow (1 - t, 2) * t * controlPoints [0].Row + - 3 * (1 - t) * Math.Pow (t, 2) * controlPoints [1].Row + - Math.Pow (t, 3) * end.Row; - return new Coord ((int)x, (int)y); - } - throw new ArgumentException ("Invalid number of control points for bezier curve"); - } - - public static Coord FindCoordOnLine (Coord start, Coord end, double t) - { - int x = (int)((1 - t) * start.Column + t * end.Column); - int y = (int)((1 - t) * start.Row + t * end.Row); - return new Coord (x, y); - } - - public static double FindLengthOfBezierCurve (Coord start, List controlPoints, Coord end) - { - double length = 0.0; - Coord prevCoord = start; - for (int i = 1; i <= 10; i++) - { - double t = i / 10.0; - Coord coord = FindCoordOnBezierCurve (start, controlPoints, end, t); - length += FindLengthOfLine (prevCoord, coord); - prevCoord = coord; - } - return length; - } - - public static double FindLengthOfLine (Coord coord1, Coord coord2) - { - return Math.Sqrt (Math.Pow (coord2.Column - coord1.Column, 2) + - Math.Pow (coord2.Row - coord1.Row, 2)); - } - - public static double FindNormalizedDistanceFromCenter (int maxRow, int maxColumn, Coord otherCoord) - { - double center_x = maxColumn / 2.0; - double center_y = maxRow / 2.0; - double maxDistance = Math.Sqrt (Math.Pow (maxColumn, 2) + Math.Pow (maxRow, 2)); - double distance = Math.Sqrt (Math.Pow (otherCoord.Column - center_x, 2) + - Math.Pow (otherCoord.Row - center_y, 2)); - return distance / (maxDistance / 2); - } -} \ No newline at end of file diff --git a/Terminal.Gui/TextEffects/HexTerm.cs b/Terminal.Gui/TextEffects/HexTerm.cs deleted file mode 100644 index b3b96c4d3..000000000 --- a/Terminal.Gui/TextEffects/HexTerm.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System.Text.RegularExpressions; - -namespace Terminal.Gui.TextEffects; -using System; -using System.Collections.Generic; -using System.Linq; - -public static class ColorUtils -{ - private static readonly Dictionary xtermToHexMap = new Dictionary - { - {0, "#000000"}, {1, "#800000"}, {2, "#008000"}, {3, "#808000"}, {4, "#000080"}, {5, "#800080"}, {6, "#008080"}, {7, "#c0c0c0"}, - {8, "#808080"}, {9, "#ff0000"}, {10, "#00ff00"}, {11, "#ffff00"}, {12, "#0000ff"}, {13, "#ff00ff"}, {14, "#00ffff"}, {15, "#ffffff"}, - {16, "#000000"}, {17, "#00005f"}, {18, "#000087"}, {19, "#0000af"}, {20, "#0000d7"}, {21, "#0000ff"}, {22, "#005f00"}, {23, "#005f5f"}, - {24, "#005f87"}, {25, "#005faf"}, {26, "#005fd7"}, {27, "#005fff"}, {28, "#008700"}, {29, "#00875f"}, {30, "#008787"}, {31, "#0087af"}, - {32, "#0087d7"}, {33, "#0087ff"}, {34, "#00af00"}, {35, "#00af5f"}, {36, "#00af87"}, {37, "#00afaf"}, {38, "#00afd7"}, {39, "#00afff"}, - {40, "#00d700"}, {41, "#00d75f"}, {42, "#00d787"}, {43, "#00d7af"}, {44, "#00d7d7"}, {45, "#00d7ff"}, {46, "#00ff00"}, {47, "#00ff5f"}, - {48, "#00ff87"}, {49, "#00ffaf"}, {50, "#00ffd7"}, {51, "#00ffff"}, {52, "#5f0000"}, {53, "#5f005f"}, {54, "#5f0087"}, {55, "#5f00af"}, - {56, "#5f00d7"}, {57, "#5f00ff"}, {58, "#5f5f00"}, {59, "#5f5f5f"}, {60, "#5f5f87"}, {61, "#5f5faf"}, {62, "#5f5fd7"}, {63, "#5f5fff"}, - {64, "#5f8700"}, {65, "#5f875f"}, {66, "#5f8787"}, {67, "#5f87af"}, {68, "#5f87d7"}, {69, "#5f87ff"}, {70, "#5faf00"}, {71, "#5faf5f"}, - {72, "#5faf87"}, {73, "#5fafaf"}, {74, "#5fafd7"}, {75, "#5fafff"}, {76, "#5fd700"}, {77, "#5fd75f"}, {78, "#5fd787"}, {79, "#5fd7af"}, - {80, "#5fd7d7"}, {81, "#5fd7ff"}, {82, "#5fff00"}, {83, "#5fff5f"}, {84, "#5fff87"}, {85, "#5fffaf"}, {86, "#5fffd7"}, {87, "#5fffff"}, - {88, "#870000"}, {89, "#87005f"}, {90, "#870087"}, {91, "#8700af"}, {92, "#8700d7"}, {93, "#8700ff"}, {94, "#875f00"}, {95, "#875f5f"}, - {96, "#875f87"}, {97, "#875faf"}, {98, "#875fd7"}, {99, "#875fff"}, {100, "#878700"}, {101, "#87875f"}, {102, "#878787"}, {103, "#8787af"}, - {104, "#8787d7"}, {105, "#8787ff"}, {106, "#87af00"}, {107, "#87af5f"}, {108, "#87af87"}, {109, "#87afaf"}, {110, "#87afd7"}, {111, "#87afff"}, - {112, "#87d700"}, {113, "#87d75f"}, {114, "#87d787"}, {115, "#87d7af"}, {116, "#87d7d7"}, {117, "#87d7ff"}, {118, "#87ff00"}, {119, "#87ff5f"}, - {120, "#87ff87"}, {121, "#87ffaf"}, {122, "#87ffd7"}, {123, "#87ffff"}, {124, "#af0000"}, {125, "#af005f"}, {126, "#af0087"}, {127, "#af00af"}, - {128, "#af00d7"}, {129, "#af00ff"}, {130, "#af5f00"}, {131, "#af5f5f"}, {132, "#af5f87"}, {133, "#af5faf"}, {134, "#af5fd7"}, {135, "#af5fff"}, - {136, "#af8700"}, {137, "#af875f"}, {138, "#af8787"}, {139, "#af87af"}, {140, "#af87d7"}, {141, "#af87ff"}, {142, "#afaf00"}, {143, "#afaf5f"}, - {144, "#afaf87"}, {145, "#afafaf"}, {146, "#afafd7"}, {147, "#afafff"}, {148, "#afd700"}, {149, "#afd75f"}, {150, "#afd787"}, {151, "#afd7af"}, - {152, "#afd7d7"}, {153, "#afd7ff"}, {154, "#afff00"}, {155, "#afff5f"}, {156, "#afff87"}, {157, "#afffaf"}, {158, "#afffd7"}, {159, "#afffff"}, - {160, "#d70000"}, {161, "#d7005f"}, {162, "#d70087"}, {163, "#d700af"}, {164, "#d700d7"}, {165, "#d700ff"}, {166, "#d75f00"}, {167, "#d75f5f"}, - {168, "#d75f87"}, {169, "#d75faf"}, {170, "#d75fd7"}, {171, "#d75fff"}, {172, "#d78700"}, {173, "#d7875f"}, {174, "#d78787"}, {175, "#d787af"}, - {176, "#d787d7"}, {177, "#d787ff"}, {178, "#d7af00"}, {179, "#d7af5f"}, {180, "#d7af87"}, {181, "#d7afaf"}, {182, "#d7afd7"}, {183, "#d7afff"}, - {184, "#d7d700"}, {185, "#d7d75f"}, {186, "#d7d787"}, {187, "#d7d7af"}, {188, "#d7d7d7"}, {189, "#d7d7ff"}, {190, "#d7ff00"}, {191, "#d7ff5f"}, - {192, "#d7ff87"}, {193, "#d7ffaf"}, {194, "#d7ffd7"}, {195, "#d7ffff"}, {196, "#ff0000"}, {197, "#ff005f"}, {198, "#ff0087"}, {199, "#ff00af"}, - {200, "#ff00d7"}, {201, "#ff00ff"}, {202, "#ff5f00"}, {203, "#ff5f5f"}, {204, "#ff5f87"}, {205, "#ff5faf"}, {206, "#ff5fd7"}, {207, "#ff5fff"}, - {208, "#ff8700"}, {209, "#ff875f"}, {210, "#ff8787"}, {211, "#ff87af"}, {212, "#ff87d7"}, {213, "#ff87ff"}, {214, "#ffaf00"}, {215, "#ffaf5f"}, - {216, "#ffaf87"}, {217, "#ffafaf"}, {218, "#ffafd7"}, {219, "#ffafff"}, {220, "#ffd700"}, {221, "#ffd75f"}, {222, "#ffd787"}, {223, "#ffd7af"}, - {224, "#ffd7d7"}, {225, "#ffd7ff"}, {226, "#ffff00"}, {227, "#ffff5f"}, {228, "#ffff87"}, {229, "#ffffaf"}, {230, "#ffffd7"}, {231, "#ffffff"}, - {232, "#080808"}, {233, "#121212"}, {234, "#1c1c1c"}, {235, "#262626"}, {236, "#303030"}, {237, "#3a3a3a"}, {238, "#444444"}, {239, "#4e4e4e"}, - {240, "#585858"}, {241, "#626262"}, {242, "#6c6c6c"}, {243, "#767676"}, {244, "#808080"}, {245, "#8a8a8a"}, {246, "#949494"}, {247, "#9e9e9e"}, - {248, "#a8a8a8"}, {249, "#b2b2b2"}, {250, "#bcbcbc"}, {251, "#c6c6c6"}, {252, "#d0d0d0"}, {253, "#dadada"}, {254, "#e4e4e4"}, {255, "#eeeeee"} - }; - - private static readonly Dictionary xtermToRgbMap = xtermToHexMap.ToDictionary ( - item => item.Key, - item => ( - R: Convert.ToInt32 (item.Value.Substring (1, 2), 16), - G: Convert.ToInt32 (item.Value.Substring (3, 2), 16), - B: Convert.ToInt32 (item.Value.Substring (5, 2), 16) - )); - private static readonly Regex hexColorRegex = new Regex ("^#?[0-9A-Fa-f]{6}$"); - - public static bool IsValidHexColor (string hexColor) - { - return hexColorRegex.IsMatch (hexColor); - } - - public static bool IsValidXtermColor (int xtermColor) - { - return xtermColor >= 0 && xtermColor <= 255; - } - - public static string XtermToHex (int xtermColor) - { - if (xtermToHexMap.TryGetValue (xtermColor, out string hex)) - { - return hex; - } - throw new ArgumentException ($"Invalid XTerm-256 color code: {xtermColor}"); - } - - public static int HexToXterm (string hexColor) - { - if (!IsValidHexColor (hexColor)) - throw new ArgumentException ("Invalid RGB hex color format."); - - hexColor = hexColor.StartsWith ("#") ? hexColor.Substring (1) : hexColor; - var rgb = ( - R: Convert.ToInt32 (hexColor.Substring (0, 2), 16), - G: Convert.ToInt32 (hexColor.Substring (2, 2), 16), - B: Convert.ToInt32 (hexColor.Substring (4, 2), 16) - ); - - return xtermToRgbMap.Aggregate ((current, next) => - ColorDifference (current.Value, rgb) < ColorDifference (next.Value, rgb) ? current : next).Key; - } - - private static double ColorDifference ((int R, int G, int B) c1, (int R, int G, int B) c2) - { - return Math.Sqrt (Math.Pow (c1.R - c2.R, 2) + Math.Pow (c1.G - c2.G, 2) + Math.Pow (c1.B - c2.B, 2)); - } -} diff --git a/Terminal.Gui/TextEffects/Motion.cs b/Terminal.Gui/TextEffects/Motion.cs deleted file mode 100644 index 9ed544f5e..000000000 --- a/Terminal.Gui/TextEffects/Motion.cs +++ /dev/null @@ -1,253 +0,0 @@ -namespace Terminal.Gui.TextEffects; -public class Coord -{ - public int Column { get; set; } - public int Row { get; set; } - - public Coord (int column, int row) - { - Column = column; - Row = row; - } - - public override string ToString () => $"({Column}, {Row})"; - - public override bool Equals (object obj) - { - if (obj is Coord other) - { - return Column == other.Column && Row == other.Row; - } - return false; - } - - public override int GetHashCode () - { - return HashCode.Combine (Column, Row); - } - - public static bool operator == (Coord left, Coord right) - { - if (left is null) - { - return right is null; - } - return left.Equals (right); - } - - public static bool operator != (Coord left, Coord right) - { - return !(left == right); - } -} - -public class Waypoint -{ - public string WaypointId { get; set; } - public Coord Coord { get; set; } - public List BezierControl { get; set; } - - public Waypoint (string waypointId, Coord coord, List bezierControl = null) - { - WaypointId = waypointId; - Coord = coord; - BezierControl = bezierControl ?? new List (); - } -} - -public class Segment -{ - public Waypoint Start { get; private set; } - public Waypoint End { get; private set; } - public double Distance { get; private set; } - public bool EnterEventTriggered { get; set; } - public bool ExitEventTriggered { get; set; } - - public Segment (Waypoint start, Waypoint end) - { - Start = start; - End = end; - Distance = CalculateDistance (start, end); - } - - private double CalculateDistance (Waypoint start, Waypoint end) - { - // Add bezier control point distance calculation if needed - return Math.Sqrt (Math.Pow (end.Coord.Column - start.Coord.Column, 2) + Math.Pow (end.Coord.Row - start.Coord.Row, 2)); - } - - public Coord GetCoordOnSegment (double distanceFactor) - { - int column = (int)(Start.Coord.Column + (End.Coord.Column - Start.Coord.Column) * distanceFactor); - int row = (int)(Start.Coord.Row + (End.Coord.Row - Start.Coord.Row) * distanceFactor); - return new Coord (column, row); - } -} -public class Path -{ - public string PathId { get; private set; } - public double Speed { get; set; } - public Func EaseFunction { get; set; } - public int Layer { get; set; } - public int HoldTime { get; set; } - public bool Loop { get; set; } - public List Segments { get; private set; } = new List (); - public int CurrentStep { get; set; } - public double TotalDistance { get; set; } - public double LastDistanceReached { get; set; } - public int MaxSteps => (int)Math.Ceiling (TotalDistance / Speed); // Calculates max steps based on total distance and speed - - public Path (string pathId, double speed, Func easeFunction = null, int layer = 0, int holdTime = 0, bool loop = false) - { - PathId = pathId; - Speed = speed; - EaseFunction = easeFunction; - Layer = layer; - HoldTime = holdTime; - Loop = loop; - } - - public void AddWaypoint (Waypoint waypoint) - { - if (Segments.Count > 0) - { - var lastSegment = Segments.Last (); - var newSegment = new Segment (lastSegment.End, waypoint); - Segments.Add (newSegment); - TotalDistance += newSegment.Distance; - } - else - { - var originWaypoint = new Waypoint ("origin", new Coord (0, 0)); // Assuming the path starts at origin - var initialSegment = new Segment (originWaypoint, waypoint); - Segments.Add (initialSegment); - TotalDistance = initialSegment.Distance; - } - } - - public Coord Step () - { - if (CurrentStep <= MaxSteps) - { - double progress = EaseFunction?.Invoke ((double)CurrentStep / TotalDistance) ?? (double)CurrentStep / TotalDistance; - double distanceTravelled = TotalDistance * progress; - LastDistanceReached = distanceTravelled; - - foreach (var segment in Segments) - { - if (distanceTravelled <= segment.Distance) - { - double segmentProgress = distanceTravelled / segment.Distance; - return segment.GetCoordOnSegment (segmentProgress); - } - - distanceTravelled -= segment.Distance; - } - } - - return Segments.Last ().End.Coord; // Return the end of the last segment if out of bounds - } -} - -public class Motion -{ - public Dictionary Paths { get; private set; } = new Dictionary (); - public Path ActivePath { get; private set; } - public Coord CurrentCoord { get; set; } - public Coord PreviousCoord { get; set; } - public EffectCharacter Character { get; private set; } // Assuming EffectCharacter is similar to base_character.EffectCharacter - - public Motion (EffectCharacter character) - { - Character = character; - CurrentCoord = new Coord (character.InputCoord.Column, character.InputCoord.Row); // Assuming similar properties - PreviousCoord = new Coord (-1, -1); - } - - public void SetCoordinate (Coord coord) - { - CurrentCoord = coord; - } - - public Path CreatePath (string pathId, double speed, Func easeFunction = null, int layer = 0, int holdTime = 0, bool loop = false) - { - if (Paths.ContainsKey (pathId)) - throw new ArgumentException ($"A path with ID {pathId} already exists."); - - var path = new Path (pathId, speed, easeFunction, layer, holdTime, loop); - Paths [pathId] = path; - return path; - } - - public Path QueryPath (string pathId) - { - if (!Paths.TryGetValue (pathId, out var path)) - throw new KeyNotFoundException ($"No path found with ID {pathId}."); - - return path; - } - - public bool MovementIsComplete () - { - return ActivePath == null || ActivePath.CurrentStep >= ActivePath.TotalDistance; - } - - public void ActivatePath (Path path) - { - if (path == null) - throw new ArgumentNullException (nameof (path), "Path cannot be null when activating."); - - ActivePath = path; - ActivePath.CurrentStep = 0; // Reset the path's progress - } - - /// - /// Set the active path to None if the active path is the given path. - /// - public void DeactivatePath (Path p) - { - if (p == ActivePath) - { - ActivePath = null; - } - } - public void DeactivatePath () - { - ActivePath = null; - } - - public void Move () - { - if (ActivePath != null) - { - PreviousCoord = CurrentCoord; - CurrentCoord = ActivePath.Step (); - ActivePath.CurrentStep++; - - if (ActivePath.CurrentStep >= ActivePath.TotalDistance) - { - if (ActivePath.Loop) - ActivePath.CurrentStep = 0; // Reset the path for looping - else - DeactivatePath (); // Deactivate the path if it is not set to loop - } - } - } - - public void ChainPaths (IEnumerable paths, bool loop = false) - { - var pathList = paths.ToList (); - for (int i = 0; i < pathList.Count; i++) - { - var currentPath = pathList [i]; - var nextPath = i + 1 < pathList.Count ? pathList [i + 1] : pathList.FirstOrDefault (); - - // Here we could define an event system to trigger path activation when another completes - // For example, you could listen for a "path complete" event and then activate the next path - if (loop && nextPath != null) - { - // Implementation depends on your event system - } - } - } -} diff --git a/Terminal.Gui/TextEffects/New/GradientFill.cs b/Terminal.Gui/TextEffects/New/GradientFill.cs deleted file mode 100644 index a6d69f4e3..000000000 --- a/Terminal.Gui/TextEffects/New/GradientFill.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Terminal.Gui.TextEffects; - -/// -/// Implementation of that uses a color gradient (including -/// radial, diagonal etc). -/// -public class GradientFill : IFill -{ - private Dictionary _map; - - public GradientFill (Rectangle area, Gradient gradient, Gradient.Direction direction) - { - _map = - gradient.BuildCoordinateColorMapping (area.Height, area.Width, direction) - .ToDictionary ( - (k) => new Point (k.Key.Column, k.Key.Row), - (v) => new Terminal.Gui.Color (v.Value.R, v.Value.G, v.Value.B)); - } - - public Terminal.Gui.Color GetColor (Point point) - { - if (_map.TryGetValue (point, out var color)) - { - return color; - } - return new Terminal.Gui.Color (0, 0, 0); // Default to black if point not found - } -} \ No newline at end of file diff --git a/Terminal.Gui/TextEffects/Terminal.cs b/Terminal.Gui/TextEffects/Terminal.cs deleted file mode 100644 index 08522069d..000000000 --- a/Terminal.Gui/TextEffects/Terminal.cs +++ /dev/null @@ -1,106 +0,0 @@ -namespace Terminal.Gui.TextEffects; -public class TerminalConfig -{ - public int TabWidth { get; set; } = 4; - public bool XtermColors { get; set; } = false; - public bool NoColor { get; set; } = false; - public bool WrapText { get; set; } = false; - public float FrameRate { get; set; } = 100.0f; - public int CanvasWidth { get; set; } = -1; - public int CanvasHeight { get; set; } = -1; - public string AnchorCanvas { get; set; } = "sw"; - public string AnchorText { get; set; } = "sw"; - public bool IgnoreTerminalDimensions { get; set; } = false; -} - -public class Canvas -{ - public int Top { get; private set; } - public int Right { get; private set; } - public int Bottom { get; private set; } = 1; - public int Left { get; private set; } = 1; - - public int CenterRow => (Top + Bottom) / 2; - public int CenterColumn => (Right + Left) / 2; - public Coord Center => new Coord (CenterColumn, CenterRow); - public int Width => Right - Left + 1; - public int Height => Top - Bottom + 1; - - public Canvas (int top, int right) - { - Top = top; - Right = right; - } - - public bool IsCoordInCanvas (Coord coord) - { - return coord.Column >= Left && coord.Column <= Right && - coord.Row >= Bottom && coord.Row <= Top; - } - - public Coord GetRandomCoord (bool outsideScope = false) - { - var random = new Random (); - if (outsideScope) - { - switch (random.Next (4)) - { - case 0: return new Coord (random.Next (Left, Right + 1), Top + 1); - case 1: return new Coord (random.Next (Left, Right + 1), Bottom - 1); - case 2: return new Coord (Left - 1, random.Next (Bottom, Top + 1)); - case 3: return new Coord (Right + 1, random.Next (Bottom, Top + 1)); - } - } - return new Coord ( - random.Next (Left, Right + 1), - random.Next (Bottom, Top + 1)); - } -} - -public class TerminalA -{ - public TerminalConfig Config { get; } - public Canvas Canvas { get; private set; } - private Dictionary CharacterByInputCoord = new Dictionary (); - - public TerminalA (string input, TerminalConfig config = null) - { - Config = config ?? new TerminalConfig (); - var dimensions = GetTerminalDimensions (); - Canvas = new Canvas (dimensions.height, dimensions.width); - ProcessInput (input); - } - - private void ProcessInput (string input) - { - // Handling input processing logic similar to Python's version - } - - public string GetPipedInput () - { - // C# way to get piped input or indicate there's none - return Console.IsInputRedirected ? Console.In.ReadToEnd () : string.Empty; - } - - public void Print (string output, bool enforceFrameRate = true) - { - if (enforceFrameRate) - EnforceFrameRate (); - - // Move cursor to top and clear the current console line - Console.SetCursorPosition (0, 0); - Console.Write (output); - Console.ResetColor (); - } - - private void EnforceFrameRate () - { - // Limit the printing speed based on the Config.FrameRate - } - - private (int width, int height) GetTerminalDimensions () - { - // Return terminal dimensions or defaults if not determinable - return (Console.WindowWidth, Console.WindowHeight); - } -} diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index 2df1131e9..d8f20bac1 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -3,10 +3,8 @@ using System.Collections.Generic; using System.Text; using System.Threading; using Terminal.Gui; -using Terminal.Gui.TextEffects; -using Color = Terminal.Gui.TextEffects.Color; -using Animation = Terminal.Gui.TextEffects.Animation; +using Terminal.Gui.Drawing; namespace UICatalog.Scenarios; @@ -68,18 +66,8 @@ public class TextEffectsScenario : Scenario }, DisplayText = "Gradients" }; - var t2 = new Tab () - { - View = new BallsView () - { - Width = Dim.Fill (), - Height = Dim.Fill (), - }, - DisplayText = "Ball" - }; tabView.AddTab (t1,false); - tabView.AddTab (t2,false); w.Add (tabView); @@ -111,11 +99,11 @@ public class TextEffectsScenario : Scenario // Define the colors of the gradient stops with more appealing colors stops = new List { - Color.FromRgb(0, 128, 255), // Bright Blue - Color.FromRgb(0, 255, 128), // Bright Green - Color.FromRgb(255, 255, 0), // Bright Yellow - Color.FromRgb(255, 128, 0), // Bright Orange - Color.FromRgb(255, 0, 128) // Bright Pink + new Color(0, 128, 255), // Bright Blue + new Color(0, 255, 128), // Bright Green + new Color(255, 255, 0), // Bright Yellow + new Color(255, 128, 0), // Bright Orange + new Color(255, 0, 128) // Bright Pink }; // Define the number of steps between each color for smoother transitions @@ -159,9 +147,9 @@ internal class GradientsView : View // Define the colors of the gradient stops var stops = new List { - Color.FromRgb(255, 0, 0), // Red - Color.FromRgb(0, 255, 0), // Green - Color.FromRgb(238, 130, 238) // Violet + new Color(255, 0, 0), // Red + new Color(0, 255, 0), // Green + new Color(238, 130, 238) // Violet }; // Define the number of steps between each color @@ -182,7 +170,7 @@ internal class GradientsView : View { for (int col = 0; col <= maxColumn; col++) { - var coord = new Coord (col, row); + var coord = new Point (col, row); var color = gradientMapping [coord]; SetColor (color); @@ -197,13 +185,13 @@ internal class GradientsView : View // Define the colors of the rainbow var stops = new List { - Color.FromRgb(255, 0, 0), // Red - Color.FromRgb(255, 165, 0), // Orange - Color.FromRgb(255, 255, 0), // Yellow - Color.FromRgb(0, 128, 0), // Green - Color.FromRgb(0, 0, 255), // Blue - Color.FromRgb(75, 0, 130), // Indigo - Color.FromRgb(238, 130, 238) // Violet + new Color(255, 0, 0), // Red + new Color(255, 165, 0), // Orange + new Color(255, 255, 0), // Yellow + new Color(0, 128, 0), // Green + new Color(0, 0, 255), // Blue + new Color(75, 0, 130), // Indigo + new Color(238, 130, 238) // Violet }; // Define the number of steps between each color @@ -240,245 +228,4 @@ internal class GradientsView : View new Terminal.Gui.Color (color.R, color.G, color.B) )); // Setting color based on RGB } -} - -internal class BallsView : View -{ - private Ball? _ball; - private bool _resized; - private LineCanvas lc; - private Gradient gradient; - - protected override void OnViewportChanged (DrawEventArgs e) - { - base.OnViewportChanged (e); - _resized = true; - - lc = new LineCanvas (new []{ - new StraightLine(new System.Drawing.Point(0,0),10,Orientation.Horizontal,LineStyle.Single), - - }); - TextEffectsScenario.GetAppealingGradientColors (out var stops, out var steps); - gradient = new Gradient (stops, steps); - var fill = new FillPair ( - new GradientFill (new System.Drawing.Rectangle (0, 0, 10, 0), gradient , Gradient.Direction.Horizontal), - new SolidFill(Terminal.Gui.Color.Black) - ); - lc.Fill = fill; - - } - - public override void OnDrawContent (Rectangle viewport) - { - base.OnDrawContent (viewport); - - if ((_ball == null && viewport.Width > 0 && viewport.Height > 0) || _resized) - { - _ball = new Ball (this); - _ball.Start (); - _resized = false; - } - - _ball?.Draw (); - - foreach(var map in lc.GetCellMap()) - { - Driver.SetAttribute (map.Value.Value.Attribute.Value); - AddRune (map.Key.X, map.Key.Y, map.Value.Value.Rune); - } - - for (int x = 0; x < 10; x++) - { - double fraction = (double)x / 10; - Color color = gradient.GetColorAtFraction (fraction); - - SetColor (color); - - AddRune (x, 2, new Rune ('█')); - } - - var map2 = gradient.BuildCoordinateColorMapping (0,10,Gradient.Direction.Horizontal); - - for (int x = 0; x < map2.Count; x++) - { - SetColor (map2[new Coord(x,0)]); - - AddRune (x, 3, new Rune ('█')); - } - } - - private void SetColor (Color color) - { - // Assuming AddRune is a method you have for drawing at specific positions - Application.Driver.SetAttribute ( - new Attribute ( - new Terminal.Gui.Color (color.R, color.G, color.B), - new Terminal.Gui.Color (color.R, color.G, color.B) - )); // Setting color based on RGB - } - public class Ball - { - public Animation Animation { get; private set; } - public Scene BouncingScene { get; private set; } - public View Viewport { get; private set; } - public EffectCharacter Character { get; private set; } - - public Ball (View viewport) - { - Viewport = viewport; - Character = new EffectCharacter (1, "O", 0, 0); - Animation = Character.Animation; - CreateBouncingScene (); - CreateMotionPath (); - } - - private void CreateBouncingScene () - { - BouncingScene = Animation.NewScene (isLooping: true); - int width = Viewport.Frame.Width; - int height = Viewport.Frame.Height; - double frequency = 4 * Math.PI / width; // Double the frequency - - for (int x = 0; x < width; x++) - { - int y = (int)((height - 1) / 2 * (1 + Math.Sin (frequency * x) * 0.8)); // Decrease amplitude - BouncingScene.AddFrame ("O", 1); - } - - for (int x = width - 1; x >= 0; x--) - { - int y = (int)((height - 1) / 2 * (1 + Math.Sin (frequency * x) * 0.8)); // Decrease amplitude - BouncingScene.AddFrame ("O", 1); - } - } - - private void CreateMotionPath () - { - int width = Viewport.Frame.Width; - int height = Viewport.Frame.Height; - double frequency = 4 * Math.PI / width; // Double the frequency - - var path = Character.Motion.CreatePath ("sineWavePath", speed: 1, loop: true); - - for (int x = 0; x < width; x++) - { - int y = (int)((height - 1) / 2 * (1 + Math.Sin (frequency * x) * 0.8)); // Decrease amplitude - path.AddWaypoint (new Waypoint ($"waypoint_{x}", new Coord (x, y))); - } - - for (int x = width - 1; x >= 0; x--) - { - int y = (int)((height - 1) / 2 * (1 + Math.Sin (frequency * x) * 0.8)); // Decrease amplitude - path.AddWaypoint (new Waypoint ($"waypoint_{x}", new Coord (x, y))); - } - - Character.Motion.ActivatePath (path); - } - - public void Start () - { - Animation.ActivateScene (BouncingScene); - new Thread (() => - { - while (true) - { - Thread.Sleep (10); // Adjust the speed of animation - Character.Tick (); - - Application.Invoke (() => Viewport.SetNeedsDisplay ()); - } - }) - { IsBackground = true }.Start (); - } - - public void Draw () - { - Driver.SetAttribute (Viewport.ColorScheme.Normal); - Viewport.AddRune (Character.Motion.CurrentCoord.Column, Character.Motion.CurrentCoord.Row, new Rune ('O')); - } - } -} - - -public class Ball -{ - public Animation Animation { get; private set; } - public Scene BouncingScene { get; private set; } - public View Viewport { get; private set; } - public EffectCharacter Character { get; private set; } - - public Ball (View viewport) - { - Viewport = viewport; - Character = new EffectCharacter (1, "O", 0, 0); - Animation = Character.Animation; - CreateBouncingScene (); - CreateMotionPath (); - } - - private void CreateBouncingScene () - { - BouncingScene = Animation.NewScene (isLooping: true); - int width = Viewport.Frame.Width; - int height = Viewport.Frame.Height; - double frequency = 4 * Math.PI / width; // Double the frequency - - for (int x = 0; x < width; x++) - { - int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude - BouncingScene.AddFrame ("O", 1); - } - - for (int x = width - 1; x >= 0; x--) - { - int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude - BouncingScene.AddFrame ("O", 1); - } - } - - private void CreateMotionPath () - { - int width = Viewport.Frame.Width; - int height = Viewport.Frame.Height; - double frequency = 4 * Math.PI / width; // Double the frequency - - var path = Character.Motion.CreatePath ("sineWavePath", speed: 1, loop: true); - - for (int x = 0; x < width; x++) - { - int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude - path.AddWaypoint (new Waypoint ($"waypoint_{x}", new Coord (x, y))); - } - - for (int x = width - 1; x >= 0; x--) - { - int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude - path.AddWaypoint (new Waypoint ($"waypoint_{x}", new Coord (x, y))); - } - - Character.Motion.ActivatePath (path); - } - - public void Start () - { - Animation.ActivateScene (BouncingScene); - new Thread (() => - { - while (true) - { - Thread.Sleep (10); // Adjust the speed of animation - Character.Tick (); - - Application.Invoke (() => Viewport.SetNeedsDisplay ()); - } - }) - { IsBackground = true }.Start (); - } - - public void Draw () - { - Application.Driver.SetAttribute (Viewport.ColorScheme.Normal); - Viewport.AddRune (Character.Motion.CurrentCoord.Column, Character.Motion.CurrentCoord.Row, new Rune ('O')); - } -} - +} \ No newline at end of file diff --git a/UnitTests/TextEffects/AnimationTests.cs b/UnitTests/TextEffects/AnimationTests.cs deleted file mode 100644 index 84de80f70..000000000 --- a/UnitTests/TextEffects/AnimationTests.cs +++ /dev/null @@ -1,191 +0,0 @@ -using Terminal.Gui.TextEffects; - -namespace Terminal.Gui.TextEffectsTests; -using Color = Terminal.Gui.TextEffects.Color; - -public class AnimationTests -{ - private EffectCharacter character; - - public AnimationTests () - { - character = new EffectCharacter (0, "a", 0, 0); - } - - [Fact] - public void TestCharacterVisualInit () - { - var visual = new CharacterVisual ( - symbol: "a", - bold: true, - dim: false, - italic: true, - underline: false, - blink: true, - reverse: false, - hidden: true, - strike: false, - color: new Color ("ffffff"), - colorCode: "ffffff" - ); - Assert.Equal ("\x1b[1m\x1b[3m\x1b[5m\x1b[8m\x1b[38;2;255;255;255ma\x1b[0m", visual.FormattedSymbol); - Assert.True (visual.Bold); - Assert.False (visual.Dim); - Assert.True (visual.Italic); - Assert.False (visual.Underline); - Assert.True (visual.Blink); - Assert.False (visual.Reverse); - Assert.True (visual.Hidden); - Assert.False (visual.Strike); - Assert.Equal (new Color ("ffffff"), visual.Color); - Assert.Equal ("ffffff", visual.ColorCode); - } - - [Fact] - public void TestFrameInit () - { - var visual = new CharacterVisual ( - symbol: "a", - bold: true, - dim: false, - italic: true, - underline: false, - blink: true, - reverse: false, - hidden: true, - strike: false, - color: new Color ("ffffff") - ); - var frame = new Frame (characterVisual: visual, duration: 5); - Assert.Equal (visual, frame.CharacterVisual); - Assert.Equal (5, frame.Duration); - Assert.Equal (0, frame.TicksElapsed); - } - - [Fact] - public void TestSceneInit () - { - var scene = new Scene (sceneId: "test_scene", isLooping: true, sync: SyncMetric.Step, ease: Easing.InSine); - Assert.Equal ("test_scene", scene.SceneId); - Assert.True (scene.IsLooping); - Assert.Equal (SyncMetric.Step, scene.Sync); - Assert.Equal (Easing.InSine, scene.Ease); - } - - [Fact] - public void TestSceneAddFrame () - { - var scene = new Scene (sceneId: "test_scene"); - scene.AddFrame (symbol: "a", duration: 5, color: new Color ("ffffff"), bold: true, italic: true, blink: true, hidden: true); - Assert.Single (scene.Frames); - var frame = scene.Frames [0]; - Assert.Equal ("\x1b[1m\x1b[3m\x1b[5m\x1b[8m\x1b[38;2;255;255;255ma\x1b[0m", frame.CharacterVisual.FormattedSymbol); - Assert.Equal (5, frame.Duration); - Assert.Equal (new Color ("ffffff"), frame.CharacterVisual.Color); - Assert.True (frame.CharacterVisual.Bold); - } - - [Fact] - public void TestSceneAddFrameInvalidDuration () - { - var scene = new Scene (sceneId: "test_scene"); - var exception = Assert.Throws (() => scene.AddFrame (symbol: "a", duration: 0, color: new Color ("ffffff"))); - Assert.Equal ("duration must be greater than 0", exception.Message); - } - - [Fact] - public void TestSceneApplyGradientToSymbolsEqualColorsAndSymbols () - { - var scene = new Scene (sceneId: "test_scene"); - var gradient = new Gradient (new [] { new Color ("000000"), new Color ("ffffff") }, - steps: new [] { 2 }); - var symbols = new List { "a", "b", "c" }; - scene.ApplyGradientToSymbols (gradient, symbols, duration: 1); - Assert.Equal (3, scene.Frames.Count); - for (int i = 0; i < scene.Frames.Count; i++) - { - Assert.Equal (1, scene.Frames [i].Duration); - Assert.Equal (gradient.Spectrum [i].RgbColor, scene.Frames [i].CharacterVisual.ColorCode); - } - } - - [Fact] - public void TestSceneApplyGradientToSymbolsUnequalColorsAndSymbols () - { - var scene = new Scene (sceneId: "test_scene"); - var gradient = new Gradient ( - new [] { new Color ("000000"), new Color ("ffffff") }, - steps: new [] { 4 }); - var symbols = new List { "q", "z" }; - scene.ApplyGradientToSymbols (gradient, symbols, duration: 1); - Assert.Equal (5, scene.Frames.Count); - Assert.Equal (gradient.Spectrum [0].RgbColor, scene.Frames [0].CharacterVisual.ColorCode); - Assert.Contains ("q", scene.Frames [0].CharacterVisual.Symbol); - Assert.Equal (gradient.Spectrum [^1].RgbColor, scene.Frames [^1].CharacterVisual.ColorCode); - Assert.Contains ("z", scene.Frames [^1].CharacterVisual.Symbol); - } - - [Fact] - public void TestAnimationInit () - { - var animation = character.Animation; - Assert.Equal (character, animation.Character); - Assert.Empty (animation.Scenes); - Assert.Null (animation.ActiveScene); - Assert.False (animation.UseXtermColors); - Assert.False (animation.NoColor); - Assert.Empty (animation.XtermColorMap); - Assert.Equal (0, animation.ActiveSceneCurrentStep); - } - - [Fact] - public void TestAnimationNewScene () - { - var animation = character.Animation; - var scene = animation.NewScene (id:"test_scene", isLooping: true); - Assert.IsType (scene); - Assert.Equal ("test_scene", scene.SceneId); - Assert.True (scene.IsLooping); - Assert.True (animation.Scenes.ContainsKey ("test_scene")); - } - - [Fact] - public void TestAnimationNewSceneWithoutId () - { - var animation = character.Animation; - var scene = animation.NewScene (); - Assert.IsType (scene); - Assert.Equal ("0", scene.SceneId); - Assert.True (animation.Scenes.ContainsKey ("0")); - } - - [Fact] - public void TestAnimationQueryScene () - { - var animation = character.Animation; - var scene = animation.NewScene (id:"test_scene", isLooping: true); - Assert.Equal (scene, animation.QueryScene ("test_scene")); - } - - [Fact] - public void TestAnimationLoopingActiveSceneIsComplete () - { - var animation = character.Animation; - var scene = animation.NewScene (id: "test_scene", isLooping: true); - scene.AddFrame (symbol: "a", duration: 2); - animation.ActivateScene (scene); - Assert.True (animation.ActiveSceneIsComplete ()); - } - - [Fact] - public void TestAnimationNonLoopingActiveSceneIsComplete () - { - var animation = character.Animation; - var scene = animation.NewScene (id: "test_scene"); - scene.AddFrame (symbol: "a", duration: 1); - animation.ActivateScene (scene); - Assert.False (animation.ActiveSceneIsComplete ()); - animation.StepAnimation (); - Assert.True (animation.ActiveSceneIsComplete ()); - } -} \ No newline at end of file diff --git a/UnitTests/TextEffects/New/GradientFillTests.cs b/UnitTests/TextEffects/New/GradientFillTests.cs index 400425dc5..a4f38d9fe 100644 --- a/UnitTests/TextEffects/New/GradientFillTests.cs +++ b/UnitTests/TextEffects/New/GradientFillTests.cs @@ -1,4 +1,6 @@ -namespace Terminal.Gui.TextEffects.Tests; +using Terminal.Gui.Drawing; + +namespace Terminal.Gui.TextEffects.Tests; public class GradientFillTests { @@ -9,8 +11,8 @@ public class GradientFillTests // Define the colors of the gradient stops var stops = new List { - Color.FromRgb(255, 0, 0), // Red - Color.FromRgb(0, 0, 255) // Blue + new Color(255, 0, 0), // Red + new Color(0, 0, 255) // Blue }; // Define the number of steps between each color From f7d584be59d0bf0bc5dee316b7b7ffddaa30b945 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 7 Jul 2024 11:12:15 +0100 Subject: [PATCH 15/48] Add attribution --- Terminal.Gui/Drawing/Gradient.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Terminal.Gui/Drawing/Gradient.cs b/Terminal.Gui/Drawing/Gradient.cs index b5d58e34a..27aab4afe 100644 --- a/Terminal.Gui/Drawing/Gradient.cs +++ b/Terminal.Gui/Drawing/Gradient.cs @@ -1,4 +1,7 @@ -namespace Terminal.Gui; +// This code is a C# port from python library Terminal Text Effects https://github.com/ChrisBuilds/terminaltexteffects/ + +namespace Terminal.Gui; + using System; using System.Collections.Generic; using System.Linq; From be764b7903d57415f3bf2384642c706a9088074f Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 7 Jul 2024 19:54:02 +0100 Subject: [PATCH 16/48] xml doc --- Terminal.Gui/Drawing/FillPair.cs | 25 ++++++++-- Terminal.Gui/Drawing/Gradient.cs | 47 ++++++++++++++----- Terminal.Gui/Drawing/GradientFill.cs | 16 ++++++- Terminal.Gui/Drawing/LineCanvas.cs | 16 +++++-- Terminal.Gui/Drawing/SolidFill.cs | 12 +++++ Terminal.Gui/Drawing/StraightLine.cs | 22 ++++----- UICatalog/Scenarios/TextEffectsScenario.cs | 35 +++++++------- .../TextEffects/New/GradientFillTests.cs | 10 ++-- 8 files changed, 129 insertions(+), 54 deletions(-) diff --git a/Terminal.Gui/Drawing/FillPair.cs b/Terminal.Gui/Drawing/FillPair.cs index f51ceec65..2a6c7d5ae 100644 --- a/Terminal.Gui/Drawing/FillPair.cs +++ b/Terminal.Gui/Drawing/FillPair.cs @@ -10,16 +10,35 @@ namespace Terminal.Gui; /// public class FillPair { + /// + /// Creates a new instance using the provided fills for foreground and background + /// color when assembling . + /// + /// + /// public FillPair (GradientFill fore, SolidFill back) { Foreground = fore; Background = back; } - IFill Foreground { get; set; } - IFill Background { get; set; } + /// + /// The fill which provides point based foreground color. + /// + public IFill Foreground { get; init; } - internal Attribute? GetAttribute (Point point) + /// + /// The fill which provides point based background color. + /// + public IFill Background { get; init; } + + /// + /// Returns the color pair (foreground+background) to use when rendering + /// a rune at the given . + /// + /// + /// + public Attribute GetAttribute (Point point) { return new Attribute ( Foreground.GetColor (point), diff --git a/Terminal.Gui/Drawing/Gradient.cs b/Terminal.Gui/Drawing/Gradient.cs index 27aab4afe..b0cdf072f 100644 --- a/Terminal.Gui/Drawing/Gradient.cs +++ b/Terminal.Gui/Drawing/Gradient.cs @@ -6,6 +6,36 @@ using System; using System.Collections.Generic; using System.Linq; +/// +/// Describes the pattern that a results in e.g. , etc +/// +public enum GradientDirection +{ + /// + /// Color varies along Y axis but is constant on X axis. + /// + Vertical, + + /// + /// Color varies along X axis but is constant on Y axis. + /// + Horizontal, + + + /// + /// Color varies by distance from center (i.e. in circular ripples) + /// + Radial, + + /// + /// Color varies by X and Y axis (i.e. a slanted gradient) + /// + Diagonal +} + +/// +/// Describes +/// public class Gradient { public List Spectrum { get; private set; } @@ -13,13 +43,6 @@ public class Gradient private readonly List _stops; private readonly List _steps; - public enum Direction - { - Vertical, - Horizontal, - Radial, - Diagonal - } public Gradient (IEnumerable stops, IEnumerable steps, bool loop = false) { @@ -83,13 +106,13 @@ public class Gradient } } - public Dictionary BuildCoordinateColorMapping (int maxRow, int maxColumn, Direction direction) + public Dictionary BuildCoordinateColorMapping (int maxRow, int maxColumn, GradientDirection direction) { var gradientMapping = new Dictionary (); switch (direction) { - case Direction.Vertical: + case GradientDirection.Vertical: for (int row = 0; row <= maxRow; row++) { double fraction = maxRow == 0 ? 1.0 : (double)row / maxRow; @@ -101,7 +124,7 @@ public class Gradient } break; - case Direction.Horizontal: + case GradientDirection.Horizontal: for (int col = 0; col <= maxColumn; col++) { double fraction = maxColumn == 0 ? 1.0 : (double)col / maxColumn; @@ -113,7 +136,7 @@ public class Gradient } break; - case Direction.Radial: + case GradientDirection.Radial: for (int row = 0; row <= maxRow; row++) { for (int col = 0; col <= maxColumn; col++) @@ -125,7 +148,7 @@ public class Gradient } break; - case Direction.Diagonal: + case GradientDirection.Diagonal: for (int row = 0; row <= maxRow; row++) { for (int col = 0; col <= maxColumn; col++) diff --git a/Terminal.Gui/Drawing/GradientFill.cs b/Terminal.Gui/Drawing/GradientFill.cs index d0bf163da..393a183f4 100644 --- a/Terminal.Gui/Drawing/GradientFill.cs +++ b/Terminal.Gui/Drawing/GradientFill.cs @@ -8,11 +8,25 @@ public class GradientFill : IFill { private Dictionary _map; - public GradientFill (Rectangle area, Gradient gradient, Gradient.Direction direction) + /// + /// Creates a new instance of the class that can return + /// color for any point in the given using the provided + /// and . + /// + /// + /// + /// + public GradientFill (Rectangle area, Gradient gradient, GradientDirection direction) { _map = gradient.BuildCoordinateColorMapping (area.Height, area.Width, direction); } + /// + /// Returns the color to use for the given or Black if it + /// lies outside of the prepared gradient area (see constructor). + /// + /// + /// public Color GetColor (Point point) { if (_map.TryGetValue (point, out var color)) diff --git a/Terminal.Gui/Drawing/LineCanvas.cs b/Terminal.Gui/Drawing/LineCanvas.cs index 2fa0d8a9e..2c7367fcf 100644 --- a/Terminal.Gui/Drawing/LineCanvas.cs +++ b/Terminal.Gui/Drawing/LineCanvas.cs @@ -4,7 +4,13 @@ namespace Terminal.Gui; /// Facilitates box drawing and line intersection detection and rendering. Does not support diagonal lines. public class LineCanvas : IDisposable { + /// + /// Optional which when present overrides the + /// (colors) of lines in the canvas. This can be used e.g. to apply a global + /// across all lines. + /// public FillPair? Fill { get; set; } + private readonly List _lines = []; private readonly Dictionary _runeResolvers = new () @@ -86,7 +92,7 @@ public class LineCanvas : IDisposable viewport = Rectangle.Union (viewport, _lines [i].Viewport); } - if (viewport is {Width: 0} or {Height: 0}) + if (viewport is { Width: 0 } or { Height: 0 }) { viewport = viewport with { @@ -325,9 +331,11 @@ public class LineCanvas : IDisposable /// private bool Exactly (HashSet intersects, params IntersectionType [] types) { return intersects.SetEquals (types); } - private Attribute? GetAttributeForIntersects (IntersectionDefinition? [] intersects) { - return Fill != null ? Fill.GetAttribute(intersects [0]!.Point): - intersects [0]!.Line.Attribute; } + private Attribute? GetAttributeForIntersects (IntersectionDefinition? [] intersects) + { + return Fill != null ? Fill.GetAttribute (intersects [0]!.Point) : + intersects [0]!.Line.Attribute; + } private Cell? GetCellForIntersects (ConsoleDriver driver, IntersectionDefinition? [] intersects) { diff --git a/Terminal.Gui/Drawing/SolidFill.cs b/Terminal.Gui/Drawing/SolidFill.cs index 202cec57c..67e1bdf1f 100644 --- a/Terminal.Gui/Drawing/SolidFill.cs +++ b/Terminal.Gui/Drawing/SolidFill.cs @@ -8,10 +8,22 @@ public class SolidFill : IFill { readonly Color _color; + /// + /// Creates a new instance of the class which will return + /// the provided regardless of which point is requested. + /// + /// public SolidFill (Color color) { _color = color; } + + /// + /// Returns the color this instance was constructed with regardless of + /// which is being colored. + /// + /// + /// public Color GetColor (Point point) { return _color; diff --git a/Terminal.Gui/Drawing/StraightLine.cs b/Terminal.Gui/Drawing/StraightLine.cs index f8dfb2d88..8bbd83494 100644 --- a/Terminal.Gui/Drawing/StraightLine.cs +++ b/Terminal.Gui/Drawing/StraightLine.cs @@ -111,10 +111,11 @@ public class StraightLine return null; } + var p = new Point (x, y); + if (StartsAt (x, y)) { - return new IntersectionDefinition ( - new Point (x, y), + return new IntersectionDefinition (p, GetTypeByLength ( IntersectionType.StartLeft, IntersectionType.PassOverHorizontal, @@ -126,8 +127,7 @@ public class StraightLine if (EndsAt (x, y)) { - return new IntersectionDefinition ( - new Point (x, y), + return new IntersectionDefinition (p, Length < 0 ? IntersectionType.StartRight : IntersectionType.StartLeft, this ); @@ -138,8 +138,7 @@ public class StraightLine if (xmin < x && xmax > x) { - return new IntersectionDefinition ( - new Point (x, y), + return new IntersectionDefinition (p, IntersectionType.PassOverHorizontal, this ); @@ -155,10 +154,11 @@ public class StraightLine return null; } + var p = new Point (x, y); + if (StartsAt (x, y)) { - return new IntersectionDefinition ( - new Point (x, y), + return new IntersectionDefinition (p, GetTypeByLength ( IntersectionType.StartUp, IntersectionType.PassOverVertical, @@ -170,8 +170,7 @@ public class StraightLine if (EndsAt (x, y)) { - return new IntersectionDefinition ( - new Point (x, y), + return new IntersectionDefinition (p, Length < 0 ? IntersectionType.StartDown : IntersectionType.StartUp, this ); @@ -182,8 +181,7 @@ public class StraightLine if (ymin < y && ymax > y) { - return new IntersectionDefinition ( - new Point (x, y), + return new IntersectionDefinition (p, IntersectionType.PassOverVertical, this ); diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index d8f20bac1..63bdc8b00 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -19,7 +19,7 @@ public class TextEffectsScenario : Scenario Application.Init (); var w = new Window { - Width = Dim.Fill(), + Width = Dim.Fill (), Height = Dim.Fill (), Title = "Text Effects Scenario" }; @@ -30,13 +30,13 @@ public class TextEffectsScenario : Scenario // TODO: Does not work // SetupGradientLineCanvas (tabView, tabView.Frame.Size); }; - w.SizeChanging += (s,e)=> + w.SizeChanging += (s, e) => { - if(e.Size.HasValue) + if (e.Size.HasValue) { SetupGradientLineCanvas (w, e.Size.Value); } - + // TODO: Does not work //SetupGradientLineCanvas (tabView, tabView.Frame.Size); }; @@ -44,7 +44,7 @@ public class TextEffectsScenario : Scenario w.ColorScheme = new ColorScheme { Normal = new Terminal.Gui.Attribute (ColorName.White, ColorName.Black), - Focus = new Terminal.Gui.Attribute (ColorName.Black,ColorName.White), + Focus = new Terminal.Gui.Attribute (ColorName.Black, ColorName.White), HotNormal = new Terminal.Gui.Attribute (ColorName.White, ColorName.Black), HotFocus = new Terminal.Gui.Attribute (ColorName.White, ColorName.Black), Disabled = new Terminal.Gui.Attribute (ColorName.Gray, ColorName.Black) @@ -67,7 +67,7 @@ public class TextEffectsScenario : Scenario DisplayText = "Gradients" }; - tabView.AddTab (t1,false); + tabView.AddTab (t1, false); w.Add (tabView); @@ -86,7 +86,7 @@ public class TextEffectsScenario : Scenario var g = new Gradient (stops, steps); var fore = new GradientFill ( - new Rectangle (0, 0, size.Width, size.Height), g, Gradient.Direction.Diagonal); + new Rectangle (0, 0, size.Width, size.Height), g, GradientDirection.Diagonal); var back = new SolidFill (new Terminal.Gui.Color (ColorName.Black)); w.LineCanvas.Fill = new FillPair ( @@ -107,7 +107,7 @@ public class TextEffectsScenario : Scenario }; // Define the number of steps between each color for smoother transitions - steps = new List { 15,15, 15, 15 }; + steps = new List { 15, 15, 15, 15 }; } } @@ -125,24 +125,25 @@ internal class GradientsView : View if (viewport.Height < 25) // Not enough space, render in a single line { - DrawGradientArea (Gradient.Direction.Horizontal, x, y); - DrawGradientArea (Gradient.Direction.Vertical, x + 32, y); - DrawGradientArea (Gradient.Direction.Radial, x + 64, y); - DrawGradientArea (Gradient.Direction.Diagonal, x + 96, y); + DrawGradientArea (GradientDirection.Horizontal, x, y); + DrawGradientArea (GradientDirection.Horizontal, x, y); + DrawGradientArea (GradientDirection.Vertical, x + 32, y); + DrawGradientArea (GradientDirection.Radial, x + 64, y); + DrawGradientArea (GradientDirection.Diagonal, x + 96, y); } else // Enough space, render in two lines { - DrawGradientArea (Gradient.Direction.Horizontal, x, y); - DrawGradientArea (Gradient.Direction.Vertical, x + 32, y); - DrawGradientArea (Gradient.Direction.Radial, x, y + 17); - DrawGradientArea (Gradient.Direction.Diagonal, x + 32, y + 17); + DrawGradientArea (GradientDirection.Horizontal, x, y); + DrawGradientArea (GradientDirection.Vertical, x + 32, y); + DrawGradientArea (GradientDirection.Radial, x, y + 17); + DrawGradientArea (GradientDirection.Diagonal, x + 32, y + 17); } } - private void DrawGradientArea (Gradient.Direction direction, int xOffset, int yOffset) + private void DrawGradientArea (GradientDirection direction, int xOffset, int yOffset) { // Define the colors of the gradient stops var stops = new List diff --git a/UnitTests/TextEffects/New/GradientFillTests.cs b/UnitTests/TextEffects/New/GradientFillTests.cs index a4f38d9fe..8349edd6d 100644 --- a/UnitTests/TextEffects/New/GradientFillTests.cs +++ b/UnitTests/TextEffects/New/GradientFillTests.cs @@ -25,13 +25,13 @@ public class GradientFillTests public void TestGradientFillCorners () { var area = new Rectangle (0, 0, 10, 10); - var gradientFill = new GradientFill (area, _gradient, Gradient.Direction.Diagonal); + var gradientFill = new GradientFill (area, _gradient, GradientDirection.Diagonal); // Test the corners var topLeft = new Point (0, 0); - var topRight = new Point (area.Width - 1, 0); - var bottomLeft = new Point (0, area.Height - 1); - var bottomRight = new Point (area.Width - 1, area.Height - 1); + var topRight = new Point (area.Width, 0); + var bottomLeft = new Point (0, area.Height ); + var bottomRight = new Point (area.Width, area.Height); var topLeftColor = gradientFill.GetColor (topLeft); var topRightColor = gradientFill.GetColor (topRight); @@ -62,7 +62,7 @@ public class GradientFillTests public void TestGradientFillColorTransition () { var area = new Rectangle (0, 0, 10, 10); - var gradientFill = new GradientFill (area, _gradient, Gradient.Direction.Diagonal); + var gradientFill = new GradientFill (area, _gradient, GradientDirection.Diagonal); for (int row = 0; row < area.Height; row++) { From a167366b147a46be441684b767a90c14a85fa36b Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 7 Jul 2024 20:13:56 +0100 Subject: [PATCH 17/48] Tests, xmldoc and guards --- Terminal.Gui/Drawing/Gradient.cs | 29 ++++++++++ Terminal.Gui/Drawing/GradientFill.cs | 2 +- .../New => Drawing}/GradientFillTests.cs | 11 ++-- UnitTests/Drawing/GradientTests.cs | 53 +++++++++++++++++++ 4 files changed, 88 insertions(+), 7 deletions(-) rename UnitTests/{TextEffects/New => Drawing}/GradientFillTests.cs (94%) create mode 100644 UnitTests/Drawing/GradientTests.cs diff --git a/Terminal.Gui/Drawing/Gradient.cs b/Terminal.Gui/Drawing/Gradient.cs index b0cdf072f..7aea37b90 100644 --- a/Terminal.Gui/Drawing/Gradient.cs +++ b/Terminal.Gui/Drawing/Gradient.cs @@ -58,10 +58,26 @@ public class Gradient Spectrum = GenerateGradient (_steps); } + /// + /// Returns the color to use at the given part of the spectrum + /// + /// Proportion of the way through the spectrum, must be between + /// 0 and 1 (inclusive). Returns the last color if is + /// . + /// + /// public Color GetColorAtFraction (double fraction) { + if (double.IsNaN (fraction)) + { + return Spectrum.Last (); + } + if (fraction < 0 || fraction > 1) + { throw new ArgumentOutOfRangeException (nameof (fraction), "Fraction must be between 0 and 1."); + } + int index = (int)(fraction * (Spectrum.Count - 1)); return Spectrum [index]; } @@ -106,6 +122,19 @@ public class Gradient } } + /// + /// + /// Creates a mapping starting at 0,0 and going to and + /// (inclusively) using the supplied . + /// + /// + /// Note that this method is inclusive i.e. passing 1/1 results in 4 mapped coordinates. + /// + /// + /// + /// + /// + /// public Dictionary BuildCoordinateColorMapping (int maxRow, int maxColumn, GradientDirection direction) { var gradientMapping = new Dictionary (); diff --git a/Terminal.Gui/Drawing/GradientFill.cs b/Terminal.Gui/Drawing/GradientFill.cs index 393a183f4..31339ebe0 100644 --- a/Terminal.Gui/Drawing/GradientFill.cs +++ b/Terminal.Gui/Drawing/GradientFill.cs @@ -18,7 +18,7 @@ public class GradientFill : IFill /// public GradientFill (Rectangle area, Gradient gradient, GradientDirection direction) { - _map = gradient.BuildCoordinateColorMapping (area.Height, area.Width, direction); + _map = gradient.BuildCoordinateColorMapping (area.Height-1, area.Width-1, direction); } /// diff --git a/UnitTests/TextEffects/New/GradientFillTests.cs b/UnitTests/Drawing/GradientFillTests.cs similarity index 94% rename from UnitTests/TextEffects/New/GradientFillTests.cs rename to UnitTests/Drawing/GradientFillTests.cs index 8349edd6d..fd6822539 100644 --- a/UnitTests/TextEffects/New/GradientFillTests.cs +++ b/UnitTests/Drawing/GradientFillTests.cs @@ -1,6 +1,5 @@ -using Terminal.Gui.Drawing; - -namespace Terminal.Gui.TextEffects.Tests; + +namespace Terminal.Gui.DrawingTests; public class GradientFillTests { @@ -29,9 +28,9 @@ public class GradientFillTests // Test the corners var topLeft = new Point (0, 0); - var topRight = new Point (area.Width, 0); - var bottomLeft = new Point (0, area.Height ); - var bottomRight = new Point (area.Width, area.Height); + var topRight = new Point (area.Width - 1, 0); + var bottomLeft = new Point (0, area.Height - 1); + var bottomRight = new Point (area.Width - 1, area.Height - 1); var topLeftColor = gradientFill.GetColor (topLeft); var topRightColor = gradientFill.GetColor (topRight); diff --git a/UnitTests/Drawing/GradientTests.cs b/UnitTests/Drawing/GradientTests.cs new file mode 100644 index 000000000..e9e0194ea --- /dev/null +++ b/UnitTests/Drawing/GradientTests.cs @@ -0,0 +1,53 @@ + +namespace Terminal.Gui.DrawingTests; + +public class GradientTests +{ + // Static method to provide all enum values + public static IEnumerable GradientDirectionValues () + { + return typeof (GradientDirection).GetEnumValues () + .Cast () + .Select (direction => new object [] { direction }); + } + + [Theory] + [MemberData (nameof (GradientDirectionValues))] + public void GradientIsInclusive_2_by_2 (GradientDirection direction) + { + // Define the colors of the gradient stops + var stops = new List + { + new Color(255, 0, 0), // Red + new Color(0, 0, 255) // Blue + }; + + // Define the number of steps between each color + var steps = new List { 10 }; // 10 steps between Red -> Blue + + var g = new Gradient (stops, steps, loop: false); + Assert.Equal (4, g.BuildCoordinateColorMapping (1, 1, direction).Count); + } + + [Theory] + [MemberData (nameof (GradientDirectionValues))] + public void GradientIsInclusive_1_by_1 (GradientDirection direction) + { + // Define the colors of the gradient stops + var stops = new List + { + new Color(255, 0, 0), // Red + new Color(0, 0, 255) // Blue + }; + + // Define the number of steps between each color + var steps = new List { 10 }; // 10 steps between Red -> Blue + + var g = new Gradient (stops, steps, loop: false); + + // Note that + var c = Assert.Single (g.BuildCoordinateColorMapping (0, 0, direction)); + Assert.Equal (c.Key, new Point(0,0)); + Assert.Equal (c.Value, new Color (0, 0, 255)); + } +} From ae7eeeb850812a2a1529b9a107e647f0e5373bf7 Mon Sep 17 00:00:00 2001 From: Tig Date: Sun, 7 Jul 2024 13:53:22 -0600 Subject: [PATCH 18/48] Update gitversion --- GitVersion.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/GitVersion.yml b/GitVersion.yml index d5f370a83..6a170eb58 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -19,18 +19,19 @@ branches: #is-source-branch-for: ['v2'] source-branches: [] - main: - tag: rc + v2: + mode: ContinuousDeployment + is-release-branch: true + tag: prealpha increment: Patch - source-branches: - - develop - - main + regex: ^v2?[/-] + source-branches: ['v2_develop'] + feature: tag: useBranchName regex: ^features?[/-] source-branches: - - develop - - main + - v2_develop pull-request: tag: PullRequest.{BranchName} increment: Inherit From b2bbd42a5079b3702d51b8ae7a746b8142a0c111 Mon Sep 17 00:00:00 2001 From: Tig Date: Sun, 7 Jul 2024 13:59:36 -0600 Subject: [PATCH 19/48] Updated yml --- .github/workflows/api-docs.yml | 2 +- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/dotnet-core.yml | 4 ++-- GitVersion.yml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/api-docs.yml b/.github/workflows/api-docs.yml index 0386c3fef..b2c998fad 100644 --- a/.github/workflows/api-docs.yml +++ b/.github/workflows/api-docs.yml @@ -2,7 +2,7 @@ name: Build and publish API docs on: push: - branches: [main, develop, v2_develop] + branches: [main, v2_develop] permissions: id-token: write diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e7d445314..de87f35c1 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -4,7 +4,7 @@ name: "Code scanning" on: push: - branches: [main] + branches: [main, v2_release] paths-ignore: - '**/*.md' - '**/*.txt' @@ -12,7 +12,7 @@ on: - docs - docfx pull_request: - branches: [main] + branches: [main, v2_release] paths-ignore: - '**/*.md' - '**/*.txt' diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index 502cfb89f..3c92c7854 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -2,11 +2,11 @@ name: Build & Test Terminal.Gui with .NET Core on: push: - branches: [ v2, v2_develop ] + branches: [ v2_release, v2_develop ] paths-ignore: - '**.md' pull_request: - branches: [ v2, v2_develop ] + branches: [ v2_release, v2_develop ] paths-ignore: - '**.md' diff --git a/GitVersion.yml b/GitVersion.yml index 6a170eb58..6591516d9 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -24,7 +24,7 @@ branches: is-release-branch: true tag: prealpha increment: Patch - regex: ^v2?[/-] + regex: ^v2_release?[/-] source-branches: ['v2_develop'] feature: From 404b406f8c103884d134e3a45584c1c1c2a2974b Mon Sep 17 00:00:00 2001 From: Tig Date: Sun, 7 Jul 2024 14:01:49 -0600 Subject: [PATCH 20/48] Updated yml --- GitVersion.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/GitVersion.yml b/GitVersion.yml index 6591516d9..91c174714 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -19,11 +19,10 @@ branches: #is-source-branch-for: ['v2'] source-branches: [] - v2: + v2_release: mode: ContinuousDeployment is-release-branch: true tag: prealpha - increment: Patch regex: ^v2_release?[/-] source-branches: ['v2_develop'] From 116cba8c8c2b845414e22867701a7ed8841ebf06 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 7 Jul 2024 21:06:21 +0100 Subject: [PATCH 21/48] Gradient tests --- Terminal.Gui/Drawing/Gradient.cs | 42 ++++++- UICatalog/Scenarios/TextEffectsScenario.cs | 42 ++++--- UnitTests/Drawing/GradientTests.cs | 124 ++++++++++++++++++++- 3 files changed, 189 insertions(+), 19 deletions(-) diff --git a/Terminal.Gui/Drawing/Gradient.cs b/Terminal.Gui/Drawing/Gradient.cs index 7aea37b90..01d7d2490 100644 --- a/Terminal.Gui/Drawing/Gradient.cs +++ b/Terminal.Gui/Drawing/Gradient.cs @@ -44,15 +44,42 @@ public class Gradient private readonly List _steps; + /// + /// Creates a new instance of the class which hosts a + /// of colors including all and interpolated colors + /// between each corresponding pair. + /// + /// The colors to use in the spectrum (N) + /// The number of colors to generate between each pair (must be N-1 numbers). + /// If only one step is passed then it is assumed to be the same distance for all pairs. + /// True to duplicate the first stop and step so that the gradient repeats itself + /// public Gradient (IEnumerable stops, IEnumerable steps, bool loop = false) { _stops = stops.ToList (); + if (_stops.Count < 1) + { throw new ArgumentException ("At least one color stop must be provided."); + } _steps = steps.ToList (); + + // If multiple colors and only 1 step assume same distance applies to all steps + if (_stops.Count > 2 && _steps.Count == 1) + { + _steps = Enumerable.Repeat (_steps.Single (),_stops.Count() - 1).ToList(); + } + if (_steps.Any (step => step < 1)) + { throw new ArgumentException ("Steps must be greater than 0."); + } + + if (_steps.Count != _stops.Count - 1) + { + throw new ArgumentException ("Number of steps must be N-1"); + } _loop = loop; Spectrum = GenerateGradient (_steps); @@ -85,6 +112,7 @@ public class Gradient private List GenerateGradient (IEnumerable steps) { List gradient = new List (); + if (_stops.Count == 1) { for (int i = 0; i < steps.Sum (); i++) @@ -94,13 +122,17 @@ public class Gradient return gradient; } + var stopsToUse = _stops.ToList (); + var stepsToUse = _steps.ToList (); + if (_loop) { - _stops.Add (_stops [0]); + stopsToUse.Add (_stops [0]); + stepsToUse.Add (_steps.First ()); } - var colorPairs = _stops.Zip (_stops.Skip (1), (start, end) => new { start, end }); - var stepsList = _steps.ToList (); + var colorPairs = stopsToUse.Zip (stopsToUse.Skip (1), (start, end) => new { start, end }); + var stepsList = stepsToUse; foreach (var (colorPair, thesteps) in colorPairs.Zip (stepsList, (pair, step) => (pair, step))) { @@ -112,7 +144,7 @@ public class Gradient private IEnumerable InterpolateColors (Color start, Color end, int steps) { - for (int step = 0; step <= steps; step++) + for (int step = 0; step < steps; step++) { double fraction = (double)step / steps; int r = (int)(start.R + fraction * (end.R - start.R)); @@ -120,8 +152,10 @@ public class Gradient int b = (int)(start.B + fraction * (end.B - start.B)); yield return new Color (r, g, b); } + yield return end; // Ensure the last color is included } + /// /// /// Creates a mapping starting at 0,0 and going to and diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index 63bdc8b00..14ce7c329 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -14,6 +14,8 @@ public class TextEffectsScenario : Scenario { private TabView tabView; + public static bool LoopingGradient = false; + public override void Main () { Application.Init (); @@ -27,8 +29,6 @@ public class TextEffectsScenario : Scenario w.Loaded += (s, e) => { SetupGradientLineCanvas (w, w.Frame.Size); - // TODO: Does not work - // SetupGradientLineCanvas (tabView, tabView.Frame.Size); }; w.SizeChanging += (s, e) => { @@ -36,9 +36,6 @@ public class TextEffectsScenario : Scenario { SetupGradientLineCanvas (w, e.Size.Value); } - - // TODO: Does not work - //SetupGradientLineCanvas (tabView, tabView.Frame.Size); }; w.ColorScheme = new ColorScheme @@ -57,16 +54,32 @@ public class TextEffectsScenario : Scenario Height = Dim.Fill (), }; + var gradientsView = new GradientsView () + { + Width = Dim.Fill (), + Height = Dim.Fill (), + }; var t1 = new Tab () { - View = new GradientsView () - { - Width = Dim.Fill (), - Height = Dim.Fill (), - }, + View = gradientsView, DisplayText = "Gradients" }; + + var cbLooping = new CheckBox () + { + Text = "Looping", + Y = Pos.AnchorEnd (1) + }; + cbLooping.Toggle += (s, e) => + { + LoopingGradient = e.NewValue == CheckState.Checked; + SetupGradientLineCanvas (w, w.Frame.Size); + tabView.SetNeedsDisplay (); + }; + + gradientsView.Add (cbLooping); + tabView.AddTab (t1, false); w.Add (tabView); @@ -83,7 +96,7 @@ public class TextEffectsScenario : Scenario { GetAppealingGradientColors (out var stops, out var steps); - var g = new Gradient (stops, steps); + var g = new Gradient (stops, steps, LoopingGradient); var fore = new GradientFill ( new Rectangle (0, 0, size.Width, size.Height), g, GradientDirection.Diagonal); @@ -107,7 +120,8 @@ public class TextEffectsScenario : Scenario }; // Define the number of steps between each color for smoother transitions - steps = new List { 15, 15, 15, 15 }; + // If we pass only a single value then it will assume equal steps between all pairs + steps = new List { 15 }; } } @@ -157,7 +171,7 @@ internal class GradientsView : View var steps = new List { 10, 10 }; // 10 steps between Red -> Green, and Green -> Blue // Create the gradient - var radialGradient = new Gradient (stops, steps, loop: false); + var radialGradient = new Gradient (stops, steps, loop: TextEffectsScenario.LoopingGradient); // Define the size of the rectangle int maxRow = 15; // Adjusted to keep aspect ratio @@ -207,7 +221,7 @@ internal class GradientsView : View }; // Create the gradient - var rainbowGradient = new Gradient (stops, steps, loop: true); + var rainbowGradient = new Gradient (stops, steps, TextEffectsScenario.LoopingGradient); for (int x = 0; x < viewport.Width; x++) { diff --git a/UnitTests/Drawing/GradientTests.cs b/UnitTests/Drawing/GradientTests.cs index e9e0194ea..0ff4c4a68 100644 --- a/UnitTests/Drawing/GradientTests.cs +++ b/UnitTests/Drawing/GradientTests.cs @@ -50,4 +50,126 @@ public class GradientTests Assert.Equal (c.Key, new Point(0,0)); Assert.Equal (c.Value, new Color (0, 0, 255)); } -} + + [Fact] + public void SingleColorStop () + { + var stops = new List { new Color (255, 0, 0) }; // Red + var steps = new List { }; + + var g = new Gradient (stops, steps, loop: false); + Assert.All (g.Spectrum, color => Assert.Equal (new Color (255, 0, 0), color)); + } + + [Fact] + public void LoopingGradient_CorrectColors () + { + var stops = new List + { + new Color(255, 0, 0), // Red + new Color(0, 0, 255) // Blue + }; + + var steps = new List { 10 }; + + var g = new Gradient (stops, steps, loop: true); + Assert.Equal (new Color (255, 0, 0), g.Spectrum.First ()); + Assert.Equal (new Color (255, 0, 0), g.Spectrum.Last ()); + } + + [Fact] + public void DifferentStepSizes () + { + var stops = new List + { + new Color(255, 0, 0), // Red + new Color(0, 255, 0), // Green + new Color(0, 0, 255) // Blue + }; + + var steps = new List { 5, 15 }; // Different steps + + var g = new Gradient (stops, steps, loop: false); + Assert.Equal (22, g.Spectrum.Count); + } + + [Fact] + public void FractionOutOfRange_ThrowsException () + { + var stops = new List + { + new Color(255, 0, 0), // Red + new Color(0, 0, 255) // Blue + }; + + var steps = new List { 10 }; + + var g = new Gradient (stops, steps, loop: false); + + Assert.Throws (() => g.GetColorAtFraction (-0.1)); + Assert.Throws (() => g.GetColorAtFraction (1.1)); + } + + [Fact] + public void NaNFraction_ReturnsLastColor () + { + var stops = new List + { + new Color(255, 0, 0), // Red + new Color(0, 0, 255) // Blue + }; + + var steps = new List { 10 }; + + var g = new Gradient (stops, steps, loop: false); + Assert.Equal (new Color (0, 0, 255), g.GetColorAtFraction (double.NaN)); + } + + [Fact] + public void Constructor_SingleStepProvided_ReplicatesForAllPairs () + { + var stops = new List + { + new Color(255, 0, 0), // Red + new Color(0, 255, 0), // Green + new Color(0, 0, 255) // Blue + }; + + var singleStep = new List { 5 }; // Single step provided + var gradient = new Gradient (stops, singleStep, loop: false); + + Assert.NotNull (gradient.Spectrum); + Assert.Equal (12, gradient.Spectrum.Count); // 5 steps Red -> Green + 5 steps Green -> Blue + 2 end colors + } + + [Fact] + public void Constructor_InvalidStepsLength_ThrowsArgumentException () + { + var stops = new List + { + new Color(255, 0, 0), // Red + new Color(0, 0, 255) // Blue + }; + + var invalidSteps = new List { 5, 5 }; // Invalid length (N-1 expected) + Assert.Throws (() => new Gradient (stops, invalidSteps, loop: false)); + } + + [Fact] + public void Constructor_ValidStepsLength_DoesNotThrow () + { + var stops = new List + { + new Color(255, 0, 0), // Red + new Color(0, 255, 0), // Green + new Color(0, 0, 255) // Blue + }; + + var validSteps = new List { 5, 5 }; // Valid length (N-1) + var gradient = new Gradient (stops, validSteps, loop: false); + + Assert.NotNull (gradient.Spectrum); + Assert.Equal (12, gradient.Spectrum.Count); // 5 steps Red -> Green + 5 steps Green -> Blue + 2 end colors + } + +} \ No newline at end of file From aeb6ed54790236e7fd564b0739c0a02f73fc73b8 Mon Sep 17 00:00:00 2001 From: Tig Date: Sun, 7 Jul 2024 14:07:50 -0600 Subject: [PATCH 22/48] Updated yml --- GitVersion.yml | 53 +++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/GitVersion.yml b/GitVersion.yml index 91c174714..4727f4315 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -2,35 +2,36 @@ mode: ContinuousDeployment tag-prefix: '[vV]' continuous-delivery-fallback-tag: pre branches: - develop: - mode: ContinuousDeployment - tag: pre - regex: develop - source-branches: - - main - pre-release-weight: 100 + # develop: + # mode: ContinuousDeployment + # tag: pre + # regex: develop + # source-branches: + # - main + # pre-release-weight: 100 - v2_develop: - mode: ContinuousDeployment - tag: pre - regex: ^v2_develop?[/-] - is-release-branch: true - tracks-release-branches: true - #is-source-branch-for: ['v2'] - source-branches: [] + # v2_develop: + # mode: ContinuousDeployment + # tag: pre + # regex: ^v2_develop?[/-] + # is-release-branch: true + # tracks-release-branches: true + # #is-source-branch-for: ['v2'] + # source-branches: [] - v2_release: - mode: ContinuousDeployment - is-release-branch: true - tag: prealpha - regex: ^v2_release?[/-] - source-branches: ['v2_develop'] + # v2_release: + # mode: ContinuousDeployment + # tag: prealpha + # regex: ^v2_release?[/-] + # is-release-branch: true + # source-branches: [] + + # feature: + # tag: useBranchName + # regex: ^features?[/-] + # source-branches: + # - v2_develop - feature: - tag: useBranchName - regex: ^features?[/-] - source-branches: - - v2_develop pull-request: tag: PullRequest.{BranchName} increment: Inherit From 6f9e79249453df2b7fda23ab80e1bb4b06d6cc8c Mon Sep 17 00:00:00 2001 From: Tig Date: Sun, 7 Jul 2024 14:15:05 -0600 Subject: [PATCH 23/48] Updated yml --- GitVersion.yml | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/GitVersion.yml b/GitVersion.yml index 4727f4315..0782f01c2 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -10,31 +10,39 @@ branches: # - main # pre-release-weight: 100 - # v2_develop: - # mode: ContinuousDeployment - # tag: pre - # regex: ^v2_develop?[/-] - # is-release-branch: true - # tracks-release-branches: true - # #is-source-branch-for: ['v2'] - # source-branches: [] + v2_develop: + mode: ContinuousDeployment + tag: pre + regex: ^v2_develop?[/-] + #is-release-branch: true + tracks-release-branches: true + is-source-branch-for: ['v2_release'] + source-branches: [] - # v2_release: - # mode: ContinuousDeployment - # tag: prealpha - # regex: ^v2_release?[/-] - # is-release-branch: true - # source-branches: [] - - # feature: - # tag: useBranchName - # regex: ^features?[/-] - # source-branches: - # - v2_develop + v2_release: + mode: ContinuousDeployment + tag: prealpha + regex: ^v2_release?[/-] + #is-release-branch: true + source-branches: ['v2_develop'] pull-request: + mode: ContinuousDeployment tag: PullRequest.{BranchName} increment: Inherit + tag-number-pattern: '[/-](?\d+)' + regex: ^(pull|pull\-requests|pr)[/-] + source-branches: + - develop + - main + - release + - v2_develop + - v2_release + - feature + - support + - hotfix + pre-release-weight: 30000 + ignore: sha: [] From f2a14a18f04d819685a484cc56df6ceff4d13e16 Mon Sep 17 00:00:00 2001 From: Tig Date: Sun, 7 Jul 2024 14:16:50 -0600 Subject: [PATCH 24/48] Updated yml --- GitVersion.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GitVersion.yml b/GitVersion.yml index 0782f01c2..fb1ea3bb9 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -22,8 +22,8 @@ branches: v2_release: mode: ContinuousDeployment tag: prealpha - regex: ^v2_release?[/-] - #is-release-branch: true + regex: v2_release + is-release-branch: true source-branches: ['v2_develop'] pull-request: From f71b50448e1a45c6663c1525e63bd8fa0abfbf69 Mon Sep 17 00:00:00 2001 From: Tig Date: Sun, 7 Jul 2024 14:18:52 -0600 Subject: [PATCH 25/48] v2_develop builds get '-dev' prefix --- GitVersion.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/GitVersion.yml b/GitVersion.yml index fb1ea3bb9..af28d2f24 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -2,17 +2,17 @@ mode: ContinuousDeployment tag-prefix: '[vV]' continuous-delivery-fallback-tag: pre branches: - # develop: - # mode: ContinuousDeployment - # tag: pre - # regex: develop - # source-branches: - # - main - # pre-release-weight: 100 + develop: + mode: ContinuousDeployment + tag: pre + regex: develop + source-branches: + - main + pre-release-weight: 100 v2_develop: mode: ContinuousDeployment - tag: pre + tag: dev regex: ^v2_develop?[/-] #is-release-branch: true tracks-release-branches: true From 0631579bd99d46b9157b75aacbfce0a954ac424e Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 7 Jul 2024 21:19:55 +0100 Subject: [PATCH 26/48] Fix naming and tests compiler warnings --- UICatalog/Scenarios/TextEffectsScenario.cs | 10 +++++----- UnitTests/Drawing/GradientFillTests.cs | 12 ------------ 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index 14ce7c329..c660ddb5c 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -12,7 +12,7 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("Colors")] public class TextEffectsScenario : Scenario { - private TabView tabView; + private TabView _tabView; public static bool LoopingGradient = false; @@ -48,7 +48,7 @@ public class TextEffectsScenario : Scenario }; // Creates a window that occupies the entire terminal with a title. - tabView = new TabView () + _tabView = new TabView () { Width = Dim.Fill (), Height = Dim.Fill (), @@ -75,14 +75,14 @@ public class TextEffectsScenario : Scenario { LoopingGradient = e.NewValue == CheckState.Checked; SetupGradientLineCanvas (w, w.Frame.Size); - tabView.SetNeedsDisplay (); + _tabView.SetNeedsDisplay (); }; gradientsView.Add (cbLooping); - tabView.AddTab (t1, false); + _tabView.AddTab (t1, false); - w.Add (tabView); + w.Add (_tabView); Application.Run (w); w.Dispose (); diff --git a/UnitTests/Drawing/GradientFillTests.cs b/UnitTests/Drawing/GradientFillTests.cs index fd6822539..bf0d4d0e2 100644 --- a/UnitTests/Drawing/GradientFillTests.cs +++ b/UnitTests/Drawing/GradientFillTests.cs @@ -37,12 +37,6 @@ public class GradientFillTests var bottomLeftColor = gradientFill.GetColor (bottomLeft); var bottomRightColor = gradientFill.GetColor (bottomRight); - // Validate the colors at the corners - Assert.NotNull (topLeftColor); - Assert.NotNull (topRightColor); - Assert.NotNull (bottomLeftColor); - Assert.NotNull (bottomRightColor); - // Expected colors var expectedTopLeftColor = new Terminal.Gui.Color (255, 0, 0); // Red var expectedBottomRightColor = new Terminal.Gui.Color (0, 0, 255); // Blue @@ -73,9 +67,6 @@ public class GradientFillTests var point = new Point (col, row); var color = gradientFill.GetColor (point); - // Ensure color is not null - Assert.NotNull (color); - // Check if the current color is 'more blue' and 'less red' as it goes right and down Assert.True (color.R <= previousRed, $"Failed at ({col}, {row}): {color.R} > {previousRed}"); Assert.True (color.B >= previousBlue, $"Failed at ({col}, {row}): {color.B} < {previousBlue}"); @@ -96,9 +87,6 @@ public class GradientFillTests var point = new Point (col, row); var color = gradientFill.GetColor (point); - // Ensure color is not null - Assert.NotNull (color); - // Check if the current color is 'more blue' and 'less red' as it goes right and down Assert.True (color.R <= previousRed, $"Failed at ({col}, {row}): {color.R} > {previousRed}"); Assert.True (color.B >= previousBlue, $"Failed at ({col}, {row}): {color.B} < {previousBlue}"); From 8a56586fec75737d68cbee023fd74354c0b39d3a Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 7 Jul 2024 21:30:31 +0100 Subject: [PATCH 27/48] Fix note comment and add tests for SolidFill class --- UnitTests/Drawing/GradientTests.cs | 2 +- UnitTests/Drawing/SolidFillTests.cs | 40 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 UnitTests/Drawing/SolidFillTests.cs diff --git a/UnitTests/Drawing/GradientTests.cs b/UnitTests/Drawing/GradientTests.cs index 0ff4c4a68..8fcfc1354 100644 --- a/UnitTests/Drawing/GradientTests.cs +++ b/UnitTests/Drawing/GradientTests.cs @@ -45,7 +45,7 @@ public class GradientTests var g = new Gradient (stops, steps, loop: false); - // Note that + // Note that maxRow and maxCol are inclusive so this results in 1x1 area i.e. a single cell. var c = Assert.Single (g.BuildCoordinateColorMapping (0, 0, direction)); Assert.Equal (c.Key, new Point(0,0)); Assert.Equal (c.Value, new Color (0, 0, 255)); diff --git a/UnitTests/Drawing/SolidFillTests.cs b/UnitTests/Drawing/SolidFillTests.cs new file mode 100644 index 000000000..50eaa9f9f --- /dev/null +++ b/UnitTests/Drawing/SolidFillTests.cs @@ -0,0 +1,40 @@ + +using Terminal.Gui.Drawing; + +namespace Terminal.Gui.DrawingTests; + +public class SolidFillTests +{ + [Fact] + public void GetColor_ReturnsCorrectColor () + { + // Arrange + var expectedColor = new Color (100, 150, 200); + var solidFill = new SolidFill (expectedColor); + + // Act + var resultColor = solidFill.GetColor (new Point (0, 0)); + + // Assert + Assert.Equal (expectedColor, resultColor); + } + + [Theory] + [InlineData (0, 0)] + [InlineData (1, 1)] + [InlineData (-1, -1)] + [InlineData (100, 100)] + [InlineData (-100, -100)] + public void GetColor_ReturnsSameColorForDifferentPoints (int x, int y) + { + // Arrange + var expectedColor = new Color (50, 100, 150); + var solidFill = new SolidFill (expectedColor); + + // Act + var resultColor = solidFill.GetColor (new Point (x, y)); + + // Assert + Assert.Equal (expectedColor, resultColor); + } +} \ No newline at end of file From afc0bf02c504c5c0a827e3e9541ae11b4e0ed49b Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 7 Jul 2024 21:35:13 +0100 Subject: [PATCH 28/48] Fix dodgy constructor on FillPair and add tests --- Terminal.Gui/Drawing/FillPair.cs | 2 +- UnitTests/Drawing/FillPairTests.cs | 32 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 UnitTests/Drawing/FillPairTests.cs diff --git a/Terminal.Gui/Drawing/FillPair.cs b/Terminal.Gui/Drawing/FillPair.cs index 2a6c7d5ae..648dbb40f 100644 --- a/Terminal.Gui/Drawing/FillPair.cs +++ b/Terminal.Gui/Drawing/FillPair.cs @@ -16,7 +16,7 @@ public class FillPair /// /// /// - public FillPair (GradientFill fore, SolidFill back) + public FillPair (IFill fore, IFill back) { Foreground = fore; Background = back; diff --git a/UnitTests/Drawing/FillPairTests.cs b/UnitTests/Drawing/FillPairTests.cs new file mode 100644 index 000000000..bbe8b6563 --- /dev/null +++ b/UnitTests/Drawing/FillPairTests.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terminal.Gui.Drawing; + +namespace Terminal.Gui.DrawingTests; + +public class FillPairTests +{ + + [Fact] + public void GetAttribute_ReturnsCorrectColors () + { + // Arrange + var foregroundColor = new Color (100, 150, 200); + var backgroundColor = new Color (50, 75, 100); + var foregroundFill = new SolidFill (foregroundColor); + var backgroundFill = new SolidFill (backgroundColor); + + var fillPair = new FillPair (foregroundFill, backgroundFill); + + // Act + var resultAttribute = fillPair.GetAttribute (new Point (0, 0)); + + // Assert + Assert.Equal (foregroundColor, resultAttribute.Foreground); + Assert.Equal (backgroundColor, resultAttribute.Background); + } +} + From d15f3af388a5bc9b8ea177a14e0a241cbda187af Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 7 Jul 2024 21:41:19 +0100 Subject: [PATCH 29/48] Add tests that confirm LineCanvas behavior with Fill --- UnitTests/Drawing/LineCanvasTests.cs | 87 ++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/UnitTests/Drawing/LineCanvasTests.cs b/UnitTests/Drawing/LineCanvasTests.cs index 8426d3952..d160b5420 100644 --- a/UnitTests/Drawing/LineCanvasTests.cs +++ b/UnitTests/Drawing/LineCanvasTests.cs @@ -1,4 +1,5 @@ using System.Text; +using Terminal.Gui.Drawing; using Xunit.Abstractions; namespace Terminal.Gui.DrawingTests; @@ -1303,6 +1304,92 @@ public class LineCanvasTests (ITestOutputHelper output) TestHelpers.AssertEqual (output, looksLike, $"{Environment.NewLine}{lc}"); } + [Fact] + public void LineCanvas_UsesFillCorrectly () + { + // Arrange + var foregroundColor = new Color (255, 0, 0); // Red + var backgroundColor = new Color (0, 0, 0); // Black + var foregroundFill = new SolidFill (foregroundColor); + var backgroundFill = new SolidFill (backgroundColor); + var fillPair = new FillPair (foregroundFill, backgroundFill); + + var lineCanvas = new LineCanvas + { + Fill = fillPair + }; + + // Act + lineCanvas.AddLine (new Point (0, 0), 5, Orientation.Horizontal, LineStyle.Single); + var cellMap = lineCanvas.GetCellMap (); + + // Assert + foreach (var cell in cellMap.Values) + { + Assert.NotNull (cell); + Assert.Equal (foregroundColor, cell.Value.Attribute.Value.Foreground); + Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background); + } + } + + [Fact] + public void LineCanvas_LineColorIgnoredBecauseOfFill () + { + // Arrange + var foregroundColor = new Color (255, 0, 0); // Red + var backgroundColor = new Color (0, 0, 0); // Black + var lineColor = new Attribute (new Color (0, 255, 0), new Color (255, 255, 255)); // Green on White + var foregroundFill = new SolidFill (foregroundColor); + var backgroundFill = new SolidFill (backgroundColor); + var fillPair = new FillPair (foregroundFill, backgroundFill); + + var lineCanvas = new LineCanvas + { + Fill = fillPair + }; + + // Act + lineCanvas.AddLine (new Point (0, 0), 5, Orientation.Horizontal, LineStyle.Single, lineColor); + var cellMap = lineCanvas.GetCellMap (); + + // Assert + foreach (var cell in cellMap.Values) + { + Assert.NotNull (cell); + Assert.Equal (foregroundColor, cell.Value.Attribute.Value.Foreground); + Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background); + } + } + + [Fact] + public void LineCanvas_IntersectingLinesUseFillCorrectly () + { + // Arrange + var foregroundColor = new Color (255, 0, 0); // Red + var backgroundColor = new Color (0, 0, 0); // Black + var foregroundFill = new SolidFill (foregroundColor); + var backgroundFill = new SolidFill (backgroundColor); + var fillPair = new FillPair (foregroundFill, backgroundFill); + + var lineCanvas = new LineCanvas + { + Fill = fillPair + }; + + // Act + lineCanvas.AddLine (new Point (0, 0), 5, Orientation.Horizontal, LineStyle.Single); + lineCanvas.AddLine (new Point (2, -2), 5, Orientation.Vertical, LineStyle.Single); + var cellMap = lineCanvas.GetCellMap (); + + // Assert + foreach (var cell in cellMap.Values) + { + Assert.NotNull (cell); + Assert.Equal (foregroundColor, cell.Value.Attribute.Value.Foreground); + Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background); + } + } + // TODO: Remove this and make all LineCanvas tests independent of View /// /// Creates a new into which a is rendered at From c62cd84b99d0df9a20148dfb7e4e782e555830a5 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 7 Jul 2024 21:44:49 +0100 Subject: [PATCH 30/48] Add xml comment --- Terminal.Gui/Drawing/Gradient.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Terminal.Gui/Drawing/Gradient.cs b/Terminal.Gui/Drawing/Gradient.cs index 01d7d2490..5091cc314 100644 --- a/Terminal.Gui/Drawing/Gradient.cs +++ b/Terminal.Gui/Drawing/Gradient.cs @@ -38,6 +38,9 @@ public enum GradientDirection /// public class Gradient { + /// + /// The discrete colors that will make up the . + /// public List Spectrum { get; private set; } private readonly bool _loop; private readonly List _stops; From b9a8c7d2630c9e18cbb9a955f32e159da3d5a704 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 7 Jul 2024 21:56:23 +0100 Subject: [PATCH 31/48] Fix GradientFill when not at origin --- Terminal.Gui/Drawing/GradientFill.cs | 7 +++-- UnitTests/Drawing/GradientFillTests.cs | 38 +++++++++++++++++++------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/Terminal.Gui/Drawing/GradientFill.cs b/Terminal.Gui/Drawing/GradientFill.cs index 31339ebe0..6763de885 100644 --- a/Terminal.Gui/Drawing/GradientFill.cs +++ b/Terminal.Gui/Drawing/GradientFill.cs @@ -18,7 +18,10 @@ public class GradientFill : IFill /// public GradientFill (Rectangle area, Gradient gradient, GradientDirection direction) { - _map = gradient.BuildCoordinateColorMapping (area.Height-1, area.Width-1, direction); + _map = gradient.BuildCoordinateColorMapping (area.Height - 1, area.Width - 1, direction) + .ToDictionary ( + kvp => new Point (kvp.Key.X + area.X, kvp.Key.Y + area.Y), + kvp => kvp.Value); } /// @@ -35,4 +38,4 @@ public class GradientFill : IFill } return new Color (0, 0, 0); // Default to black if point not found } -} \ No newline at end of file +} diff --git a/UnitTests/Drawing/GradientFillTests.cs b/UnitTests/Drawing/GradientFillTests.cs index bf0d4d0e2..e1f035d8d 100644 --- a/UnitTests/Drawing/GradientFillTests.cs +++ b/UnitTests/Drawing/GradientFillTests.cs @@ -1,5 +1,4 @@ - -namespace Terminal.Gui.DrawingTests; +namespace Terminal.Gui.DrawingTests; public class GradientFillTests { @@ -21,7 +20,7 @@ public class GradientFillTests } [Fact] - public void TestGradientFillCorners () + public void TestGradientFillCorners_AtOrigin () { var area = new Rectangle (0, 0, 10, 10); var gradientFill = new GradientFill (area, _gradient, GradientDirection.Diagonal); @@ -38,17 +37,36 @@ public class GradientFillTests var bottomRightColor = gradientFill.GetColor (bottomRight); // Expected colors - var expectedTopLeftColor = new Terminal.Gui.Color (255, 0, 0); // Red - var expectedBottomRightColor = new Terminal.Gui.Color (0, 0, 255); // Blue + var expectedTopLeftColor = new Color (255, 0, 0); // Red + var expectedBottomRightColor = new Color (0, 0, 255); // Blue Assert.Equal (expectedTopLeftColor, topLeftColor); Assert.Equal (expectedBottomRightColor, bottomRightColor); + } - // Additional checks can be added to verify the exact expected colors if known - Console.WriteLine ($"Top-left: {topLeftColor}"); - Console.WriteLine ($"Top-right: {topRightColor}"); - Console.WriteLine ($"Bottom-left: {bottomLeftColor}"); - Console.WriteLine ($"Bottom-right: {bottomRightColor}"); + [Fact] + public void TestGradientFillCorners_NotAtOrigin () + { + var area = new Rectangle (5, 5, 10, 10); + var gradientFill = new GradientFill (area, _gradient, GradientDirection.Diagonal); + + // Test the corners + var topLeft = new Point (5, 5); + var topRight = new Point (area.Right - 1, 5); + var bottomLeft = new Point (5, area.Bottom - 1); + var bottomRight = new Point (area.Right - 1, area.Bottom - 1); + + var topLeftColor = gradientFill.GetColor (topLeft); + var topRightColor = gradientFill.GetColor (topRight); + var bottomLeftColor = gradientFill.GetColor (bottomLeft); + var bottomRightColor = gradientFill.GetColor (bottomRight); + + // Expected colors + var expectedTopLeftColor = new Color (255, 0, 0); // Red + var expectedBottomRightColor = new Color (0, 0, 255); // Blue + + Assert.Equal (expectedTopLeftColor, topLeftColor); + Assert.Equal (expectedBottomRightColor, bottomRightColor); } [Fact] From 18e1956b7c48769c5663bad019da2505b05610d8 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 7 Jul 2024 22:12:49 +0100 Subject: [PATCH 32/48] Make gradients flow like a flow layout --- UICatalog/Scenarios/TextEffectsScenario.cs | 64 +++++++++++++++------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index c660ddb5c..b34e77321 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -128,6 +128,11 @@ public class TextEffectsScenario : Scenario internal class GradientsView : View { + private const int GradientWidth = 30; + private const int GradientHeight = 15; + private const int LabelHeight = 1; + private const int GradientWithLabelHeight = GradientHeight + LabelHeight + 1; // +1 for spacing + public override void OnDrawContent (Rectangle viewport) { base.OnDrawContent (viewport); @@ -137,25 +142,47 @@ internal class GradientsView : View int x = 2; int y = 3; - if (viewport.Height < 25) // Not enough space, render in a single line + var gradients = new List<(string Label, GradientDirection Direction)> { - DrawGradientArea (GradientDirection.Horizontal, x, y); - DrawGradientArea (GradientDirection.Horizontal, x, y); - DrawGradientArea (GradientDirection.Vertical, x + 32, y); - DrawGradientArea (GradientDirection.Radial, x + 64, y); - DrawGradientArea (GradientDirection.Diagonal, x + 96, y); - } - else // Enough space, render in two lines + ("Horizontal", GradientDirection.Horizontal), + ("Vertical", GradientDirection.Vertical), + ("Radial", GradientDirection.Radial), + ("Diagonal", GradientDirection.Diagonal) + }; + + foreach (var (label, direction) in gradients) { - DrawGradientArea (GradientDirection.Horizontal, x, y); - DrawGradientArea (GradientDirection.Vertical, x + 32, y); - DrawGradientArea (GradientDirection.Radial, x, y + 17); - DrawGradientArea (GradientDirection.Diagonal, x + 32, y + 17); + if (x + GradientWidth > viewport.Width) + { + x = 2; // Reset to left margin + y += GradientWithLabelHeight; // Move down to next row + } + + DrawLabeledGradientArea (label, direction, x, y); + x += GradientWidth + 2; // Move right for next gradient, +2 for spacing } } + private void DrawLabeledGradientArea (string label, GradientDirection direction, int xOffset, int yOffset) + { + DrawGradientArea (direction, xOffset, yOffset); + CenterText (label, xOffset, yOffset + GradientHeight); // Adjusted for text below the gradient + } + private void CenterText (string text, int xOffset, int yOffset) + { + if(yOffset+1 >= Viewport.Height) + { + // Not enough space for label + return; + } + var width = text.Length; + var x = xOffset + (GradientWidth - width) / 2; // Center the text within the gradient area width + Driver.SetAttribute (GetNormalColor ()); + Move (x, yOffset+1); + Driver.AddStr (text); + } private void DrawGradientArea (GradientDirection direction, int xOffset, int yOffset) { @@ -174,8 +201,8 @@ internal class GradientsView : View var radialGradient = new Gradient (stops, steps, loop: TextEffectsScenario.LoopingGradient); // Define the size of the rectangle - int maxRow = 15; // Adjusted to keep aspect ratio - int maxColumn = 30; + int maxRow = GradientHeight; // Adjusted to keep aspect ratio + int maxColumn = GradientWidth; // Build the coordinate-color mapping for a radial gradient var gradientMapping = radialGradient.BuildCoordinateColorMapping (maxRow, maxColumn, direction); @@ -236,11 +263,6 @@ internal class GradientsView : View private void SetColor (Color color) { - // Assuming AddRune is a method you have for drawing at specific positions - Application.Driver.SetAttribute ( - new Attribute ( - new Terminal.Gui.Color (color.R, color.G, color.B), - new Terminal.Gui.Color (color.R, color.G, color.B) - )); // Setting color based on RGB + Application.Driver.SetAttribute (new Attribute (color, color)); } -} \ No newline at end of file +} From 85a0ad16544fe04a9320785169a94b73a4e83332 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 8 Jul 2024 18:29:00 -0600 Subject: [PATCH 33/48] Diagnosing xunit failures in github action on ubuntu/macos (#3593) * Just ubuntu * windows & ubuntu * commented test out * uncommented test * back to ubuntu * deleted all Views tests * deleted all Views tests * VSTEST_DUMP_PATH * Revert "deleted all Views tests" This reverts commit 985f6143e6809705f9f25805da43a8cb16002d2c. * Deleted first half Views tests * Revert "Deleted first half Views tests" This reverts commit bff2484cd72df2d57b7c410b3f4c7ad4dcf3b616. * Deleted 2nd half Views tests * VSTEST_DUMP_PATH 2 * VSTEST_DUMP_PATH 3 * Revert "Deleted 2nd half Views tests" This reverts commit b1dbd79dc9f757a4367cf7b8bc5536c925028eb6. * Reapply "Deleted first half Views tests" This reverts commit 3e8e890b0371146695306974e708a85531f6fe0c. * Revert "Reapply "Deleted first half Views tests"" This reverts commit 731b50f392423dfc54fd61bd9d2989da32ef89b7. * ubuntu/mac * removed dupe test * removed dupe test * removed statusbar tests * Revert "removed statusbar tests" This reverts commit 889813143b019bfcc478744cd68297e0884ce2e9. * Fixed shortcut tests * windows, mac, linux * fail-fast: false temporarily * fail-fast: false temporarily * trying stuff * fixed quote error * fixed sed issue * Skip WindowDispose_CanFocusProblem * Skip SynchronizationContext_CreateCopy * mac * mac * mac * mac * mac * gsed * gsed * gsed * gsed * finally fixed! Hopefully. --- .github/workflows/dotnet-core.yml | 40 +++++---- .../Application/SynchronizatonContextTests.cs | 2 +- UnitTests/Input/KeyTests.cs | 4 - UnitTests/View/MouseTests.cs | 3 +- UnitTests/View/NavigationTests.cs | 2 +- UnitTests/Views/ShortcutTests.cs | 83 +++++++++---------- UnitTests/Views/StatusBarTests.cs | 13 --- 7 files changed, 68 insertions(+), 79 deletions(-) diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index da75b0fd5..5c30cd7cb 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -15,16 +15,18 @@ jobs: runs-on: ${{ matrix.os }} strategy: - # Turn on fail-fast once we have the tests running on all platforms - fail-fast: false + # Turn off fail-fast to let all runners run even if there are errors + fail-fast: true matrix: - os: [ windows-latest ] - #os: [ ubuntu-latest, windows-latest, macos-latest ] + os: [ ubuntu-latest, windows-latest, macos-latest ] timeout-minutes: 10 steps: - - - uses: actions/checkout@v4 + +# Build + + - name: Checkout code + uses: actions/checkout@v4 - name: Setup .NET Core uses: actions/setup-dotnet@v4 @@ -39,31 +41,37 @@ jobs: - name: Build Debug run: dotnet build --configuration Debug --no-restore - - name: Install sed on macOS and update xunit.runner.json +# Test + # Note: The --blame and VSTEST_DUMP_PATH stuff is needed to diagnose the test runner crashing on ubuntu/mac + # See https://github.com/microsoft/vstest/issues/2952 for why the --blame stuff below is needed. + # Without it, the test runner crashes on ubuntu (but not Windows or mac) + + - name: MacOS - Patch test runner settings to stop on fail if: runner.os == 'macOS' run: | - brew install gnu-sed - PATH="/opt/homebrew/opt/gnu-sed/libexec/gnubin:$PATH" - sed -i 's/"stopOnFail": false/"stopOnFail": true/g' UnitTests/xunit.runner.json + brew install gnu-sed + gsed -i 's/"stopOnFail": false/"stopOnFail": true/g' UnitTests/xunit.runner.json - - name: Update xunit.runner.json (Windows/Ubuntu) + - name: Windows/Linux - Patch test runner settings to stop on fail if: runner.os != 'macOS' run: | sed -i 's/"stopOnFail": false/"stopOnFail": true/g' UnitTests/xunit.runner.json - # See https://github.com/microsoft/vstest/issues/2952 for why the --blame stuff below is needed. - # Without it, the test runner crashes on ubuntu (but not Windows or mac) + - name: Set VSTEST_DUMP_PATH + shell: bash + run: echo "{VSTEST_DUMP_PATH}={logs/${{ runner.os }}/}" >> $GITHUB_ENV + - name: Test run: | - dotnet test --verbosity normal --collect:"XPlat Code Coverage" --settings UnitTests/coverlet.runsettings --diag:logs/logs.txt --blame --blame-crash --blame-hang --blame-hang-timeout 60s --blame-crash-collect-always + dotnet test --verbosity normal --collect:"XPlat Code Coverage" --settings UnitTests/coverlet.runsettings --diag:logs/${{ runner.os }}/logs.txt --blame --blame-crash --blame-hang --blame-hang-timeout 60s --blame-crash-collect-always - # mv -v UnitTests/TestResults/*/*.* UnitTests/TestResults/ + # mv -v UnitTests/TestResults/*/*.* UnitTests/TestResults/ - name: Upload Test Logs if: always() uses: actions/upload-artifact@v4 with: - name: test-logs-for-blame-debugging + name: test-logs-${{ runner.os }} path: | logs/ UnitTests/TestResults/ diff --git a/UnitTests/Application/SynchronizatonContextTests.cs b/UnitTests/Application/SynchronizatonContextTests.cs index f0dd036d3..fce4a3250 100644 --- a/UnitTests/Application/SynchronizatonContextTests.cs +++ b/UnitTests/Application/SynchronizatonContextTests.cs @@ -4,7 +4,7 @@ namespace Terminal.Gui.ApplicationTests; public class SyncrhonizationContextTests { - [Fact] + [Fact(Skip = "Causes ubuntu to crash in github action.")] public void SynchronizationContext_CreateCopy () { Application.Init (); diff --git a/UnitTests/Input/KeyTests.cs b/UnitTests/Input/KeyTests.cs index 15d123fb2..0a23a0fcc 100644 --- a/UnitTests/Input/KeyTests.cs +++ b/UnitTests/Input/KeyTests.cs @@ -61,7 +61,6 @@ public class KeyTests [InlineData ((KeyCode)'英', '英')] [InlineData ((KeyCode)'{', '{')] [InlineData ((KeyCode)'\'', '\'')] - [InlineData ((KeyCode)'\r', '\r')] [InlineData ((KeyCode)'ó', 'ó')] [InlineData ((KeyCode)'ó' | KeyCode.ShiftMask, 'ó')] [InlineData ((KeyCode)'Ó', 'Ó')] @@ -111,9 +110,6 @@ public class KeyTests [InlineData ('!', (KeyCode)'!')] [InlineData ('\r', KeyCode.Enter)] [InlineData ('\t', KeyCode.Tab)] -#pragma warning disable xUnit1025 // InlineData should be unique within the Theory it belongs to - [InlineData ('\r', (KeyCode)13)] -#pragma warning restore xUnit1025 // InlineData should be unique within the Theory it belongs to [InlineData ('\n', (KeyCode)10)] [InlineData ('ó', (KeyCode)'ó')] [InlineData ('Ó', (KeyCode)'Ó')] diff --git a/UnitTests/View/MouseTests.cs b/UnitTests/View/MouseTests.cs index 5c5453f3a..a0bef94f5 100644 --- a/UnitTests/View/MouseTests.cs +++ b/UnitTests/View/MouseTests.cs @@ -1,5 +1,4 @@ -using Terminal.Gui.ViewsTests; -using Xunit.Abstractions; +using Xunit.Abstractions; namespace Terminal.Gui.ViewTests; diff --git a/UnitTests/View/NavigationTests.cs b/UnitTests/View/NavigationTests.cs index 6c53aceb8..05dc30a1f 100644 --- a/UnitTests/View/NavigationTests.cs +++ b/UnitTests/View/NavigationTests.cs @@ -1534,7 +1534,7 @@ public class NavigationTests (ITestOutputHelper output) r.Dispose (); } - [Fact] + [Fact (Skip="Causes crash on Ubuntu in Github Action. Bogus test anyway.")] public void WindowDispose_CanFocusProblem () { // Arrange diff --git a/UnitTests/Views/ShortcutTests.cs b/UnitTests/Views/ShortcutTests.cs index 56e02a823..21143b11a 100644 --- a/UnitTests/Views/ShortcutTests.cs +++ b/UnitTests/Views/ShortcutTests.cs @@ -27,7 +27,7 @@ public class ShortcutTests [InlineData ("C", "H", KeyCode.Null, 6)] [InlineData ("", "H", KeyCode.K, 8)] [InlineData ("C", "H", KeyCode.K, 9)] - public void NaturalSize (string command, string help, Key key, int expectedWidth) + public void NaturalSize (string command, string help, KeyCode key, int expectedWidth) { var shortcut = new Shortcut { @@ -115,7 +115,7 @@ public class ShortcutTests [Theory] [InlineData (KeyCode.Null, "")] [InlineData (KeyCode.F1, "F1")] - public void KeyView_Text_Tracks_Key (Key key, string expected) + public void KeyView_Text_Tracks_Key (KeyCode key, string expected) { var shortcut = new Shortcut { @@ -316,6 +316,7 @@ public class ShortcutTests } [Theory] + // 0123456789 // " C 0 A " [InlineData (-1, 0)] @@ -332,7 +333,8 @@ public class ShortcutTests [AutoInitShutdown] public void MouseClick_Fires_Accept (int x, int expectedAccept) { - Toplevel current = new Toplevel (); + var current = new Toplevel (); + var shortcut = new Shortcut { Key = Key.A, @@ -343,14 +345,15 @@ public class ShortcutTests Application.Begin (current); - int accepted = 0; + var accepted = 0; shortcut.Accept += (s, e) => accepted++; - Application.OnMouseEvent (new MouseEvent () - { - Position = new Point (x, 0), - Flags = MouseFlags.Button1Clicked, - }); + Application.OnMouseEvent ( + new() + { + Position = new (x, 0), + Flags = MouseFlags.Button1Clicked + }); Assert.Equal (expectedAccept, accepted); @@ -358,6 +361,7 @@ public class ShortcutTests } [Theory] + // 0123456789 // " C 0 A " [InlineData (-1, 0, 0)] @@ -374,37 +378,37 @@ public class ShortcutTests [AutoInitShutdown] public void MouseClick_Button_CommandView_Fires_Accept (int x, int expectedAccept, int expectedButtonAccept) { - Toplevel current = new Toplevel (); + var current = new Toplevel (); + var shortcut = new Shortcut { Key = Key.A, - Text = "0", + Text = "0" }; - shortcut.CommandView = new Button () + + shortcut.CommandView = new Button { Title = "C", NoDecorations = true, - NoPadding = true, + NoPadding = true }; - int buttonAccepted = 0; - shortcut.CommandView.Accept += (s, e) => - { - buttonAccepted++; - }; + var buttonAccepted = 0; + shortcut.CommandView.Accept += (s, e) => { buttonAccepted++; }; current.Add (shortcut); Application.Begin (current); - int accepted = 0; + var accepted = 0; shortcut.Accept += (s, e) => accepted++; //Assert.True (shortcut.HasFocus); - Application.OnMouseEvent (new MouseEvent () - { - Position = new Point (x, 0), - Flags = MouseFlags.Button1Clicked, - }); + Application.OnMouseEvent ( + new() + { + Position = new (x, 0), + Flags = MouseFlags.Button1Clicked + }); Assert.Equal (expectedAccept, accepted); Assert.Equal (expectedButtonAccept, buttonAccepted); @@ -419,7 +423,6 @@ public class ShortcutTests [InlineData (true, KeyCode.Enter, 1)] [InlineData (true, KeyCode.Space, 0)] [InlineData (true, KeyCode.F1, 0)] - [InlineData (false, KeyCode.A, 1)] [InlineData (false, KeyCode.C, 1)] [InlineData (false, KeyCode.C | KeyCode.AltMask, 1)] @@ -429,7 +432,8 @@ public class ShortcutTests [AutoInitShutdown] public void KeyDown_Invokes_Accept (bool canFocus, KeyCode key, int expectedAccept) { - Toplevel current = new Toplevel (); + var current = new Toplevel (); + var shortcut = new Shortcut { Key = Key.A, @@ -442,7 +446,7 @@ public class ShortcutTests Application.Begin (current); Assert.Equal (canFocus, shortcut.HasFocus); - int accepted = 0; + var accepted = 0; shortcut.Accept += (s, e) => accepted++; Application.OnKeyDown (key); @@ -450,10 +454,8 @@ public class ShortcutTests Assert.Equal (expectedAccept, accepted); current.Dispose (); - } - [Theory] [InlineData (KeyCode.A, 1)] [InlineData (KeyCode.C, 1)] @@ -464,19 +466,20 @@ public class ShortcutTests [AutoInitShutdown] public void KeyDown_App_Scope_Invokes_Accept (KeyCode key, int expectedAccept) { - Toplevel current = new Toplevel (); + var current = new Toplevel (); + var shortcut = new Shortcut { Key = Key.A, KeyBindingScope = KeyBindingScope.Application, Text = "0", - Title = "_C", + Title = "_C" }; current.Add (shortcut); Application.Begin (current); - int accepted = 0; + var accepted = 0; shortcut.Accept += (s, e) => accepted++; Application.OnKeyDown (key); @@ -486,7 +489,6 @@ public class ShortcutTests current.Dispose (); } - [Theory] [InlineData (true, KeyCode.A, 1)] [InlineData (true, KeyCode.C, 1)] @@ -494,7 +496,6 @@ public class ShortcutTests [InlineData (true, KeyCode.Enter, 1)] [InlineData (true, KeyCode.Space, 0)] [InlineData (true, KeyCode.F1, 0)] - [InlineData (false, KeyCode.A, 1)] [InlineData (false, KeyCode.C, 1)] [InlineData (false, KeyCode.C | KeyCode.AltMask, 1)] @@ -504,7 +505,8 @@ public class ShortcutTests [AutoInitShutdown] public void KeyDown_Invokes_Action (bool canFocus, KeyCode key, int expectedAction) { - Toplevel current = new Toplevel (); + var current = new Toplevel (); + var shortcut = new Shortcut { Key = Key.A, @@ -517,7 +519,7 @@ public class ShortcutTests Application.Begin (current); Assert.Equal (canFocus, shortcut.HasFocus); - int action = 0; + var action = 0; shortcut.Action += () => action++; Application.OnKeyDown (key); @@ -525,7 +527,6 @@ public class ShortcutTests Assert.Equal (expectedAction, action); current.Dispose (); - } [Theory] @@ -535,7 +536,6 @@ public class ShortcutTests [InlineData (true, KeyCode.Enter, 1)] [InlineData (true, KeyCode.Space, 0)] [InlineData (true, KeyCode.F1, 0)] - [InlineData (false, KeyCode.A, 1)] [InlineData (false, KeyCode.C, 1)] [InlineData (false, KeyCode.C | KeyCode.AltMask, 1)] @@ -545,7 +545,8 @@ public class ShortcutTests [AutoInitShutdown] public void KeyDown_App_Scope_Invokes_Action (bool canFocus, KeyCode key, int expectedAction) { - Toplevel current = new Toplevel (); + var current = new Toplevel (); + var shortcut = new Shortcut { Key = Key.A, @@ -559,7 +560,7 @@ public class ShortcutTests Application.Begin (current); Assert.Equal (canFocus, shortcut.HasFocus); - int action = 0; + var action = 0; shortcut.Action += () => action++; Application.OnKeyDown (key); @@ -567,7 +568,5 @@ public class ShortcutTests Assert.Equal (expectedAction, action); current.Dispose (); - } - } diff --git a/UnitTests/Views/StatusBarTests.cs b/UnitTests/Views/StatusBarTests.cs index ce404f7b5..6f6f93061 100644 --- a/UnitTests/Views/StatusBarTests.cs +++ b/UnitTests/Views/StatusBarTests.cs @@ -81,19 +81,6 @@ public class StatusBarTests (ITestOutputHelper output) // top.Dispose (); //} - [Fact] - [AutoInitShutdown] - public void Redraw_Output () - { - } - - [Fact] - [AutoInitShutdown] - public void Redraw_Output_CTRLQ () - { - - } - [Fact] [AutoInitShutdown] public void Run_Action_With_Key_And_Mouse () From 019421072b4ebd2ea0a74637d1ebd8c7da5fd593 Mon Sep 17 00:00:00 2001 From: Tig Date: Tue, 9 Jul 2024 09:30:09 -0600 Subject: [PATCH 34/48] Latest packages --- ...nal.Gui.Analyzers.Internal.Debugging.csproj | 14 +++++++++----- ...erminal.Gui.Analyzers.Internal.Tests.csproj | 16 ++++++++++------ .../Terminal.Gui.Analyzers.Internal.csproj | 18 +++++++++++------- .../CommunityToolkitExample.csproj | 4 ++++ Example/Example.csproj | 3 +++ ReactiveExample/ReactiveExample.csproj | 5 ++++- Terminal.Gui/Terminal.Gui.csproj | 12 ++++++------ UICatalog/UICatalog.csproj | 7 +++++-- UnitTests/UnitTests.csproj | 13 ++++++++----- 9 files changed, 60 insertions(+), 32 deletions(-) diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Debugging/Terminal.Gui.Analyzers.Internal.Debugging.csproj b/Analyzers/Terminal.Gui.Analyzers.Internal.Debugging/Terminal.Gui.Analyzers.Internal.Debugging.csproj index 3cad5995b..5594dd0da 100644 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Debugging/Terminal.Gui.Analyzers.Internal.Debugging.csproj +++ b/Analyzers/Terminal.Gui.Analyzers.Internal.Debugging/Terminal.Gui.Analyzers.Internal.Debugging.csproj @@ -6,12 +6,12 @@ enable - - - - + + + + - + @@ -22,4 +22,8 @@ + + + + diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Terminal.Gui.Analyzers.Internal.Tests.csproj b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Terminal.Gui.Analyzers.Internal.Tests.csproj index e4e88bd61..42445111d 100644 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Terminal.Gui.Analyzers.Internal.Tests.csproj +++ b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Terminal.Gui.Analyzers.Internal.Tests.csproj @@ -14,13 +14,13 @@ - - - - + + + + - - + + all @@ -45,4 +45,8 @@ + + + + diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/Terminal.Gui.Analyzers.Internal.csproj b/Analyzers/Terminal.Gui.Analyzers.Internal/Terminal.Gui.Analyzers.Internal.csproj index 80d788ac3..06ce0794b 100644 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/Terminal.Gui.Analyzers.Internal.csproj +++ b/Analyzers/Terminal.Gui.Analyzers.Internal/Terminal.Gui.Analyzers.Internal.csproj @@ -47,17 +47,21 @@ - - - - + + + + - - - + + + + + + + diff --git a/CommunityToolkitExample/CommunityToolkitExample.csproj b/CommunityToolkitExample/CommunityToolkitExample.csproj index fdb41dfcd..44b28218e 100644 --- a/CommunityToolkitExample/CommunityToolkitExample.csproj +++ b/CommunityToolkitExample/CommunityToolkitExample.csproj @@ -16,4 +16,8 @@ + + + + diff --git a/Example/Example.csproj b/Example/Example.csproj index 4bb8cc0bc..47fafd9a1 100644 --- a/Example/Example.csproj +++ b/Example/Example.csproj @@ -13,4 +13,7 @@ + + + \ No newline at end of file diff --git a/ReactiveExample/ReactiveExample.csproj b/ReactiveExample/ReactiveExample.csproj index 54f46753a..42ddcc91d 100644 --- a/ReactiveExample/ReactiveExample.csproj +++ b/ReactiveExample/ReactiveExample.csproj @@ -12,10 +12,13 @@ - + + + + \ No newline at end of file diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj index 1959d51d3..b436834a1 100644 --- a/Terminal.Gui/Terminal.Gui.csproj +++ b/Terminal.Gui/Terminal.Gui.csproj @@ -50,14 +50,14 @@ - - - - + + + + - - + + all diff --git a/UICatalog/UICatalog.csproj b/UICatalog/UICatalog.csproj index e461d7c2f..1295f4f5a 100644 --- a/UICatalog/UICatalog.csproj +++ b/UICatalog/UICatalog.csproj @@ -29,9 +29,9 @@ - + - + @@ -46,4 +46,7 @@ + + + \ No newline at end of file diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index 716afb0fa..d64e3c63a 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -29,12 +29,12 @@ true - - - - + + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -57,6 +57,9 @@ + + + False From c981eefed8f9bcf9e44990af67fcf5c5b982d021 Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 9 Jul 2024 16:54:00 +0100 Subject: [PATCH 35/48] Fix hanging xml comment --- Terminal.Gui/Drawing/Gradient.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Terminal.Gui/Drawing/Gradient.cs b/Terminal.Gui/Drawing/Gradient.cs index 5091cc314..622f93239 100644 --- a/Terminal.Gui/Drawing/Gradient.cs +++ b/Terminal.Gui/Drawing/Gradient.cs @@ -34,7 +34,9 @@ public enum GradientDirection } /// -/// Describes +/// Describes a of colors that can be combined +/// to make a color gradient. Use +/// to create into gradient fill area maps. /// public class Gradient { From 1ba84ded9a800206a1724a92980d634522c19bc7 Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 9 Jul 2024 16:59:21 +0100 Subject: [PATCH 36/48] Fix bad namespace --- Terminal.Gui/Drawing/FillPair.cs | 2 -- Terminal.Gui/Drawing/SolidFill.cs | 2 +- UICatalog/Scenarios/TextEffectsScenario.cs | 2 -- UnitTests/Drawing/FillPairTests.cs | 1 - UnitTests/Drawing/LineCanvasTests.cs | 1 - UnitTests/Drawing/SolidFillTests.cs | 2 -- 6 files changed, 1 insertion(+), 9 deletions(-) diff --git a/Terminal.Gui/Drawing/FillPair.cs b/Terminal.Gui/Drawing/FillPair.cs index 648dbb40f..5a7caf6d8 100644 --- a/Terminal.Gui/Drawing/FillPair.cs +++ b/Terminal.Gui/Drawing/FillPair.cs @@ -1,6 +1,4 @@  -using Terminal.Gui.Drawing; - namespace Terminal.Gui; diff --git a/Terminal.Gui/Drawing/SolidFill.cs b/Terminal.Gui/Drawing/SolidFill.cs index 67e1bdf1f..d456df8a9 100644 --- a/Terminal.Gui/Drawing/SolidFill.cs +++ b/Terminal.Gui/Drawing/SolidFill.cs @@ -1,4 +1,4 @@ -namespace Terminal.Gui.Drawing; +namespace Terminal.Gui; /// diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index b34e77321..71c75cfca 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -4,8 +4,6 @@ using System.Text; using System.Threading; using Terminal.Gui; -using Terminal.Gui.Drawing; - namespace UICatalog.Scenarios; [ScenarioMetadata ("Text Effects", "Text Effects.")] diff --git a/UnitTests/Drawing/FillPairTests.cs b/UnitTests/Drawing/FillPairTests.cs index bbe8b6563..34953a4a4 100644 --- a/UnitTests/Drawing/FillPairTests.cs +++ b/UnitTests/Drawing/FillPairTests.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using Terminal.Gui.Drawing; namespace Terminal.Gui.DrawingTests; diff --git a/UnitTests/Drawing/LineCanvasTests.cs b/UnitTests/Drawing/LineCanvasTests.cs index d160b5420..05df06bcf 100644 --- a/UnitTests/Drawing/LineCanvasTests.cs +++ b/UnitTests/Drawing/LineCanvasTests.cs @@ -1,5 +1,4 @@ using System.Text; -using Terminal.Gui.Drawing; using Xunit.Abstractions; namespace Terminal.Gui.DrawingTests; diff --git a/UnitTests/Drawing/SolidFillTests.cs b/UnitTests/Drawing/SolidFillTests.cs index 50eaa9f9f..9e70103d5 100644 --- a/UnitTests/Drawing/SolidFillTests.cs +++ b/UnitTests/Drawing/SolidFillTests.cs @@ -1,6 +1,4 @@  -using Terminal.Gui.Drawing; - namespace Terminal.Gui.DrawingTests; public class SolidFillTests From 918bd3ae568b21d52991e80e05721a51bc1c879a Mon Sep 17 00:00:00 2001 From: Tig Date: Tue, 9 Jul 2024 11:30:48 -0600 Subject: [PATCH 37/48] Removed orig --- GitVersion.yml.orig | 103 -------------------------------------------- 1 file changed, 103 deletions(-) delete mode 100644 GitVersion.yml.orig diff --git a/GitVersion.yml.orig b/GitVersion.yml.orig deleted file mode 100644 index 345f7237f..000000000 --- a/GitVersion.yml.orig +++ /dev/null @@ -1,103 +0,0 @@ -mode: ContinuousDeployment -tag-prefix: '[vV]' -continuous-delivery-fallback-tag: dev -branches: - develop: - mode: ContinuousDeployment - tag: dev - regex: develop - source-branches: - - main - pre-release-weight: 100 - - v2_develop: - mode: ContinuousDeployment - tag: dev - regex: ^v2_develop?[/-] -<<<<<<< HEAD - #is-release-branch: true -======= ->>>>>>> v2_develop - tracks-release-branches: true - is-source-branch-for: ['v2_release'] - source-branches: [] - - v2_release: - mode: ContinuousDeployment - tag: prealpha - regex: v2_release - is-release-branch: true - source-branches: ['v2_develop'] - - pull-request: - mode: ContinuousDeployment - tag: PullRequest.{BranchName} - increment: Inherit - tag-number-pattern: '[/-](?\d+)' - regex: ^(pull|pull\-requests|pr)[/-] - source-branches: - - develop - - main - - release - - v2_develop - - v2_release - - feature - - support - - hotfix - pre-release-weight: 30000 - -ignore: - sha: [] - - -# next-version: 2.0.0 -# mode: ContinuousDeployment -# tag-prefix: '[vV]' -# continuous-delivery-fallback-tag: 'pre' -# branches: -# # v1_develop: -# # mode: ContinuousDeployment -# # tag: pre -# # regex: ^v1_develop?[/-] -# # is-release-branch: false -# # source-branches: -# # - v1 -# # v1: -# # tag: rc -# # increment: Patch -# # regex: ^v2?[/-] -# # is-release-branch: false -# # source-branches: [] -# # is-mainline: true - -# v2_develop: -# mode: ContinuousDeployment -# tag: pre -# regex: ^v2_develop?[/-] -# is-release-branch: true -# tracks-release-branches: true -# is-source-branch-for: ['v2'] -# source-branches: [] -# v2: -# mode: ContinuousDeployment -# is-release-branch: false -# tag: alpha -# increment: Patch -# regex: ^v2?[/-] -# source-branches: ['v2_develop'] - -# # feature: -# # tag: useBranchName -# # regex: ^features?[/-] -# # source-branches: -# # - v1 -# # - v1_develop -# # - v2 -# # - v2_develop - -# pull-request: -# tag: PullRequest.{BranchName} -# increment: Inherit -# ignore: -# sha: [] -# merge-message-formats: {} From 47d3762ee2a5329dce24eaf1d29d26cd6a153804 Mon Sep 17 00:00:00 2001 From: Tig Date: Tue, 9 Jul 2024 11:31:59 -0600 Subject: [PATCH 38/48] Fixed .gitignore --- .gitignore | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index bbfa8e16e..cca1f4801 100644 --- a/.gitignore +++ b/.gitignore @@ -50,9 +50,9 @@ UnitTests/TestResults TestResults # git merge files -.orig -.theirs -.ours +*.orig +*.theirs +*.ours demo.* From 65efddbc9ff1a7f080579c6bfec3c02407f434ff Mon Sep 17 00:00:00 2001 From: Tig Date: Tue, 9 Jul 2024 13:26:19 -0600 Subject: [PATCH 39/48] Added Border Settings. Enabled BorderSettings.Gradient --- Terminal.Gui/View/Adornment/Border.cs | 146 ++++++++++++------ Terminal.Gui/View/Adornment/BorderSettings.cs | 26 ++++ Terminal.Gui/View/Adornment/Margin.cs | 2 +- Terminal.Gui/Views/Shortcut.cs | 2 +- UICatalog/Scenarios/BorderEditor.cs | 58 +++++-- 5 files changed, 173 insertions(+), 61 deletions(-) create mode 100644 Terminal.Gui/View/Adornment/BorderSettings.cs diff --git a/Terminal.Gui/View/Adornment/Border.cs b/Terminal.Gui/View/Adornment/Border.cs index 39d98f635..1ed64abca 100644 --- a/Terminal.Gui/View/Adornment/Border.cs +++ b/Terminal.Gui/View/Adornment/Border.cs @@ -1,3 +1,5 @@ +using Terminal.Gui.Drawing; + namespace Terminal.Gui; /// The Border for a . @@ -78,7 +80,7 @@ public class Border : Adornment if ((Parent?.Arrangement & ViewArrangement.Movable) != 0) { HighlightStyle |= HighlightStyle.Hover; - } + } #endif base.BeginInit (); @@ -149,31 +151,32 @@ public class Border : Adornment } } - Rectangle GetBorderRectangle (Rectangle screenRect) + private Rectangle GetBorderRectangle (Rectangle screenRect) { return new ( - screenRect.X + Math.Max (0, Thickness.Left - 1), - screenRect.Y + Math.Max (0, Thickness.Top - 1), - Math.Max ( - 0, - screenRect.Width - - Math.Max ( - 0, - Math.Max (0, Thickness.Left - 1) - + Math.Max (0, Thickness.Right - 1) - ) - ), - Math.Max ( - 0, - screenRect.Height - - Math.Max ( - 0, - Math.Max (0, Thickness.Top - 1) - + Math.Max (0, Thickness.Bottom - 1) - ) - ) - ); + screenRect.X + Math.Max (0, Thickness.Left - 1), + screenRect.Y + Math.Max (0, Thickness.Top - 1), + Math.Max ( + 0, + screenRect.Width + - Math.Max ( + 0, + Math.Max (0, Thickness.Left - 1) + + Math.Max (0, Thickness.Right - 1) + ) + ), + Math.Max ( + 0, + screenRect.Height + - Math.Max ( + 0, + Math.Max (0, Thickness.Top - 1) + + Math.Max (0, Thickness.Bottom - 1) + ) + ) + ); } + /// /// Sets the style of the border by changing the . This is a helper API for setting the /// to (1,1,1,1) and setting the line style of the views that comprise the border. If @@ -196,21 +199,22 @@ public class Border : Adornment set => _lineStyle = value; } - private bool _showTitle = true; + private BorderSettings _settings = BorderSettings.Title; /// - /// Gets or sets whether the title should be shown. The default is . + /// Gets or sets the settings for the border. /// - public bool ShowTitle + public BorderSettings Settings { - get => _showTitle; + get => _settings; set { - if (value == _showTitle) + if (value == _settings) { return; } - _showTitle = value; + + _settings = value; Parent?.SetNeedsDisplay (); } @@ -225,6 +229,7 @@ public class Border : Adornment if (!Parent.Arrangement.HasFlag (ViewArrangement.Movable)) { e.Cancel = true; + return; } @@ -235,9 +240,9 @@ public class Border : Adornment _savedForeColor = ColorScheme.Normal.Foreground; } - ColorScheme cs = new ColorScheme (ColorScheme) + var cs = new ColorScheme (ColorScheme) { - Normal = new Attribute (ColorScheme.Normal.Foreground.GetHighlightColor (), ColorScheme.Normal.Background) + Normal = new (ColorScheme.Normal.Foreground.GetHighlightColor (), ColorScheme.Normal.Background) }; ColorScheme = cs; } @@ -254,12 +259,13 @@ public class Border : Adornment if (e.NewValue == HighlightStyle.None && _savedForeColor.HasValue) { - ColorScheme cs = new ColorScheme (ColorScheme) + var cs = new ColorScheme (ColorScheme) { - Normal = new Attribute (_savedForeColor.Value, ColorScheme.Normal.Background) + Normal = new (_savedForeColor.Value, ColorScheme.Normal.Background) }; ColorScheme = cs; } + Parent?.SetNeedsDisplay (); e.Cancel = true; } @@ -267,7 +273,7 @@ public class Border : Adornment private Point? _dragPosition; private Point _startGrabPoint; - /// + /// protected internal override bool OnMouseEvent (MouseEvent mouseEvent) { if (base.OnMouseEvent (mouseEvent)) @@ -322,16 +328,17 @@ public class Border : Adornment _dragPosition = mouseEvent.Position; - Point parentLoc = Parent.SuperView?.ScreenToViewport (new (mouseEvent.ScreenPosition.X, mouseEvent.ScreenPosition.Y)) ?? mouseEvent.ScreenPosition; + Point parentLoc = Parent.SuperView?.ScreenToViewport (new (mouseEvent.ScreenPosition.X, mouseEvent.ScreenPosition.Y)) + ?? mouseEvent.ScreenPosition; GetLocationEnsuringFullVisibility ( - Parent, - parentLoc.X - _startGrabPoint.X, - parentLoc.Y - _startGrabPoint.Y, - out int nx, - out int ny, - out _ - ); + Parent, + parentLoc.X - _startGrabPoint.X, + parentLoc.Y - _startGrabPoint.Y, + out int nx, + out int ny, + out _ + ); Parent.X = nx; Parent.Y = ny; @@ -352,7 +359,6 @@ public class Border : Adornment return false; } - /// protected override void Dispose (bool disposing) { @@ -403,7 +409,7 @@ public class Border : Adornment // ...thickness extends outward (border/title is always as far in as possible) // PERF: How about a call to Rectangle.Offset? - var borderBounds = GetBorderRectangle (screenBounds); + Rectangle borderBounds = GetBorderRectangle (screenBounds); int topTitleLineY = borderBounds.Y; int titleY = borderBounds.Y; var titleBarsLength = 0; // the little vertical thingies @@ -421,7 +427,7 @@ public class Border : Adornment int sideLineLength = borderBounds.Height; bool canDrawBorder = borderBounds is { Width: > 0, Height: > 0 }; - if (ShowTitle) + if (Settings.FastHasFlags (BorderSettings.Title)) { if (Thickness.Top == 2) { @@ -453,9 +459,10 @@ public class Border : Adornment } } - if (canDrawBorder && Thickness.Top > 0 && maxTitleWidth > 0 && ShowTitle && !string.IsNullOrEmpty (Parent?.Title)) + if (canDrawBorder && Thickness.Top > 0 && maxTitleWidth > 0 && Settings.FastHasFlags (BorderSettings.Title) && !string.IsNullOrEmpty (Parent?.Title)) { - var focus = Parent.GetNormalColor (); + Attribute focus = Parent.GetNormalColor (); + if (Parent.SuperView is { } && Parent.SuperView?.Subviews!.Count (s => s.CanFocus) > 1) { // Only use focus color if there are multiple focusable views @@ -492,7 +499,7 @@ public class Border : Adornment { // ╔╡Title╞═════╗ // ╔╡╞═════╗ - if (borderBounds.Width < 4 || !ShowTitle || string.IsNullOrEmpty (Parent?.Title)) + if (borderBounds.Width < 4 || !Settings.FastHasFlags (BorderSettings.Title) || string.IsNullOrEmpty (Parent?.Title)) { // ╔╡╞╗ should be ╔══╗ lc.AddLine ( @@ -631,7 +638,7 @@ public class Border : Adornment Driver.SetAttribute (prevAttr); // TODO: This should be moved to LineCanvas as a new BorderStyle.Ruler - if (View.Diagnostics.HasFlag (ViewDiagnosticFlags.Ruler)) + if (Diagnostics.HasFlag (ViewDiagnosticFlags.Ruler)) { // Top var hruler = new Ruler { Length = screenBounds.Width, Orientation = Orientation.Horizontal }; @@ -642,7 +649,7 @@ public class Border : Adornment } // Redraw title - if (drawTop && maxTitleWidth > 0 && ShowTitle) + if (drawTop && maxTitleWidth > 0 && Settings.FastHasFlags (BorderSettings.Title)) { Parent.TitleTextFormatter.Draw ( new (borderBounds.X + 2, titleY, maxTitleWidth, 1), @@ -670,6 +677,45 @@ public class Border : Adornment vruler.Draw (new (screenBounds.X + screenBounds.Width - 1, screenBounds.Y + 1), 1); } } + + // TODO: This should not be done on each draw? + if (Settings.FastHasFlags (BorderSettings.Gradient)) + { + SetupGradientLineCanvas (lc, screenBounds); + } + else + { + lc.Fill = null; + } } } + + private void SetupGradientLineCanvas (LineCanvas lc, Rectangle rect) + { + GetAppealingGradientColors (out List stops, out List steps); + + var g = new Gradient (stops, steps); + + var fore = new GradientFill (rect, g, GradientDirection.Diagonal); + var back = new SolidFill (GetNormalColor ().Background); + + lc.Fill = new (fore, back); + } + + private static void GetAppealingGradientColors (out List stops, out List steps) + { + // Define the colors of the gradient stops with more appealing colors + stops = new() + { + new (0, 128, 255), // Bright Blue + new (0, 255, 128), // Bright Green + new (255, 255), // Bright Yellow + new (255, 128), // Bright Orange + new (255, 0, 128) // Bright Pink + }; + + // Define the number of steps between each color for smoother transitions + // If we pass only a single value then it will assume equal steps between all pairs + steps = new() { 15 }; + } } diff --git a/Terminal.Gui/View/Adornment/BorderSettings.cs b/Terminal.Gui/View/Adornment/BorderSettings.cs new file mode 100644 index 000000000..5829d1ed6 --- /dev/null +++ b/Terminal.Gui/View/Adornment/BorderSettings.cs @@ -0,0 +1,26 @@ +using Terminal.Gui.Analyzers.Internal.Attributes; + +namespace Terminal.Gui; + +/// +/// Determines the settings for . +/// +[Flags] +[GenerateEnumExtensionMethods (FastHasFlags = true)] +public enum BorderSettings +{ + /// + /// No settings. + /// + None = 0, + + /// + /// Show the title. + /// + Title = 1, + + /// + /// Use to draw the border. + /// + Gradient = 2, +} diff --git a/Terminal.Gui/View/Adornment/Margin.cs b/Terminal.Gui/View/Adornment/Margin.cs index 2e1ea5760..046965e32 100644 --- a/Terminal.Gui/View/Adornment/Margin.cs +++ b/Terminal.Gui/View/Adornment/Margin.cs @@ -223,7 +223,7 @@ public class Margin : Adornment if (ShadowStyle != ShadowStyle.None && _rightShadow is { } && _bottomShadow is { }) { _rightShadow.Y = Parent.Border.Thickness.Top > 0 - ? Parent.Border.Thickness.Top - (Parent.Border.Thickness.Top > 2 && Parent.Border.ShowTitle ? 1 : 0) + ? Parent.Border.Thickness.Top - (Parent.Border.Thickness.Top > 2 && Parent.Border.Settings.FastHasFlags (BorderSettings.Title) ? 1 : 0) : 1; _bottomShadow.X = Parent.Border.Thickness.Left > 0 ? Parent.Border.Thickness.Left : 1; } diff --git a/Terminal.Gui/Views/Shortcut.cs b/Terminal.Gui/Views/Shortcut.cs index 802631d52..7ddbe7c5a 100644 --- a/Terminal.Gui/Views/Shortcut.cs +++ b/Terminal.Gui/Views/Shortcut.cs @@ -104,7 +104,7 @@ public class Shortcut : View void OnInitialized (object sender, EventArgs e) { SuperViewRendersLineCanvas = true; - Border.ShowTitle = false; + Border.Settings &= ~BorderSettings.Title; ShowHide (); diff --git a/UICatalog/Scenarios/BorderEditor.cs b/UICatalog/Scenarios/BorderEditor.cs index a5ccae212..5e0a6e77a 100644 --- a/UICatalog/Scenarios/BorderEditor.cs +++ b/UICatalog/Scenarios/BorderEditor.cs @@ -9,29 +9,30 @@ public class BorderEditor : AdornmentEditor { private CheckBox _ckbTitle; private RadioGroup _rbBorderStyle; + private CheckBox _ckbGradient; public BorderEditor () { Title = "_Border"; Initialized += BorderEditor_Initialized; AdornmentChanged += BorderEditor_AdornmentChanged; - } private void BorderEditor_AdornmentChanged (object sender, EventArgs e) { - _ckbTitle.State = ((Border)AdornmentToEdit).ShowTitle ? CheckState.Checked : CheckState.UnChecked; + _ckbTitle.State = ((Border)AdornmentToEdit).Settings.FastHasFlags (BorderSettings.Title) ? CheckState.Checked : CheckState.UnChecked; _rbBorderStyle.SelectedItem = (int)((Border)AdornmentToEdit).LineStyle; + _ckbGradient.State = ((Border)AdornmentToEdit).Settings.FastHasFlags (BorderSettings.Gradient) ? CheckState.Checked : CheckState.UnChecked; } private void BorderEditor_Initialized (object sender, EventArgs e) { - List borderStyleEnum = Enum.GetValues (typeof (LineStyle)).Cast ().ToList (); - _rbBorderStyle = new RadioGroup + _rbBorderStyle = new() { X = 0, + // BUGBUG: Hack until dimauto is working properly Y = Pos.Bottom (Subviews [^1]), Width = Dim.Width (Subviews [^2]) + Dim.Width (Subviews [^1]) - 1, @@ -46,21 +47,34 @@ public class BorderEditor : AdornmentEditor _rbBorderStyle.SelectedItemChanged += OnRbBorderStyleOnSelectedItemChanged; - _ckbTitle = new CheckBox + _ckbTitle = new() { X = 0, Y = Pos.Bottom (_rbBorderStyle), State = CheckState.Checked, SuperViewRendersLineCanvas = true, - Text = "Show Title", + Text = "Title", Enabled = AdornmentToEdit is { } }; - _ckbTitle.Toggle += OnCkbTitleOnToggle; Add (_ckbTitle); + _ckbGradient = new () + { + X = 0, + Y = Pos.Bottom (_ckbTitle), + + State = CheckState.Checked, + SuperViewRendersLineCanvas = true, + Text = "Gradient", + Enabled = AdornmentToEdit is { } + }; + + _ckbGradient.Toggle += OnCkbGradientOnToggle; + Add (_ckbGradient); + return; void OnRbBorderStyleOnSelectedItemChanged (object s, SelectedItemChangedArgs e) @@ -81,6 +95,32 @@ public class BorderEditor : AdornmentEditor LayoutSubviews (); } - void OnCkbTitleOnToggle (object sender, CancelEventArgs args) { ((Border)AdornmentToEdit).ShowTitle = args.NewValue == CheckState.Checked; } + void OnCkbTitleOnToggle (object sender, CancelEventArgs args) + { + if (args.NewValue == CheckState.Checked) + + { + ((Border)AdornmentToEdit).Settings |= BorderSettings.Title; + } + else + + { + ((Border)AdornmentToEdit).Settings &= ~BorderSettings.Title; + } + } + + void OnCkbGradientOnToggle (object sender, CancelEventArgs args) + { + if (args.NewValue == CheckState.Checked) + + { + ((Border)AdornmentToEdit).Settings |= BorderSettings.Gradient; + } + else + + { + ((Border)AdornmentToEdit).Settings &= ~BorderSettings.Gradient; + } + } } -} \ No newline at end of file +} From d0f1280f293ae5d63bd0cff442c51655a8e481e4 Mon Sep 17 00:00:00 2001 From: Tig Date: Tue, 9 Jul 2024 13:37:08 -0600 Subject: [PATCH 40/48] Code cleanup --- Terminal.Gui/Drawing/FillPair.cs | 27 +-- Terminal.Gui/Drawing/Gradient.cs | 138 ++++++------ Terminal.Gui/Drawing/GradientFill.cs | 27 +-- Terminal.Gui/Drawing/IFill.cs | 9 +- Terminal.Gui/Drawing/LineCanvas.cs | 29 ++- Terminal.Gui/Drawing/SolidFill.cs | 23 +- Terminal.Gui/Drawing/StraightLine.cs | 71 +++--- UICatalog/Scenarios/TextEffectsScenario.cs | 207 +++++++++--------- UnitTests/Drawing/FillPairTests.cs | 11 +- UnitTests/Drawing/GradientFillTests.cs | 52 ++--- UnitTests/Drawing/GradientTests.cs | 162 +++++++------- UnitTests/Drawing/LineCanvasTests.cs | 96 ++++---- UnitTests/Drawing/SolidFillTests.cs | 9 +- .../Drawing/StraightLineExtensionsTests.cs | 25 +-- UnitTests/Drawing/StraightLineTests.cs | 9 +- 15 files changed, 440 insertions(+), 455 deletions(-) diff --git a/Terminal.Gui/Drawing/FillPair.cs b/Terminal.Gui/Drawing/FillPair.cs index 648dbb40f..2150f778d 100644 --- a/Terminal.Gui/Drawing/FillPair.cs +++ b/Terminal.Gui/Drawing/FillPair.cs @@ -1,18 +1,14 @@ - -using Terminal.Gui.Drawing; - -namespace Terminal.Gui; - +namespace Terminal.Gui; /// -/// Describes a pair of which cooperate in creating -/// . One gives foreground color while other gives background. +/// Describes a pair of which cooperate in creating +/// . One gives foreground color while other gives background. /// public class FillPair { /// - /// Creates a new instance using the provided fills for foreground and background - /// color when assembling . + /// Creates a new instance using the provided fills for foreground and background + /// color when assembling . /// /// /// @@ -23,26 +19,23 @@ public class FillPair } /// - /// The fill which provides point based foreground color. + /// The fill which provides point based foreground color. /// public IFill Foreground { get; init; } /// - /// The fill which provides point based background color. + /// The fill which provides point based background color. /// public IFill Background { get; init; } /// - /// Returns the color pair (foreground+background) to use when rendering - /// a rune at the given . + /// Returns the color pair (foreground+background) to use when rendering + /// a rune at the given . /// /// /// public Attribute GetAttribute (Point point) { - return new Attribute ( - Foreground.GetColor (point), - Background.GetColor (point) - ); + return new (Foreground.GetColor (point), Background.GetColor (point)); } } diff --git a/Terminal.Gui/Drawing/Gradient.cs b/Terminal.Gui/Drawing/Gradient.cs index 5091cc314..fa1cb0482 100644 --- a/Terminal.Gui/Drawing/Gradient.cs +++ b/Terminal.Gui/Drawing/Gradient.cs @@ -2,59 +2,57 @@ namespace Terminal.Gui; -using System; -using System.Collections.Generic; -using System.Linq; - /// -/// Describes the pattern that a results in e.g. , etc +/// Describes the pattern that a results in e.g. , +/// etc /// public enum GradientDirection { /// - /// Color varies along Y axis but is constant on X axis. + /// Color varies along Y axis but is constant on X axis. /// Vertical, /// - /// Color varies along X axis but is constant on Y axis. + /// Color varies along X axis but is constant on Y axis. /// Horizontal, - /// - /// Color varies by distance from center (i.e. in circular ripples) + /// Color varies by distance from center (i.e. in circular ripples) /// Radial, /// - /// Color varies by X and Y axis (i.e. a slanted gradient) + /// Color varies by X and Y axis (i.e. a slanted gradient) /// Diagonal } /// -/// Describes +/// Describes /// public class Gradient { /// - /// The discrete colors that will make up the . + /// The discrete colors that will make up the . /// - public List Spectrum { get; private set; } + public List Spectrum { get; } + private readonly bool _loop; private readonly List _stops; private readonly List _steps; - /// - /// Creates a new instance of the class which hosts a - /// of colors including all and interpolated colors - /// between each corresponding pair. + /// Creates a new instance of the class which hosts a + /// of colors including all and interpolated colors + /// between each corresponding pair. /// /// The colors to use in the spectrum (N) - /// The number of colors to generate between each pair (must be N-1 numbers). - /// If only one step is passed then it is assumed to be the same distance for all pairs. + /// + /// The number of colors to generate between each pair (must be N-1 numbers). + /// If only one step is passed then it is assumed to be the same distance for all pairs. + /// /// True to duplicate the first stop and step so that the gradient repeats itself /// public Gradient (IEnumerable stops, IEnumerable steps, bool loop = false) @@ -71,13 +69,13 @@ public class Gradient // If multiple colors and only 1 step assume same distance applies to all steps if (_stops.Count > 2 && _steps.Count == 1) { - _steps = Enumerable.Repeat (_steps.Single (),_stops.Count() - 1).ToList(); + _steps = Enumerable.Repeat (_steps.Single (), _stops.Count () - 1).ToList (); } if (_steps.Any (step => step < 1)) { throw new ArgumentException ("Steps must be greater than 0."); - } + } if (_steps.Count != _stops.Count - 1) { @@ -89,11 +87,13 @@ public class Gradient } /// - /// Returns the color to use at the given part of the spectrum + /// Returns the color to use at the given part of the spectrum /// - /// Proportion of the way through the spectrum, must be between - /// 0 and 1 (inclusive). Returns the last color if is - /// . + /// + /// Proportion of the way through the spectrum, must be between + /// 0 and 1 (inclusive). Returns the last color if is + /// . + /// /// /// public Color GetColorAtFraction (double fraction) @@ -103,30 +103,32 @@ public class Gradient return Spectrum.Last (); } - if (fraction < 0 || fraction > 1) + if (fraction is < 0 or > 1) { - throw new ArgumentOutOfRangeException (nameof (fraction), "Fraction must be between 0 and 1."); + throw new ArgumentOutOfRangeException (nameof (fraction), @"Fraction must be between 0 and 1."); } - int index = (int)(fraction * (Spectrum.Count - 1)); + var index = (int)(fraction * (Spectrum.Count - 1)); + return Spectrum [index]; } private List GenerateGradient (IEnumerable steps) { - List gradient = new List (); + List gradient = new (); if (_stops.Count == 1) { - for (int i = 0; i < steps.Sum (); i++) + for (var i = 0; i < steps.Sum (); i++) { gradient.Add (_stops [0]); } + return gradient; } - var stopsToUse = _stops.ToList (); - var stepsToUse = _steps.ToList (); + List stopsToUse = _stops.ToList (); + List stepsToUse = _steps.ToList (); if (_loop) { @@ -135,9 +137,9 @@ public class Gradient } var colorPairs = stopsToUse.Zip (stopsToUse.Skip (1), (start, end) => new { start, end }); - var stepsList = stepsToUse; + List stepsList = stepsToUse; - foreach (var (colorPair, thesteps) in colorPairs.Zip (stepsList, (pair, step) => (pair, step))) + foreach ((var colorPair, int thesteps) in colorPairs.Zip (stepsList, (pair, step) => (pair, step))) { gradient.AddRange (InterpolateColors (colorPair.start, colorPair.end, thesteps)); } @@ -145,28 +147,29 @@ public class Gradient return gradient; } - private IEnumerable InterpolateColors (Color start, Color end, int steps) + private static IEnumerable InterpolateColors (Color start, Color end, int steps) { - for (int step = 0; step < steps; step++) + for (var step = 0; step < steps; step++) { double fraction = (double)step / steps; - int r = (int)(start.R + fraction * (end.R - start.R)); - int g = (int)(start.G + fraction * (end.G - start.G)); - int b = (int)(start.B + fraction * (end.B - start.B)); - yield return new Color (r, g, b); + var r = (int)(start.R + fraction * (end.R - start.R)); + var g = (int)(start.G + fraction * (end.G - start.G)); + var b = (int)(start.B + fraction * (end.B - start.B)); + + yield return new (r, g, b); } + yield return end; // Ensure the last color is included } - /// - /// - /// Creates a mapping starting at 0,0 and going to and - /// (inclusively) using the supplied . - /// - /// - /// Note that this method is inclusive i.e. passing 1/1 results in 4 mapped coordinates. - /// + /// + /// Creates a mapping starting at 0,0 and going to and + /// (inclusively) using the supplied . + /// + /// + /// Note that this method is inclusive i.e. passing 1/1 results in 4 mapped coordinates. + /// /// /// /// @@ -174,63 +177,69 @@ public class Gradient /// public Dictionary BuildCoordinateColorMapping (int maxRow, int maxColumn, GradientDirection direction) { - var gradientMapping = new Dictionary (); + Dictionary gradientMapping = new (); switch (direction) { case GradientDirection.Vertical: - for (int row = 0; row <= maxRow; row++) + for (var row = 0; row <= maxRow; row++) { double fraction = maxRow == 0 ? 1.0 : (double)row / maxRow; Color color = GetColorAtFraction (fraction); - for (int col = 0; col <= maxColumn; col++) + + for (var col = 0; col <= maxColumn; col++) { - gradientMapping [new Point (col, row)] = color; + gradientMapping [new (col, row)] = color; } } + break; case GradientDirection.Horizontal: - for (int col = 0; col <= maxColumn; col++) + for (var col = 0; col <= maxColumn; col++) { double fraction = maxColumn == 0 ? 1.0 : (double)col / maxColumn; Color color = GetColorAtFraction (fraction); - for (int row = 0; row <= maxRow; row++) + + for (var row = 0; row <= maxRow; row++) { - gradientMapping [new Point (col, row)] = color; + gradientMapping [new (col, row)] = color; } } + break; case GradientDirection.Radial: - for (int row = 0; row <= maxRow; row++) + for (var row = 0; row <= maxRow; row++) { - for (int col = 0; col <= maxColumn; col++) + for (var col = 0; col <= maxColumn; col++) { - double distanceFromCenter = FindNormalizedDistanceFromCenter (maxRow, maxColumn, new Point (col, row)); + double distanceFromCenter = FindNormalizedDistanceFromCenter (maxRow, maxColumn, new (col, row)); Color color = GetColorAtFraction (distanceFromCenter); - gradientMapping [new Point (col, row)] = color; + gradientMapping [new (col, row)] = color; } } + break; case GradientDirection.Diagonal: - for (int row = 0; row <= maxRow; row++) + for (var row = 0; row <= maxRow; row++) { - for (int col = 0; col <= maxColumn; col++) + for (var col = 0; col <= maxColumn; col++) { double fraction = ((double)row * 2 + col) / (maxRow * 2 + maxColumn); Color color = GetColorAtFraction (fraction); - gradientMapping [new Point (col, row)] = color; + gradientMapping [new (col, row)] = color; } } + break; } return gradientMapping; } - private double FindNormalizedDistanceFromCenter (int maxRow, int maxColumn, Point coord) + private static double FindNormalizedDistanceFromCenter (int maxRow, int maxColumn, Point coord) { double centerX = maxColumn / 2.0; double centerY = maxRow / 2.0; @@ -238,6 +247,7 @@ public class Gradient double dy = coord.Y - centerY; double distance = Math.Sqrt (dx * dx + dy * dy); double maxDistance = Math.Sqrt (centerX * centerX + centerY * centerY); + return distance / maxDistance; } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Drawing/GradientFill.cs b/Terminal.Gui/Drawing/GradientFill.cs index 6763de885..6518d2dab 100644 --- a/Terminal.Gui/Drawing/GradientFill.cs +++ b/Terminal.Gui/Drawing/GradientFill.cs @@ -1,17 +1,17 @@ namespace Terminal.Gui; /// -/// Implementation of that uses a color gradient (including -/// radial, diagonal etc). +/// Implementation of that uses a color gradient (including +/// radial, diagonal etc.). /// public class GradientFill : IFill { - private Dictionary _map; + private readonly Dictionary _map; /// - /// Creates a new instance of the class that can return - /// color for any point in the given using the provided - /// and . + /// Creates a new instance of the class that can return + /// color for any point in the given using the provided + /// and . /// /// /// @@ -19,23 +19,24 @@ public class GradientFill : IFill public GradientFill (Rectangle area, Gradient gradient, GradientDirection direction) { _map = gradient.BuildCoordinateColorMapping (area.Height - 1, area.Width - 1, direction) - .ToDictionary ( - kvp => new Point (kvp.Key.X + area.X, kvp.Key.Y + area.Y), - kvp => kvp.Value); + .ToDictionary ( + kvp => new Point (kvp.Key.X + area.X, kvp.Key.Y + area.Y), + kvp => kvp.Value); } /// - /// Returns the color to use for the given or Black if it - /// lies outside of the prepared gradient area (see constructor). + /// Returns the color to use for the given or Black if it + /// lies outside the prepared gradient area (see constructor). /// /// /// public Color GetColor (Point point) { - if (_map.TryGetValue (point, out var color)) + if (_map.TryGetValue (point, out Color color)) { return color; } - return new Color (0, 0, 0); // Default to black if point not found + + return new (0, 0); // Default to black if point not found } } diff --git a/Terminal.Gui/Drawing/IFill.cs b/Terminal.Gui/Drawing/IFill.cs index 8f81d305a..7d1d19a68 100644 --- a/Terminal.Gui/Drawing/IFill.cs +++ b/Terminal.Gui/Drawing/IFill.cs @@ -1,15 +1,14 @@ - -namespace Terminal.Gui; +namespace Terminal.Gui; /// -/// Describes an area fill (e.g. solid color or gradient). +/// Describes an area fill (e.g. solid color or gradient). /// public interface IFill { /// - /// Returns the color that should be used at the given point + /// Returns the color that should be used at the given point /// /// /// Color GetColor (Point point); -} \ No newline at end of file +} diff --git a/Terminal.Gui/Drawing/LineCanvas.cs b/Terminal.Gui/Drawing/LineCanvas.cs index 2c7367fcf..2bda9e5dd 100644 --- a/Terminal.Gui/Drawing/LineCanvas.cs +++ b/Terminal.Gui/Drawing/LineCanvas.cs @@ -5,9 +5,9 @@ namespace Terminal.Gui; public class LineCanvas : IDisposable { /// - /// Optional which when present overrides the - /// (colors) of lines in the canvas. This can be used e.g. to apply a global - /// across all lines. + /// Optional which when present overrides the + /// (colors) of lines in the canvas. This can be used e.g. to apply a global + /// across all lines. /// public FillPair? Fill { get; set; } @@ -142,7 +142,7 @@ public class LineCanvas : IDisposable ) { _cachedViewport = Rectangle.Empty; - _lines.Add (new StraightLine (start, length, orientation, style, attribute)); + _lines.Add (new (start, length, orientation, style, attribute)); } /// Adds a new line to the canvas @@ -190,7 +190,7 @@ public class LineCanvas : IDisposable if (cell is { }) { - map.Add (new Point (x, y), cell); + map.Add (new (x, y), cell); } } } @@ -225,7 +225,7 @@ public class LineCanvas : IDisposable if (rune is { }) { - map.Add (new Point (x, y), rune.Value); + map.Add (new (x, y), rune.Value); } } } @@ -333,8 +333,7 @@ public class LineCanvas : IDisposable private Attribute? GetAttributeForIntersects (IntersectionDefinition? [] intersects) { - return Fill != null ? Fill.GetAttribute (intersects [0]!.Point) : - intersects [0]!.Line.Attribute; + return Fill != null ? Fill.GetAttribute (intersects [0]!.Point) : intersects [0]!.Line.Attribute; } private Cell? GetCellForIntersects (ConsoleDriver driver, IntersectionDefinition? [] intersects) @@ -439,12 +438,12 @@ public class LineCanvas : IDisposable useThickDotted ? Glyphs.VLineHvDa4 : Glyphs.VLine; default: - throw new Exception ( - "Could not find resolver or switch case for " - + nameof (runeType) - + ":" - + runeType - ); + throw new ( + "Could not find resolver or switch case for " + + nameof (runeType) + + ":" + + runeType + ); } } @@ -854,4 +853,4 @@ public class LineCanvas : IDisposable _normal = Glyphs.URCorner; } } -} \ No newline at end of file +} diff --git a/Terminal.Gui/Drawing/SolidFill.cs b/Terminal.Gui/Drawing/SolidFill.cs index 67e1bdf1f..70c549608 100644 --- a/Terminal.Gui/Drawing/SolidFill.cs +++ b/Terminal.Gui/Drawing/SolidFill.cs @@ -1,31 +1,24 @@ namespace Terminal.Gui.Drawing; - /// -/// implementation that uses a solid color for all points +/// implementation that uses a solid color for all points /// public class SolidFill : IFill { - readonly Color _color; + private readonly Color _color; /// - /// Creates a new instance of the class which will return - /// the provided regardless of which point is requested. + /// Creates a new instance of the class which will return + /// the provided regardless of which point is requested. /// /// - public SolidFill (Color color) - { - _color = color; - } + public SolidFill (Color color) { _color = color; } /// - /// Returns the color this instance was constructed with regardless of - /// which is being colored. + /// Returns the color this instance was constructed with regardless of + /// which is being colored. /// /// /// - public Color GetColor (Point point) - { - return _color; - } + public Color GetColor (Point point) { return _color; } } diff --git a/Terminal.Gui/Drawing/StraightLine.cs b/Terminal.Gui/Drawing/StraightLine.cs index 8bbd83494..2f36995df 100644 --- a/Terminal.Gui/Drawing/StraightLine.cs +++ b/Terminal.Gui/Drawing/StraightLine.cs @@ -45,6 +45,7 @@ public class StraightLine /// Gets the rectangle that describes the bounds of the canvas. Location is the coordinates of the line that is /// furthest left/top and Size is defined by the line that extends the furthest right/bottom. /// + // PERF: Probably better to store the rectangle rather than make a new one on every single access to Viewport. internal Rectangle Viewport { @@ -115,22 +116,24 @@ public class StraightLine if (StartsAt (x, y)) { - return new IntersectionDefinition (p, - GetTypeByLength ( - IntersectionType.StartLeft, - IntersectionType.PassOverHorizontal, - IntersectionType.StartRight - ), - this - ); + return new ( + p, + GetTypeByLength ( + IntersectionType.StartLeft, + IntersectionType.PassOverHorizontal, + IntersectionType.StartRight + ), + this + ); } if (EndsAt (x, y)) { - return new IntersectionDefinition (p, - Length < 0 ? IntersectionType.StartRight : IntersectionType.StartLeft, - this - ); + return new ( + p, + Length < 0 ? IntersectionType.StartRight : IntersectionType.StartLeft, + this + ); } int xmin = Math.Min (Start.X, Start.X + Length); @@ -138,10 +141,11 @@ public class StraightLine if (xmin < x && xmax > x) { - return new IntersectionDefinition (p, - IntersectionType.PassOverHorizontal, - this - ); + return new ( + p, + IntersectionType.PassOverHorizontal, + this + ); } return null; @@ -158,22 +162,24 @@ public class StraightLine if (StartsAt (x, y)) { - return new IntersectionDefinition (p, - GetTypeByLength ( - IntersectionType.StartUp, - IntersectionType.PassOverVertical, - IntersectionType.StartDown - ), - this - ); + return new ( + p, + GetTypeByLength ( + IntersectionType.StartUp, + IntersectionType.PassOverVertical, + IntersectionType.StartDown + ), + this + ); } if (EndsAt (x, y)) { - return new IntersectionDefinition (p, - Length < 0 ? IntersectionType.StartDown : IntersectionType.StartUp, - this - ); + return new ( + p, + Length < 0 ? IntersectionType.StartDown : IntersectionType.StartUp, + this + ); } int ymin = Math.Min (Start.Y, Start.Y + Length); @@ -181,10 +187,11 @@ public class StraightLine if (ymin < y && ymax > y) { - return new IntersectionDefinition (p, - IntersectionType.PassOverVertical, - this - ); + return new ( + p, + IntersectionType.PassOverVertical, + this + ); } return null; diff --git a/UICatalog/Scenarios/TextEffectsScenario.cs b/UICatalog/Scenarios/TextEffectsScenario.cs index b34e77321..4c4f5d7cb 100644 --- a/UICatalog/Scenarios/TextEffectsScenario.cs +++ b/UICatalog/Scenarios/TextEffectsScenario.cs @@ -1,24 +1,25 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; +using System.Collections.Generic; using Terminal.Gui; - using Terminal.Gui.Drawing; namespace UICatalog.Scenarios; [ScenarioMetadata ("Text Effects", "Text Effects.")] [ScenarioCategory ("Colors")] +[ScenarioCategory ("Text and Formatting")] public class TextEffectsScenario : Scenario { private TabView _tabView; - public static bool LoopingGradient = false; + /// + /// Enable or disable looping of the gradient colors. + /// + public static bool LoopingGradient; public override void Main () { Application.Init (); + var w = new Window { Width = Dim.Fill (), @@ -26,57 +27,56 @@ public class TextEffectsScenario : Scenario Title = "Text Effects Scenario" }; - w.Loaded += (s, e) => - { - SetupGradientLineCanvas (w, w.Frame.Size); - }; - w.SizeChanging += (s, e) => - { - if (e.Size.HasValue) - { - SetupGradientLineCanvas (w, e.Size.Value); - } - }; + w.Loaded += (s, e) => { SetupGradientLineCanvas (w, w.Frame.Size); }; - w.ColorScheme = new ColorScheme + w.SizeChanging += (s, e) => + { + if (e.Size.HasValue) + { + SetupGradientLineCanvas (w, e.Size.Value); + } + }; + + w.ColorScheme = new () { - Normal = new Terminal.Gui.Attribute (ColorName.White, ColorName.Black), - Focus = new Terminal.Gui.Attribute (ColorName.Black, ColorName.White), - HotNormal = new Terminal.Gui.Attribute (ColorName.White, ColorName.Black), - HotFocus = new Terminal.Gui.Attribute (ColorName.White, ColorName.Black), - Disabled = new Terminal.Gui.Attribute (ColorName.Gray, ColorName.Black) + Normal = new (ColorName.White, ColorName.Black), + Focus = new (ColorName.Black, ColorName.White), + HotNormal = new (ColorName.White, ColorName.Black), + HotFocus = new (ColorName.White, ColorName.Black), + Disabled = new (ColorName.Gray, ColorName.Black) }; // Creates a window that occupies the entire terminal with a title. - _tabView = new TabView () + _tabView = new () { Width = Dim.Fill (), - Height = Dim.Fill (), + Height = Dim.Fill () }; - var gradientsView = new GradientsView () + var gradientsView = new GradientsView { Width = Dim.Fill (), - Height = Dim.Fill (), + Height = Dim.Fill () }; - var t1 = new Tab () + + var t1 = new Tab { View = gradientsView, DisplayText = "Gradients" }; - - var cbLooping = new CheckBox () + var cbLooping = new CheckBox { Text = "Looping", Y = Pos.AnchorEnd (1) }; + cbLooping.Toggle += (s, e) => - { - LoopingGradient = e.NewValue == CheckState.Checked; - SetupGradientLineCanvas (w, w.Frame.Size); - _tabView.SetNeedsDisplay (); - }; + { + LoopingGradient = e.NewValue == CheckState.Checked; + SetupGradientLineCanvas (w, w.Frame.Size); + _tabView.SetNeedsDisplay (); + }; gradientsView.Add (cbLooping); @@ -88,50 +88,50 @@ public class TextEffectsScenario : Scenario w.Dispose (); Application.Shutdown (); - this.Dispose (); + Dispose (); } - - private void SetupGradientLineCanvas (View w, Size size) + private static void SetupGradientLineCanvas (View w, Size size) { - GetAppealingGradientColors (out var stops, out var steps); + GetAppealingGradientColors (out List stops, out List steps); var g = new Gradient (stops, steps, LoopingGradient); var fore = new GradientFill ( - new Rectangle (0, 0, size.Width, size.Height), g, GradientDirection.Diagonal); - var back = new SolidFill (new Terminal.Gui.Color (ColorName.Black)); + new (0, 0, size.Width, size.Height), + g, + GradientDirection.Diagonal); + var back = new SolidFill (new (ColorName.Black)); - w.LineCanvas.Fill = new FillPair ( - fore, - back); + w.LineCanvas.Fill = new ( + fore, + back); } public static void GetAppealingGradientColors (out List stops, out List steps) { // Define the colors of the gradient stops with more appealing colors - stops = new List - { - new Color(0, 128, 255), // Bright Blue - new Color(0, 255, 128), // Bright Green - new Color(255, 255, 0), // Bright Yellow - new Color(255, 128, 0), // Bright Orange - new Color(255, 0, 128) // Bright Pink - }; + stops = + [ + new (0, 128, 255), // Bright Blue + new (0, 255, 128), // Bright Green + new (255, 255), // Bright Yellow + new (255, 128), // Bright Orange + new (255, 0, 128) + ]; // Define the number of steps between each color for smoother transitions // If we pass only a single value then it will assume equal steps between all pairs - steps = new List { 15 }; + steps = [15]; } } - internal class GradientsView : View { - private const int GradientWidth = 30; - private const int GradientHeight = 15; - private const int LabelHeight = 1; - private const int GradientWithLabelHeight = GradientHeight + LabelHeight + 1; // +1 for spacing + private const int GRADIENT_WIDTH = 30; + private const int GRADIENT_HEIGHT = 15; + private const int LABEL_HEIGHT = 1; + private const int GRADIENT_WITH_LABEL_HEIGHT = GRADIENT_HEIGHT + LABEL_HEIGHT + 1; // +1 for spacing public override void OnDrawContent (Rectangle viewport) { @@ -139,10 +139,10 @@ internal class GradientsView : View DrawTopLineGradient (viewport); - int x = 2; - int y = 3; + var x = 2; + var y = 3; - var gradients = new List<(string Label, GradientDirection Direction)> + List<(string Label, GradientDirection Direction)> gradients = new () { ("Horizontal", GradientDirection.Horizontal), ("Vertical", GradientDirection.Vertical), @@ -150,74 +150,74 @@ internal class GradientsView : View ("Diagonal", GradientDirection.Diagonal) }; - foreach (var (label, direction) in gradients) + foreach ((string label, GradientDirection direction) in gradients) { - if (x + GradientWidth > viewport.Width) + if (x + GRADIENT_WIDTH > viewport.Width) { x = 2; // Reset to left margin - y += GradientWithLabelHeight; // Move down to next row + y += GRADIENT_WITH_LABEL_HEIGHT; // Move down to next row } DrawLabeledGradientArea (label, direction, x, y); - x += GradientWidth + 2; // Move right for next gradient, +2 for spacing + x += GRADIENT_WIDTH + 2; // Move right for next gradient, +2 for spacing } } private void DrawLabeledGradientArea (string label, GradientDirection direction, int xOffset, int yOffset) { DrawGradientArea (direction, xOffset, yOffset); - CenterText (label, xOffset, yOffset + GradientHeight); // Adjusted for text below the gradient + CenterText (label, xOffset, yOffset + GRADIENT_HEIGHT); // Adjusted for text below the gradient } private void CenterText (string text, int xOffset, int yOffset) { - if(yOffset+1 >= Viewport.Height) + if (yOffset + 1 >= Viewport.Height) { // Not enough space for label return; } - var width = text.Length; - var x = xOffset + (GradientWidth - width) / 2; // Center the text within the gradient area width + int width = text.Length; + int x = xOffset + (GRADIENT_WIDTH - width) / 2; // Center the text within the gradient area width Driver.SetAttribute (GetNormalColor ()); - Move (x, yOffset+1); + Move (x, yOffset + 1); Driver.AddStr (text); } private void DrawGradientArea (GradientDirection direction, int xOffset, int yOffset) { // Define the colors of the gradient stops - var stops = new List - { - new Color(255, 0, 0), // Red - new Color(0, 255, 0), // Green - new Color(238, 130, 238) // Violet - }; + List stops = + [ + new (255, 0), // Red + new (0, 255), // Green + new (238, 130, 238) + ]; // Define the number of steps between each color - var steps = new List { 10, 10 }; // 10 steps between Red -> Green, and Green -> Blue + List steps = [10, 10]; // 10 steps between Red -> Green, and Green -> Blue // Create the gradient - var radialGradient = new Gradient (stops, steps, loop: TextEffectsScenario.LoopingGradient); + var radialGradient = new Gradient (stops, steps, TextEffectsScenario.LoopingGradient); // Define the size of the rectangle - int maxRow = GradientHeight; // Adjusted to keep aspect ratio - int maxColumn = GradientWidth; + int maxRow = GRADIENT_HEIGHT; // Adjusted to keep aspect ratio + int maxColumn = GRADIENT_WIDTH; // Build the coordinate-color mapping for a radial gradient - var gradientMapping = radialGradient.BuildCoordinateColorMapping (maxRow, maxColumn, direction); + Dictionary gradientMapping = radialGradient.BuildCoordinateColorMapping (maxRow, maxColumn, direction); // Print the gradient - for (int row = 0; row <= maxRow; row++) + for (var row = 0; row <= maxRow; row++) { - for (int col = 0; col <= maxColumn; col++) + for (var col = 0; col <= maxColumn; col++) { var coord = new Point (col, row); - var color = gradientMapping [coord]; + Color color = gradientMapping [coord]; SetColor (color); - AddRune (col + xOffset, row + yOffset, new Rune ('█')); + AddRune (col + xOffset, row + yOffset, new ('█')); } } } @@ -225,44 +225,41 @@ internal class GradientsView : View private void DrawTopLineGradient (Rectangle viewport) { // Define the colors of the rainbow - var stops = new List - { - new Color(255, 0, 0), // Red - new Color(255, 165, 0), // Orange - new Color(255, 255, 0), // Yellow - new Color(0, 128, 0), // Green - new Color(0, 0, 255), // Blue - new Color(75, 0, 130), // Indigo - new Color(238, 130, 238) // Violet - }; + List stops = + [ + new (255, 0), // Red + new (255, 165), // Orange + new (255, 255), // Yellow + new (0, 128), // Green + new (0, 0, 255), // Blue + new (75, 0, 130), // Indigo + new (238, 130, 238) + ]; // Define the number of steps between each color - var steps = new List - { + List steps = + [ 20, // between Red and Orange 20, // between Orange and Yellow 20, // between Yellow and Green 20, // between Green and Blue 20, // between Blue and Indigo - 20 // between Indigo and Violet - }; + 20 + ]; // Create the gradient var rainbowGradient = new Gradient (stops, steps, TextEffectsScenario.LoopingGradient); - for (int x = 0; x < viewport.Width; x++) + for (var x = 0; x < viewport.Width; x++) { double fraction = (double)x / (viewport.Width - 1); Color color = rainbowGradient.GetColorAtFraction (fraction); SetColor (color); - AddRune (x, 0, new Rune ('█')); + AddRune (x, 0, new ('█')); } } - private void SetColor (Color color) - { - Application.Driver.SetAttribute (new Attribute (color, color)); - } + private static void SetColor (Color color) { Application.Driver.SetAttribute (new (color, color)); } } diff --git a/UnitTests/Drawing/FillPairTests.cs b/UnitTests/Drawing/FillPairTests.cs index bbe8b6563..067053e6e 100644 --- a/UnitTests/Drawing/FillPairTests.cs +++ b/UnitTests/Drawing/FillPairTests.cs @@ -1,15 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Terminal.Gui.Drawing; +using Terminal.Gui.Drawing; namespace Terminal.Gui.DrawingTests; public class FillPairTests { - [Fact] public void GetAttribute_ReturnsCorrectColors () { @@ -22,11 +16,10 @@ public class FillPairTests var fillPair = new FillPair (foregroundFill, backgroundFill); // Act - var resultAttribute = fillPair.GetAttribute (new Point (0, 0)); + Attribute resultAttribute = fillPair.GetAttribute (new (0, 0)); // Assert Assert.Equal (foregroundColor, resultAttribute.Foreground); Assert.Equal (backgroundColor, resultAttribute.Background); } } - diff --git a/UnitTests/Drawing/GradientFillTests.cs b/UnitTests/Drawing/GradientFillTests.cs index e1f035d8d..75bc65bbc 100644 --- a/UnitTests/Drawing/GradientFillTests.cs +++ b/UnitTests/Drawing/GradientFillTests.cs @@ -2,21 +2,21 @@ public class GradientFillTests { - private Gradient _gradient; + private readonly Gradient _gradient; public GradientFillTests () { // Define the colors of the gradient stops - var stops = new List + List stops = new List { - new Color(255, 0, 0), // Red - new Color(0, 0, 255) // Blue + new (255, 0), // Red + new (0, 0, 255) // Blue }; // Define the number of steps between each color - var steps = new List { 10 }; // 10 steps between Red -> Blue + List steps = new() { 10 }; // 10 steps between Red -> Blue - _gradient = new Gradient (stops, steps, loop: false); + _gradient = new (stops, steps); } [Fact] @@ -31,13 +31,13 @@ public class GradientFillTests var bottomLeft = new Point (0, area.Height - 1); var bottomRight = new Point (area.Width - 1, area.Height - 1); - var topLeftColor = gradientFill.GetColor (topLeft); - var topRightColor = gradientFill.GetColor (topRight); - var bottomLeftColor = gradientFill.GetColor (bottomLeft); - var bottomRightColor = gradientFill.GetColor (bottomRight); + Color topLeftColor = gradientFill.GetColor (topLeft); + Color topRightColor = gradientFill.GetColor (topRight); + Color bottomLeftColor = gradientFill.GetColor (bottomLeft); + Color bottomRightColor = gradientFill.GetColor (bottomRight); // Expected colors - var expectedTopLeftColor = new Color (255, 0, 0); // Red + var expectedTopLeftColor = new Color (255, 0); // Red var expectedBottomRightColor = new Color (0, 0, 255); // Blue Assert.Equal (expectedTopLeftColor, topLeftColor); @@ -56,13 +56,13 @@ public class GradientFillTests var bottomLeft = new Point (5, area.Bottom - 1); var bottomRight = new Point (area.Right - 1, area.Bottom - 1); - var topLeftColor = gradientFill.GetColor (topLeft); - var topRightColor = gradientFill.GetColor (topRight); - var bottomLeftColor = gradientFill.GetColor (bottomLeft); - var bottomRightColor = gradientFill.GetColor (bottomRight); + Color topLeftColor = gradientFill.GetColor (topLeft); + Color topRightColor = gradientFill.GetColor (topRight); + Color bottomLeftColor = gradientFill.GetColor (bottomLeft); + Color bottomRightColor = gradientFill.GetColor (bottomRight); // Expected colors - var expectedTopLeftColor = new Color (255, 0, 0); // Red + var expectedTopLeftColor = new Color (255, 0); // Red var expectedBottomRightColor = new Color (0, 0, 255); // Blue Assert.Equal (expectedTopLeftColor, topLeftColor); @@ -75,15 +75,15 @@ public class GradientFillTests var area = new Rectangle (0, 0, 10, 10); var gradientFill = new GradientFill (area, _gradient, GradientDirection.Diagonal); - for (int row = 0; row < area.Height; row++) + for (var row = 0; row < area.Height; row++) { - int previousRed = 255; - int previousBlue = 0; + var previousRed = 255; + var previousBlue = 0; - for (int col = 0; col < area.Width; col++) + for (var col = 0; col < area.Width; col++) { var point = new Point (col, row); - var color = gradientFill.GetColor (point); + Color color = gradientFill.GetColor (point); // Check if the current color is 'more blue' and 'less red' as it goes right and down Assert.True (color.R <= previousRed, $"Failed at ({col}, {row}): {color.R} > {previousRed}"); @@ -95,15 +95,15 @@ public class GradientFillTests } } - for (int col = 0; col < area.Width; col++) + for (var col = 0; col < area.Width; col++) { - int previousRed = 255; - int previousBlue = 0; + var previousRed = 255; + var previousBlue = 0; - for (int row = 0; row < area.Height; row++) + for (var row = 0; row < area.Height; row++) { var point = new Point (col, row); - var color = gradientFill.GetColor (point); + Color color = gradientFill.GetColor (point); // Check if the current color is 'more blue' and 'less red' as it goes right and down Assert.True (color.R <= previousRed, $"Failed at ({col}, {row}): {color.R} > {previousRed}"); diff --git a/UnitTests/Drawing/GradientTests.cs b/UnitTests/Drawing/GradientTests.cs index 8fcfc1354..0174a7ff9 100644 --- a/UnitTests/Drawing/GradientTests.cs +++ b/UnitTests/Drawing/GradientTests.cs @@ -1,5 +1,4 @@ - -namespace Terminal.Gui.DrawingTests; +namespace Terminal.Gui.DrawingTests; public class GradientTests { @@ -7,8 +6,8 @@ public class GradientTests public static IEnumerable GradientDirectionValues () { return typeof (GradientDirection).GetEnumValues () - .Cast () - .Select (direction => new object [] { direction }); + .Cast () + .Select (direction => new object [] { direction }); } [Theory] @@ -16,16 +15,16 @@ public class GradientTests public void GradientIsInclusive_2_by_2 (GradientDirection direction) { // Define the colors of the gradient stops - var stops = new List - { - new Color(255, 0, 0), // Red - new Color(0, 0, 255) // Blue - }; + List stops = new() + { + new (255, 0), // Red + new (0, 0, 255) // Blue + }; // Define the number of steps between each color - var steps = new List { 10 }; // 10 steps between Red -> Blue + List steps = new() { 10 }; // 10 steps between Red -> Blue - var g = new Gradient (stops, steps, loop: false); + var g = new Gradient (stops, steps); Assert.Equal (4, g.BuildCoordinateColorMapping (1, 1, direction).Count); } @@ -34,77 +33,77 @@ public class GradientTests public void GradientIsInclusive_1_by_1 (GradientDirection direction) { // Define the colors of the gradient stops - var stops = new List - { - new Color(255, 0, 0), // Red - new Color(0, 0, 255) // Blue - }; + List stops = new() + { + new (255, 0), // Red + new (0, 0, 255) // Blue + }; // Define the number of steps between each color - var steps = new List { 10 }; // 10 steps between Red -> Blue + List steps = new() { 10 }; // 10 steps between Red -> Blue - var g = new Gradient (stops, steps, loop: false); + var g = new Gradient (stops, steps); // Note that maxRow and maxCol are inclusive so this results in 1x1 area i.e. a single cell. - var c = Assert.Single (g.BuildCoordinateColorMapping (0, 0, direction)); - Assert.Equal (c.Key, new Point(0,0)); - Assert.Equal (c.Value, new Color (0, 0, 255)); + KeyValuePair c = Assert.Single (g.BuildCoordinateColorMapping (0, 0, direction)); + Assert.Equal (c.Key, new (0, 0)); + Assert.Equal (c.Value, new (0, 0, 255)); } [Fact] public void SingleColorStop () { - var stops = new List { new Color (255, 0, 0) }; // Red - var steps = new List { }; + List stops = new() { new (255, 0) }; // Red + List steps = new (); - var g = new Gradient (stops, steps, loop: false); - Assert.All (g.Spectrum, color => Assert.Equal (new Color (255, 0, 0), color)); + var g = new Gradient (stops, steps); + Assert.All (g.Spectrum, color => Assert.Equal (new (255, 0), color)); } [Fact] public void LoopingGradient_CorrectColors () { - var stops = new List - { - new Color(255, 0, 0), // Red - new Color(0, 0, 255) // Blue - }; + List stops = new() + { + new (255, 0), // Red + new (0, 0, 255) // Blue + }; - var steps = new List { 10 }; + List steps = new() { 10 }; - var g = new Gradient (stops, steps, loop: true); - Assert.Equal (new Color (255, 0, 0), g.Spectrum.First ()); - Assert.Equal (new Color (255, 0, 0), g.Spectrum.Last ()); + var g = new Gradient (stops, steps, true); + Assert.Equal (new (255, 0), g.Spectrum.First ()); + Assert.Equal (new (255, 0), g.Spectrum.Last ()); } [Fact] public void DifferentStepSizes () { - var stops = new List - { - new Color(255, 0, 0), // Red - new Color(0, 255, 0), // Green - new Color(0, 0, 255) // Blue - }; + List stops = new List + { + new (255, 0), // Red + new (0, 255), // Green + new (0, 0, 255) // Blue + }; - var steps = new List { 5, 15 }; // Different steps + List steps = new() { 5, 15 }; // Different steps - var g = new Gradient (stops, steps, loop: false); + var g = new Gradient (stops, steps); Assert.Equal (22, g.Spectrum.Count); } [Fact] public void FractionOutOfRange_ThrowsException () { - var stops = new List - { - new Color(255, 0, 0), // Red - new Color(0, 0, 255) // Blue - }; + List stops = new() + { + new (255, 0), // Red + new (0, 0, 255) // Blue + }; - var steps = new List { 10 }; + List steps = new() { 10 }; - var g = new Gradient (stops, steps, loop: false); + var g = new Gradient (stops, steps); Assert.Throws (() => g.GetColorAtFraction (-0.1)); Assert.Throws (() => g.GetColorAtFraction (1.1)); @@ -113,30 +112,30 @@ public class GradientTests [Fact] public void NaNFraction_ReturnsLastColor () { - var stops = new List - { - new Color(255, 0, 0), // Red - new Color(0, 0, 255) // Blue - }; + List stops = new() + { + new (255, 0), // Red + new (0, 0, 255) // Blue + }; - var steps = new List { 10 }; + List steps = new() { 10 }; - var g = new Gradient (stops, steps, loop: false); - Assert.Equal (new Color (0, 0, 255), g.GetColorAtFraction (double.NaN)); + var g = new Gradient (stops, steps); + Assert.Equal (new (0, 0, 255), g.GetColorAtFraction (double.NaN)); } [Fact] public void Constructor_SingleStepProvided_ReplicatesForAllPairs () { - var stops = new List - { - new Color(255, 0, 0), // Red - new Color(0, 255, 0), // Green - new Color(0, 0, 255) // Blue - }; + List stops = new List + { + new (255, 0), // Red + new (0, 255), // Green + new (0, 0, 255) // Blue + }; - var singleStep = new List { 5 }; // Single step provided - var gradient = new Gradient (stops, singleStep, loop: false); + List singleStep = new() { 5 }; // Single step provided + var gradient = new Gradient (stops, singleStep); Assert.NotNull (gradient.Spectrum); Assert.Equal (12, gradient.Spectrum.Count); // 5 steps Red -> Green + 5 steps Green -> Blue + 2 end colors @@ -145,31 +144,30 @@ public class GradientTests [Fact] public void Constructor_InvalidStepsLength_ThrowsArgumentException () { - var stops = new List - { - new Color(255, 0, 0), // Red - new Color(0, 0, 255) // Blue - }; + List stops = new() + { + new (255, 0), // Red + new (0, 0, 255) // Blue + }; - var invalidSteps = new List { 5, 5 }; // Invalid length (N-1 expected) - Assert.Throws (() => new Gradient (stops, invalidSteps, loop: false)); + List invalidSteps = new() { 5, 5 }; // Invalid length (N-1 expected) + Assert.Throws (() => new Gradient (stops, invalidSteps)); } [Fact] public void Constructor_ValidStepsLength_DoesNotThrow () { - var stops = new List - { - new Color(255, 0, 0), // Red - new Color(0, 255, 0), // Green - new Color(0, 0, 255) // Blue - }; + List stops = new List + { + new (255, 0), // Red + new (0, 255), // Green + new (0, 0, 255) // Blue + }; - var validSteps = new List { 5, 5 }; // Valid length (N-1) - var gradient = new Gradient (stops, validSteps, loop: false); + List validSteps = new() { 5, 5 }; // Valid length (N-1) + var gradient = new Gradient (stops, validSteps); Assert.NotNull (gradient.Spectrum); Assert.Equal (12, gradient.Spectrum.Count); // 5 steps Red -> Green + 5 steps Green -> Blue + 2 end colors } - -} \ No newline at end of file +} diff --git a/UnitTests/Drawing/LineCanvasTests.cs b/UnitTests/Drawing/LineCanvasTests.cs index d160b5420..7d002770d 100644 --- a/UnitTests/Drawing/LineCanvasTests.cs +++ b/UnitTests/Drawing/LineCanvasTests.cs @@ -4,7 +4,7 @@ using Xunit.Abstractions; namespace Terminal.Gui.DrawingTests; -public class LineCanvasTests (ITestOutputHelper output) +public class LineCanvasTests (ITestOutputHelper _output) { [Theory] @@ -295,7 +295,7 @@ public class LineCanvasTests (ITestOutputHelper output) lc.AddLine (new (x1, y1), len1, o1, s1); lc.AddLine (new (x2, y2), len2, o2, s2); - TestHelpers.AssertEqual (output, expected, lc.ToString ()); + TestHelpers.AssertEqual (_output, expected, lc.ToString ()); v.Dispose (); } @@ -504,7 +504,7 @@ public class LineCanvasTests (ITestOutputHelper output) Assert.Equal (new (x, y, 4, 2), lc.Viewport); TestHelpers.AssertEqual ( - output, + _output, @" ╔╡╞╗ ║ ║", @@ -554,7 +554,7 @@ public class LineCanvasTests (ITestOutputHelper output) Assert.Equal (new (x, y, 4, 2), lc.Viewport); TestHelpers.AssertEqual ( - output, + _output, @" ╔╡╞╗ ║ ║", @@ -597,7 +597,7 @@ public class LineCanvasTests (ITestOutputHelper output) // Add a line at 5, 5 that's has length of 1 canvas.AddLine (new (x, y), 1, orientation, LineStyle.Single); - TestHelpers.AssertEqual (output, $"{expected}", $"{canvas}"); + TestHelpers.AssertEqual (_output, $"{expected}", $"{canvas}"); } // X is offset by 2 @@ -654,7 +654,7 @@ public class LineCanvasTests (ITestOutputHelper output) canvas.AddLine (new (x, y), length, orientation, LineStyle.Single); var result = canvas.ToString (); - TestHelpers.AssertEqual (output, expected, result); + TestHelpers.AssertEqual (_output, expected, result); } [Fact] @@ -681,7 +681,7 @@ public class LineCanvasTests (ITestOutputHelper output) // Add a line at 0, 0 that's has length of 0 lc.AddLine (Point.Empty, 0, orientation, LineStyle.Single); - TestHelpers.AssertEqual (output, expected, $"{lc}"); + TestHelpers.AssertEqual (_output, expected, $"{lc}"); } [InlineData (Orientation.Horizontal, "┼")] @@ -702,7 +702,7 @@ public class LineCanvasTests (ITestOutputHelper output) // Add a line at 0, 0 that's has length of 0 lc.AddLine (Point.Empty, 0, orientation, LineStyle.Single); - TestHelpers.AssertEqual (output, expected, $"{lc}"); + TestHelpers.AssertEqual (_output, expected, $"{lc}"); } [InlineData (Orientation.Horizontal, "╥")] @@ -725,7 +725,7 @@ public class LineCanvasTests (ITestOutputHelper output) // Add a line at 0, 0 that's has length of 0 lc.AddLine (Point.Empty, 0, orientation, LineStyle.Single); - TestHelpers.AssertEqual (output, expected, $"{lc}"); + TestHelpers.AssertEqual (_output, expected, $"{lc}"); } [Fact] @@ -741,7 +741,7 @@ public class LineCanvasTests (ITestOutputHelper output) @" ┌─ │ "; - TestHelpers.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}"); + TestHelpers.AssertEqual (_output, looksLike, $"{Environment.NewLine}{canvas}"); } [Fact] @@ -768,7 +768,7 @@ public class LineCanvasTests (ITestOutputHelper output) ┣━━━━╋━━━┫ ┃ ┃ ┃ ┗━━━━┻━━━┛"; - TestHelpers.AssertDriverContentsAre (looksLike, output); + TestHelpers.AssertDriverContentsAre (looksLike, _output); v.Dispose (); } @@ -799,7 +799,7 @@ public class LineCanvasTests (ITestOutputHelper output) │ │ │ ┕━━━━┷━━━┙ "; - TestHelpers.AssertDriverContentsAre (looksLike, output); + TestHelpers.AssertDriverContentsAre (looksLike, _output); v.Dispose (); } @@ -831,7 +831,7 @@ public class LineCanvasTests (ITestOutputHelper output) ┖────┸───┚ "; - TestHelpers.AssertDriverContentsAre (looksLike, output); + TestHelpers.AssertDriverContentsAre (looksLike, _output); v.Dispose (); } @@ -849,7 +849,7 @@ public class LineCanvasTests (ITestOutputHelper output) @" ┌─ │ "; - TestHelpers.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}"); + TestHelpers.AssertEqual (_output, looksLike, $"{Environment.NewLine}{canvas}"); } [Fact] @@ -879,7 +879,7 @@ public class LineCanvasTests (ITestOutputHelper output) Assert.Equal (2, map.Count); TestHelpers.AssertEqual ( - output, + _output, @" ─ ─", @@ -892,7 +892,7 @@ public class LineCanvasTests (ITestOutputHelper output) public void ToString_Empty () { var lc = new LineCanvas (); - TestHelpers.AssertEqual (output, string.Empty, lc.ToString ()); + TestHelpers.AssertEqual (_output, string.Empty, lc.ToString ()); } // 012 @@ -911,7 +911,7 @@ public class LineCanvasTests (ITestOutputHelper output) { var lc = new LineCanvas (); lc.AddLine (new (x, y), 3, Orientation.Horizontal, LineStyle.Double); - TestHelpers.AssertEqual (output, expected, $"{lc}"); + TestHelpers.AssertEqual (_output, expected, $"{lc}"); } [InlineData (0, 0, 0, 0, "═══")] @@ -936,7 +936,7 @@ public class LineCanvasTests (ITestOutputHelper output) lc.AddLine (new (x1, y1), 3, Orientation.Horizontal, LineStyle.Double); lc.AddLine (new (x2, y2), 3, Orientation.Horizontal, LineStyle.Double); - TestHelpers.AssertEqual (output, expected, $"{lc}"); + TestHelpers.AssertEqual (_output, expected, $"{lc}"); } // [Fact, SetupFakeDriver] @@ -996,7 +996,7 @@ public class LineCanvasTests (ITestOutputHelper output) v.Draw (); - TestHelpers.AssertDriverContentsAre (expected, output); + TestHelpers.AssertDriverContentsAre (expected, _output); v.Dispose (); } @@ -1015,7 +1015,7 @@ public class LineCanvasTests (ITestOutputHelper output) @" ┌─ │"; - TestHelpers.AssertDriverContentsAre (looksLike, output); + TestHelpers.AssertDriverContentsAre (looksLike, _output); v.Dispose (); } @@ -1038,7 +1038,7 @@ public class LineCanvasTests (ITestOutputHelper output) ── │ │"; - TestHelpers.AssertDriverContentsAre (looksLike, output); + TestHelpers.AssertDriverContentsAre (looksLike, _output); v.Dispose (); } @@ -1056,7 +1056,7 @@ public class LineCanvasTests (ITestOutputHelper output) var looksLike = @" ──"; - TestHelpers.AssertDriverContentsAre (looksLike, output); + TestHelpers.AssertDriverContentsAre (looksLike, _output); v.Dispose (); } @@ -1072,7 +1072,7 @@ public class LineCanvasTests (ITestOutputHelper output) var looksLike = @" ══"; - TestHelpers.AssertDriverContentsAre (looksLike, output); + TestHelpers.AssertDriverContentsAre (looksLike, _output); v.Dispose (); } @@ -1091,7 +1091,7 @@ public class LineCanvasTests (ITestOutputHelper output) @" │ │"; - TestHelpers.AssertDriverContentsAre (looksLike, output); + TestHelpers.AssertDriverContentsAre (looksLike, _output); v.Dispose (); } @@ -1108,7 +1108,7 @@ public class LineCanvasTests (ITestOutputHelper output) @" ║ ║"; - TestHelpers.AssertDriverContentsAre (looksLike, output); + TestHelpers.AssertDriverContentsAre (looksLike, _output); v.Dispose (); } @@ -1136,7 +1136,7 @@ public class LineCanvasTests (ITestOutputHelper output) ╠════╬═══╣ ║ ║ ║ ╚════╩═══╝"; - TestHelpers.AssertDriverContentsAre (looksLike, output); + TestHelpers.AssertDriverContentsAre (looksLike, _output); v.Dispose (); } @@ -1167,7 +1167,7 @@ public class LineCanvasTests (ITestOutputHelper output) │ │ │ ╘════╧═══╛ "; - TestHelpers.AssertDriverContentsAre (looksLike, output); + TestHelpers.AssertDriverContentsAre (looksLike, _output); v.Dispose (); } @@ -1204,7 +1204,7 @@ public class LineCanvasTests (ITestOutputHelper output) ├────┼───┤ │ │ │ ╰────┴───╯"; - TestHelpers.AssertDriverContentsAre (looksLike, output); + TestHelpers.AssertDriverContentsAre (looksLike, _output); v.Dispose (); } @@ -1236,7 +1236,7 @@ public class LineCanvasTests (ITestOutputHelper output) ╙────╨───╜ "; - TestHelpers.AssertDriverContentsAre (looksLike, output); + TestHelpers.AssertDriverContentsAre (looksLike, _output); v.Dispose (); } @@ -1263,7 +1263,7 @@ public class LineCanvasTests (ITestOutputHelper output) ├────┼───┤ │ │ │ └────┴───┘"; - TestHelpers.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}"); + TestHelpers.AssertEqual (_output, looksLike, $"{Environment.NewLine}{canvas}"); } [Fact] @@ -1301,15 +1301,15 @@ public class LineCanvasTests (ITestOutputHelper output) var looksLike = @" ╔╡╞══╗ ║ ║"; - TestHelpers.AssertEqual (output, looksLike, $"{Environment.NewLine}{lc}"); + TestHelpers.AssertEqual (_output, looksLike, $"{Environment.NewLine}{lc}"); } [Fact] public void LineCanvas_UsesFillCorrectly () { // Arrange - var foregroundColor = new Color (255, 0, 0); // Red - var backgroundColor = new Color (0, 0, 0); // Black + var foregroundColor = new Color (255, 0); // Red + var backgroundColor = new Color (0, 0); // Black var foregroundFill = new SolidFill (foregroundColor); var backgroundFill = new SolidFill (backgroundColor); var fillPair = new FillPair (foregroundFill, backgroundFill); @@ -1320,11 +1320,11 @@ public class LineCanvasTests (ITestOutputHelper output) }; // Act - lineCanvas.AddLine (new Point (0, 0), 5, Orientation.Horizontal, LineStyle.Single); - var cellMap = lineCanvas.GetCellMap (); + lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single); + Dictionary cellMap = lineCanvas.GetCellMap (); // Assert - foreach (var cell in cellMap.Values) + foreach (Cell? cell in cellMap.Values) { Assert.NotNull (cell); Assert.Equal (foregroundColor, cell.Value.Attribute.Value.Foreground); @@ -1336,9 +1336,9 @@ public class LineCanvasTests (ITestOutputHelper output) public void LineCanvas_LineColorIgnoredBecauseOfFill () { // Arrange - var foregroundColor = new Color (255, 0, 0); // Red - var backgroundColor = new Color (0, 0, 0); // Black - var lineColor = new Attribute (new Color (0, 255, 0), new Color (255, 255, 255)); // Green on White + var foregroundColor = new Color (255, 0); // Red + var backgroundColor = new Color (0, 0); // Black + var lineColor = new Attribute (new Color (0, 255), new Color (255, 255, 255)); // Green on White var foregroundFill = new SolidFill (foregroundColor); var backgroundFill = new SolidFill (backgroundColor); var fillPair = new FillPair (foregroundFill, backgroundFill); @@ -1349,11 +1349,11 @@ public class LineCanvasTests (ITestOutputHelper output) }; // Act - lineCanvas.AddLine (new Point (0, 0), 5, Orientation.Horizontal, LineStyle.Single, lineColor); - var cellMap = lineCanvas.GetCellMap (); + lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single, lineColor); + Dictionary cellMap = lineCanvas.GetCellMap (); // Assert - foreach (var cell in cellMap.Values) + foreach (Cell? cell in cellMap.Values) { Assert.NotNull (cell); Assert.Equal (foregroundColor, cell.Value.Attribute.Value.Foreground); @@ -1365,8 +1365,8 @@ public class LineCanvasTests (ITestOutputHelper output) public void LineCanvas_IntersectingLinesUseFillCorrectly () { // Arrange - var foregroundColor = new Color (255, 0, 0); // Red - var backgroundColor = new Color (0, 0, 0); // Black + var foregroundColor = new Color (255, 0); // Red + var backgroundColor = new Color (0, 0); // Black var foregroundFill = new SolidFill (foregroundColor); var backgroundFill = new SolidFill (backgroundColor); var fillPair = new FillPair (foregroundFill, backgroundFill); @@ -1377,12 +1377,12 @@ public class LineCanvasTests (ITestOutputHelper output) }; // Act - lineCanvas.AddLine (new Point (0, 0), 5, Orientation.Horizontal, LineStyle.Single); - lineCanvas.AddLine (new Point (2, -2), 5, Orientation.Vertical, LineStyle.Single); - var cellMap = lineCanvas.GetCellMap (); + lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single); + lineCanvas.AddLine (new (2, -2), 5, Orientation.Vertical, LineStyle.Single); + Dictionary cellMap = lineCanvas.GetCellMap (); // Assert - foreach (var cell in cellMap.Values) + foreach (Cell? cell in cellMap.Values) { Assert.NotNull (cell); Assert.Equal (foregroundColor, cell.Value.Attribute.Value.Foreground); diff --git a/UnitTests/Drawing/SolidFillTests.cs b/UnitTests/Drawing/SolidFillTests.cs index 50eaa9f9f..3cf016867 100644 --- a/UnitTests/Drawing/SolidFillTests.cs +++ b/UnitTests/Drawing/SolidFillTests.cs @@ -1,5 +1,4 @@ - -using Terminal.Gui.Drawing; +using Terminal.Gui.Drawing; namespace Terminal.Gui.DrawingTests; @@ -13,7 +12,7 @@ public class SolidFillTests var solidFill = new SolidFill (expectedColor); // Act - var resultColor = solidFill.GetColor (new Point (0, 0)); + Color resultColor = solidFill.GetColor (new (0, 0)); // Assert Assert.Equal (expectedColor, resultColor); @@ -32,9 +31,9 @@ public class SolidFillTests var solidFill = new SolidFill (expectedColor); // Act - var resultColor = solidFill.GetColor (new Point (x, y)); + Color resultColor = solidFill.GetColor (new (x, y)); // Assert Assert.Equal (expectedColor, resultColor); } -} \ No newline at end of file +} diff --git a/UnitTests/Drawing/StraightLineExtensionsTests.cs b/UnitTests/Drawing/StraightLineExtensionsTests.cs index b7a5f36b1..865ae805a 100644 --- a/UnitTests/Drawing/StraightLineExtensionsTests.cs +++ b/UnitTests/Drawing/StraightLineExtensionsTests.cs @@ -2,11 +2,8 @@ namespace Terminal.Gui.DrawingTests; -public class StraightLineExtensionsTests +public class StraightLineExtensionsTests (ITestOutputHelper output) { - private readonly ITestOutputHelper _output; - public StraightLineExtensionsTests (ITestOutputHelper output) { _output = output; } - [Fact] [AutoInitShutdown] public void LineCanvasIntegrationTest () @@ -18,7 +15,7 @@ public class StraightLineExtensionsTests lc.AddLine (new Point (0, 4), -5, Orientation.Vertical, LineStyle.Single); TestHelpers.AssertEqual ( - _output, + output, @" ┌────────┐ │ │ @@ -32,7 +29,7 @@ public class StraightLineExtensionsTests lc = new LineCanvas (origLines.Exclude (Point.Empty, 10, Orientation.Horizontal)); TestHelpers.AssertEqual ( - _output, + output, @" │ │ │ │ @@ -44,7 +41,7 @@ public class StraightLineExtensionsTests lc = new LineCanvas (origLines.Exclude (new Point (0, 1), 10, Orientation.Horizontal)); TestHelpers.AssertEqual ( - _output, + output, @" ┌────────┐ @@ -57,7 +54,7 @@ public class StraightLineExtensionsTests lc = new LineCanvas (origLines.Exclude (new Point (0, 2), 10, Orientation.Horizontal)); TestHelpers.AssertEqual ( - _output, + output, @" ┌────────┐ │ │ @@ -70,7 +67,7 @@ public class StraightLineExtensionsTests lc = new LineCanvas (origLines.Exclude (new Point (0, 3), 10, Orientation.Horizontal)); TestHelpers.AssertEqual ( - _output, + output, @" ┌────────┐ │ │ @@ -83,7 +80,7 @@ public class StraightLineExtensionsTests lc = new LineCanvas (origLines.Exclude (new Point (0, 4), 10, Orientation.Horizontal)); TestHelpers.AssertEqual ( - _output, + output, @" ┌────────┐ │ │ @@ -95,7 +92,7 @@ public class StraightLineExtensionsTests lc = new LineCanvas (origLines.Exclude (Point.Empty, 10, Orientation.Vertical)); TestHelpers.AssertEqual ( - _output, + output, @" ────────┐ │ @@ -108,7 +105,7 @@ public class StraightLineExtensionsTests lc = new LineCanvas (origLines.Exclude (new Point (1, 0), 10, Orientation.Vertical)); TestHelpers.AssertEqual ( - _output, + output, @" ┌ ───────┐ │ │ @@ -121,7 +118,7 @@ public class StraightLineExtensionsTests lc = new LineCanvas (origLines.Exclude (new Point (8, 0), 10, Orientation.Vertical)); TestHelpers.AssertEqual ( - _output, + output, @" ┌─────── ┐ │ │ @@ -134,7 +131,7 @@ public class StraightLineExtensionsTests lc = new LineCanvas (origLines.Exclude (new Point (9, 0), 10, Orientation.Vertical)); TestHelpers.AssertEqual ( - _output, + output, @" ┌──────── │ diff --git a/UnitTests/Drawing/StraightLineTests.cs b/UnitTests/Drawing/StraightLineTests.cs index bb6870821..4395ea0c1 100644 --- a/UnitTests/Drawing/StraightLineTests.cs +++ b/UnitTests/Drawing/StraightLineTests.cs @@ -2,10 +2,9 @@ namespace Terminal.Gui.DrawingTests; -public class StraightLineTests +public class StraightLineTests (ITestOutputHelper output) { - private readonly ITestOutputHelper output; - public StraightLineTests (ITestOutputHelper output) { this.output = output; } + private readonly ITestOutputHelper _output = output; [InlineData ( Orientation.Horizontal, @@ -320,8 +319,8 @@ public class StraightLineTests int expectedHeight ) { - var sl = new StraightLine (new Point (x, y), length, orientation, LineStyle.Single); + var sl = new StraightLine (new (x, y), length, orientation, LineStyle.Single); - Assert.Equal (new Rectangle (expectedX, expectedY, expectedWidth, expectedHeight), sl.Viewport); + Assert.Equal (new (expectedX, expectedY, expectedWidth, expectedHeight), sl.Viewport); } } From f770bb983a023c3c55a531c1446a15ae4af0ffd8 Mon Sep 17 00:00:00 2001 From: Tig Date: Tue, 9 Jul 2024 13:39:52 -0600 Subject: [PATCH 41/48] Code cleanup2 --- Terminal.Gui/View/Adornment/Border.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Terminal.Gui/View/Adornment/Border.cs b/Terminal.Gui/View/Adornment/Border.cs index 1ed64abca..2931cd9ad 100644 --- a/Terminal.Gui/View/Adornment/Border.cs +++ b/Terminal.Gui/View/Adornment/Border.cs @@ -1,5 +1,3 @@ -using Terminal.Gui.Drawing; - namespace Terminal.Gui; /// The Border for a . From 043538c58105bea402a03551c04329b2913db5cc Mon Sep 17 00:00:00 2001 From: Brandon Thetford Date: Tue, 9 Jul 2024 20:48:01 -0700 Subject: [PATCH 42/48] When single-targeting, use the singular. It's a little different in msbuild. --- Terminal.Gui/Terminal.Gui.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj index b436834a1..b41e4ab24 100644 --- a/Terminal.Gui/Terminal.Gui.csproj +++ b/Terminal.Gui/Terminal.Gui.csproj @@ -1,4 +1,4 @@ - + @@ -19,7 +19,7 @@ - net8.0 + net8.0 12 $(AssemblyName) true From b667c3de9820e3030a721f65056cd3ecc7052321 Mon Sep 17 00:00:00 2001 From: Brandon Thetford Date: Tue, 9 Jul 2024 21:45:21 -0700 Subject: [PATCH 43/48] Remove analyzer projects from solution and project references --- Analyzers.slnf | 10 ---------- NoSamples.slnf | 2 -- Release.slnf | 1 - Terminal.Gui/Terminal.Gui.csproj | 5 ----- Terminal.sln | 23 ----------------------- 5 files changed, 41 deletions(-) delete mode 100644 Analyzers.slnf diff --git a/Analyzers.slnf b/Analyzers.slnf deleted file mode 100644 index 974c5a965..000000000 --- a/Analyzers.slnf +++ /dev/null @@ -1,10 +0,0 @@ -{ - "solution": { - "path": "Terminal.sln", - "projects": [ - "Analyzers\\Terminal.Gui.Analyzers.Internal.Debugging\\Terminal.Gui.Analyzers.Internal.Debugging.csproj", - "Analyzers\\Terminal.Gui.Analyzers.Internal.Tests\\Terminal.Gui.Analyzers.Internal.Tests.csproj", - "Analyzers\\Terminal.Gui.Analyzers.Internal\\Terminal.Gui.Analyzers.Internal.csproj", - ] - } -} \ No newline at end of file diff --git a/NoSamples.slnf b/NoSamples.slnf index f0b138221..8d2a38dad 100644 --- a/NoSamples.slnf +++ b/NoSamples.slnf @@ -2,8 +2,6 @@ "solution": { "path": "Terminal.sln", "projects": [ - "Analyzers\\Terminal.Gui.Analyzers.Internal.Tests\\Terminal.Gui.Analyzers.Internal.Tests.csproj", - "Analyzers\\Terminal.Gui.Analyzers.Internal\\Terminal.Gui.Analyzers.Internal.csproj", "Terminal.Gui\\Terminal.Gui.csproj", "UICatalog\\UICatalog.csproj", "UnitTests\\UnitTests.csproj" diff --git a/Release.slnf b/Release.slnf index 4949e26c3..8d2a38dad 100644 --- a/Release.slnf +++ b/Release.slnf @@ -2,7 +2,6 @@ "solution": { "path": "Terminal.sln", "projects": [ - "Analyzers\\Terminal.Gui.Analyzers.Internal\\Terminal.Gui.Analyzers.Internal.csproj", "Terminal.Gui\\Terminal.Gui.csproj", "UICatalog\\UICatalog.csproj", "UnitTests\\UnitTests.csproj" diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj index b41e4ab24..21926c19d 100644 --- a/Terminal.Gui/Terminal.Gui.csproj +++ b/Terminal.Gui/Terminal.Gui.csproj @@ -59,11 +59,6 @@ - - all - Analyzer - false - diff --git a/Terminal.sln b/Terminal.sln index a63762780..0eb947ab4 100644 --- a/Terminal.sln +++ b/Terminal.sln @@ -14,14 +14,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example", "Example\Example. EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E143FB1F-0B88-48CB-9086-72CDCECFCD22}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analyzers", "Analyzers", "{CCADA0BC-61CF-4B4B-96BA-A3B0C0A7F54D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Terminal.Gui.Analyzers.Internal", "Analyzers\Terminal.Gui.Analyzers.Internal\Terminal.Gui.Analyzers.Internal.csproj", "{5DE91722-8765-4E2B-97E4-2A18010B5CED}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Terminal.Gui.Analyzers.Internal.Tests", "Analyzers\Terminal.Gui.Analyzers.Internal.Tests\Terminal.Gui.Analyzers.Internal.Tests.csproj", "{715DB4BA-F989-4DF6-B46F-5ED26A32B2DD}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Terminal.Gui.Analyzers.Internal.Debugging", "Analyzers\Terminal.Gui.Analyzers.Internal.Debugging\Terminal.Gui.Analyzers.Internal.Debugging.csproj", "{C2AD09BD-D579-45A7-ACA3-E4EF3BC027D2}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkitExample", "CommunityToolkitExample\CommunityToolkitExample.csproj", "{58FDCA8F-08F7-4D80-9DA3-6A9AED01E163}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Settings", "Settings", "{B8F48EE8-34A6-43B7-B00E-CD91CDD93962}" @@ -61,14 +53,6 @@ Global {00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Debug|Any CPU.Build.0 = Debug|Any CPU {00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Release|Any CPU.ActiveCfg = Release|Any CPU {00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Release|Any CPU.Build.0 = Release|Any CPU - {5DE91722-8765-4E2B-97E4-2A18010B5CED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5DE91722-8765-4E2B-97E4-2A18010B5CED}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5DE91722-8765-4E2B-97E4-2A18010B5CED}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5DE91722-8765-4E2B-97E4-2A18010B5CED}.Release|Any CPU.Build.0 = Release|Any CPU - {715DB4BA-F989-4DF6-B46F-5ED26A32B2DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {715DB4BA-F989-4DF6-B46F-5ED26A32B2DD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {715DB4BA-F989-4DF6-B46F-5ED26A32B2DD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {715DB4BA-F989-4DF6-B46F-5ED26A32B2DD}.Release|Any CPU.Build.0 = Release|Any CPU {88979F89-9A42-448F-AE3E-3060145F6375}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {88979F89-9A42-448F-AE3E-3060145F6375}.Debug|Any CPU.Build.0 = Debug|Any CPU {88979F89-9A42-448F-AE3E-3060145F6375}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -85,10 +69,6 @@ Global {B0A602CD-E176-449D-8663-64238D54F857}.Debug|Any CPU.Build.0 = Debug|Any CPU {B0A602CD-E176-449D-8663-64238D54F857}.Release|Any CPU.ActiveCfg = Release|Any CPU {B0A602CD-E176-449D-8663-64238D54F857}.Release|Any CPU.Build.0 = Release|Any CPU - {C2AD09BD-D579-45A7-ACA3-E4EF3BC027D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C2AD09BD-D579-45A7-ACA3-E4EF3BC027D2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C2AD09BD-D579-45A7-ACA3-E4EF3BC027D2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C2AD09BD-D579-45A7-ACA3-E4EF3BC027D2}.Release|Any CPU.Build.0 = Release|Any CPU {58FDCA8F-08F7-4D80-9DA3-6A9AED01E163}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {58FDCA8F-08F7-4D80-9DA3-6A9AED01E163}.Debug|Any CPU.Build.0 = Debug|Any CPU {58FDCA8F-08F7-4D80-9DA3-6A9AED01E163}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -98,9 +78,6 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {5DE91722-8765-4E2B-97E4-2A18010B5CED} = {CCADA0BC-61CF-4B4B-96BA-A3B0C0A7F54D} - {715DB4BA-F989-4DF6-B46F-5ED26A32B2DD} = {CCADA0BC-61CF-4B4B-96BA-A3B0C0A7F54D} - {C2AD09BD-D579-45A7-ACA3-E4EF3BC027D2} = {CCADA0BC-61CF-4B4B-96BA-A3B0C0A7F54D} {B8F48EE8-34A6-43B7-B00E-CD91CDD93962} = {E143FB1F-0B88-48CB-9086-72CDCECFCD22} {13BB2C46-B324-4B9C-92EB-CE6184D4736E} = {E143FB1F-0B88-48CB-9086-72CDCECFCD22} {C7A51224-5E0F-4986-AB37-A6BF89966C12} = {E143FB1F-0B88-48CB-9086-72CDCECFCD22} From 90073ad56edae51cae265c0ef219cec2829fff69 Mon Sep 17 00:00:00 2001 From: Brandon Thetford Date: Tue, 9 Jul 2024 21:46:29 -0700 Subject: [PATCH 44/48] Bring in last generated code for existing extensions and format to match project. --- .../EnumExtensions/AddOrSubtractExtensions.cs | 51 ++++++++++ .../EnumExtensions/AlignmentExtensions.cs | 53 +++++++++++ .../AlignmentModesExtensions.cs | 90 ++++++++++++++++++ .../BorderSettingsExtensions.cs | 89 ++++++++++++++++++ .../EnumExtensions/DimAutoStyleExtensions.cs | 89 ++++++++++++++++++ .../DimPercentModeExtensions.cs | 51 ++++++++++ .../EnumExtensions/DimensionExtensions.cs | 51 ++++++++++ .../KeyBindingScopeExtensions.cs | 93 +++++++++++++++++++ Terminal.Gui/EnumExtensions/SideExtensions.cs | 53 +++++++++++ .../ViewDiagnosticFlagsExtensions.cs | 93 +++++++++++++++++++ 10 files changed, 713 insertions(+) create mode 100644 Terminal.Gui/EnumExtensions/AddOrSubtractExtensions.cs create mode 100644 Terminal.Gui/EnumExtensions/AlignmentExtensions.cs create mode 100644 Terminal.Gui/EnumExtensions/AlignmentModesExtensions.cs create mode 100644 Terminal.Gui/EnumExtensions/BorderSettingsExtensions.cs create mode 100644 Terminal.Gui/EnumExtensions/DimAutoStyleExtensions.cs create mode 100644 Terminal.Gui/EnumExtensions/DimPercentModeExtensions.cs create mode 100644 Terminal.Gui/EnumExtensions/DimensionExtensions.cs create mode 100644 Terminal.Gui/EnumExtensions/KeyBindingScopeExtensions.cs create mode 100644 Terminal.Gui/EnumExtensions/SideExtensions.cs create mode 100644 Terminal.Gui/EnumExtensions/ViewDiagnosticFlagsExtensions.cs diff --git a/Terminal.Gui/EnumExtensions/AddOrSubtractExtensions.cs b/Terminal.Gui/EnumExtensions/AddOrSubtractExtensions.cs new file mode 100644 index 000000000..8fb98d81c --- /dev/null +++ b/Terminal.Gui/EnumExtensions/AddOrSubtractExtensions.cs @@ -0,0 +1,51 @@ +#nullable enable + +using System.CodeDom.Compiler; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Terminal.Gui.EnumExtensions; + +/// Extension methods for the type. +[GeneratedCode ("Terminal.Gui.Analyzers.Internal", "1.0")] +[CompilerGenerated] +[DebuggerNonUserCode] +[ExcludeFromCodeCoverage (Justification = "Generated code is already tested.")] +[PublicAPI] +public static class AddOrSubtractExtensions +{ + /// + /// Directly converts this value to an value with the + /// same binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static int AsInt32 (this AddOrSubtract e) => Unsafe.As (ref e); + + /// + /// Directly converts this value to a value with the + /// same binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static uint AsUInt32 (this AddOrSubtract e) => Unsafe.As (ref e); + + /// + /// Determines if the specified value is explicitly defined as a named value of the + /// type. + /// + /// + /// Only explicitly named values return true, as with IsDefined. Combined valid flag values of flags enums which are + /// not explicitly named will return false. + /// + public static bool FastIsDefined (this AddOrSubtract _, int value) + { + return value switch + { + 0 => true, + 1 => true, + _ => false + }; + } +} diff --git a/Terminal.Gui/EnumExtensions/AlignmentExtensions.cs b/Terminal.Gui/EnumExtensions/AlignmentExtensions.cs new file mode 100644 index 000000000..3666d2de5 --- /dev/null +++ b/Terminal.Gui/EnumExtensions/AlignmentExtensions.cs @@ -0,0 +1,53 @@ +#nullable enable + +using System.CodeDom.Compiler; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Terminal.Gui.EnumExtensions; + +/// Extension methods for the type. +[GeneratedCode ("Terminal.Gui.Analyzers.Internal", "1.0")] +[CompilerGenerated] +[DebuggerNonUserCode] +[ExcludeFromCodeCoverage (Justification = "Generated code is already tested.")] +[PublicAPI] +public static class AlignmentExtensions +{ + /// + /// Directly converts this value to an value with the same + /// binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static int AsInt32 (this Alignment e) => Unsafe.As (ref e); + + /// + /// Directly converts this value to a value with the same + /// binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static uint AsUInt32 (this Alignment e) => Unsafe.As (ref e); + + /// + /// Determines if the specified value is explicitly defined as a named value of the + /// type. + /// + /// + /// Only explicitly named values return true, as with IsDefined. Combined valid flag values of flags enums which are + /// not explicitly named will return false. + /// + public static bool FastIsDefined (this Alignment _, int value) + { + return value switch + { + 0 => true, + 1 => true, + 2 => true, + 3 => true, + _ => false + }; + } +} diff --git a/Terminal.Gui/EnumExtensions/AlignmentModesExtensions.cs b/Terminal.Gui/EnumExtensions/AlignmentModesExtensions.cs new file mode 100644 index 000000000..3babe81b6 --- /dev/null +++ b/Terminal.Gui/EnumExtensions/AlignmentModesExtensions.cs @@ -0,0 +1,90 @@ +#nullable enable + +using System.CodeDom.Compiler; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Terminal.Gui.EnumExtensions; + +/// Extension methods for the type. +[GeneratedCode ("Terminal.Gui.Analyzers.Internal", "1.0")] +[CompilerGenerated] +[DebuggerNonUserCode] +[ExcludeFromCodeCoverage (Justification = "Generated code is already tested.")] +[PublicAPI] +public static class AlignmentModesExtensions +{ + /// + /// Directly converts this value to an value with the + /// same binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static int AsInt32 (this AlignmentModes e) => Unsafe.As (ref e); + + /// + /// Directly converts this value to a value with the + /// same binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static uint AsUInt32 (this AlignmentModes e) => Unsafe.As (ref e); + + /// + /// Determines if the specified flags are set in the current value of this + /// . + /// + /// NO VALIDATION IS PERFORMED! + /// + /// True, if all flags present in are also present in the current value of the + /// .
Otherwise false. + ///
+ [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static bool FastHasFlags (this AlignmentModes e, AlignmentModes checkFlags) + { + ref uint enumCurrentValueRef = ref Unsafe.As (ref e); + ref uint checkFlagsValueRef = ref Unsafe.As (ref checkFlags); + + return (enumCurrentValueRef & checkFlagsValueRef) == checkFlagsValueRef; + } + + /// + /// Determines if the specified mask bits are set in the current value of this + /// . + /// + /// The value to check against the value. + /// A mask to apply to the current value. + /// + /// True, if all bits set to 1 in the mask are also set to 1 in the current value of the + /// .
Otherwise false. + ///
+ /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static bool FastHasFlags (this AlignmentModes e, int mask) + { + ref int enumCurrentValueRef = ref Unsafe.As (ref e); + + return (enumCurrentValueRef & mask) == mask; + } + + /// + /// Determines if the specified value is explicitly defined as a named value of the + /// type. + /// + /// + /// Only explicitly named values return true, as with IsDefined. Combined valid flag values of flags enums which are + /// not explicitly named will return false. + /// + public static bool FastIsDefined (this AlignmentModes _, int value) + { + return value switch + { + 0 => true, + 1 => true, + 2 => true, + 4 => true, + _ => false + }; + } +} diff --git a/Terminal.Gui/EnumExtensions/BorderSettingsExtensions.cs b/Terminal.Gui/EnumExtensions/BorderSettingsExtensions.cs new file mode 100644 index 000000000..074a45976 --- /dev/null +++ b/Terminal.Gui/EnumExtensions/BorderSettingsExtensions.cs @@ -0,0 +1,89 @@ +#nullable enable + +using System.CodeDom.Compiler; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Terminal.Gui.EnumExtensions; + +/// Extension methods for the type. +[GeneratedCode ("Terminal.Gui.Analyzers.Internal", "1.0")] +[CompilerGenerated] +[DebuggerNonUserCode] +[ExcludeFromCodeCoverage (Justification = "Generated code is already tested.")] +[PublicAPI] +public static class BorderSettingsExtensions +{ + /// + /// Directly converts this value to an value with the + /// same binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static int AsInt32 (this BorderSettings e) => Unsafe.As (ref e); + + /// + /// Directly converts this value to a value with the + /// same binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static uint AsUInt32 (this BorderSettings e) => Unsafe.As (ref e); + + /// + /// Determines if the specified flags are set in the current value of this + /// . + /// + /// NO VALIDATION IS PERFORMED! + /// + /// True, if all flags present in are also present in the current value of the + /// .
Otherwise false. + ///
+ [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static bool FastHasFlags (this BorderSettings e, BorderSettings checkFlags) + { + ref uint enumCurrentValueRef = ref Unsafe.As (ref e); + ref uint checkFlagsValueRef = ref Unsafe.As (ref checkFlags); + + return (enumCurrentValueRef & checkFlagsValueRef) == checkFlagsValueRef; + } + + /// + /// Determines if the specified mask bits are set in the current value of this + /// . + /// + /// The value to check against the value. + /// A mask to apply to the current value. + /// + /// True, if all bits set to 1 in the mask are also set to 1 in the current value of the + /// .
Otherwise false. + ///
+ /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static bool FastHasFlags (this BorderSettings e, int mask) + { + ref int enumCurrentValueRef = ref Unsafe.As (ref e); + + return (enumCurrentValueRef & mask) == mask; + } + + /// + /// Determines if the specified value is explicitly defined as a named value of the + /// type. + /// + /// + /// Only explicitly named values return true, as with IsDefined. Combined valid flag values of flags enums which are + /// not explicitly named will return false. + /// + public static bool FastIsDefined (this BorderSettings _, int value) + { + return value switch + { + 0 => true, + 1 => true, + 2 => true, + _ => false + }; + } +} diff --git a/Terminal.Gui/EnumExtensions/DimAutoStyleExtensions.cs b/Terminal.Gui/EnumExtensions/DimAutoStyleExtensions.cs new file mode 100644 index 000000000..6c0813df8 --- /dev/null +++ b/Terminal.Gui/EnumExtensions/DimAutoStyleExtensions.cs @@ -0,0 +1,89 @@ +#nullable enable + +using System.CodeDom.Compiler; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Terminal.Gui.EnumExtensions; + +/// Extension methods for the type. +[GeneratedCode ("Terminal.Gui.Analyzers.Internal", "1.0")] +[CompilerGenerated] +[DebuggerNonUserCode] +[ExcludeFromCodeCoverage (Justification = "Generated code is already tested.")] +[PublicAPI] +public static class DimAutoStyleExtensions +{ + /// + /// Directly converts this value to an value with the + /// same binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static int AsInt32 (this DimAutoStyle e) => Unsafe.As (ref e); + + /// + /// Directly converts this value to a value with the + /// same binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static uint AsUInt32 (this DimAutoStyle e) => Unsafe.As (ref e); + + /// + /// Determines if the specified flags are set in the current value of this + /// . + /// + /// NO VALIDATION IS PERFORMED! + /// + /// True, if all flags present in are also present in the current value of the + /// .
Otherwise false. + ///
+ [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static bool FastHasFlags (this DimAutoStyle e, DimAutoStyle checkFlags) + { + ref uint enumCurrentValueRef = ref Unsafe.As (ref e); + ref uint checkFlagsValueRef = ref Unsafe.As (ref checkFlags); + + return (enumCurrentValueRef & checkFlagsValueRef) == checkFlagsValueRef; + } + + /// + /// Determines if the specified mask bits are set in the current value of this + /// . + /// + /// The value to check against the value. + /// A mask to apply to the current value. + /// + /// True, if all bits set to 1 in the mask are also set to 1 in the current value of the + /// .
Otherwise false. + ///
+ /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static bool FastHasFlags (this DimAutoStyle e, int mask) + { + ref int enumCurrentValueRef = ref Unsafe.As (ref e); + + return (enumCurrentValueRef & mask) == mask; + } + + /// + /// Determines if the specified value is explicitly defined as a named value of the + /// type. + /// + /// + /// Only explicitly named values return true, as with IsDefined. Combined valid flag values of flags enums which are + /// not explicitly named will return false. + /// + public static bool FastIsDefined (this DimAutoStyle _, int value) + { + return value switch + { + 1 => true, + 2 => true, + 3 => true, + _ => false + }; + } +} diff --git a/Terminal.Gui/EnumExtensions/DimPercentModeExtensions.cs b/Terminal.Gui/EnumExtensions/DimPercentModeExtensions.cs new file mode 100644 index 000000000..2fc943f17 --- /dev/null +++ b/Terminal.Gui/EnumExtensions/DimPercentModeExtensions.cs @@ -0,0 +1,51 @@ +#nullable enable + +using System.CodeDom.Compiler; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Terminal.Gui.EnumExtensions; + +/// Extension methods for the type. +[GeneratedCode ("Terminal.Gui.Analyzers.Internal", "1.0")] +[CompilerGenerated] +[DebuggerNonUserCode] +[ExcludeFromCodeCoverage (Justification = "Generated code is already tested.")] +[PublicAPI] +public static class DimPercentModeExtensions +{ + /// + /// Directly converts this value to an value with the + /// same binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static int AsInt32 (this DimPercentMode e) => Unsafe.As (ref e); + + /// + /// Directly converts this value to a value with the + /// same binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static uint AsUInt32 (this DimPercentMode e) => Unsafe.As (ref e); + + /// + /// Determines if the specified value is explicitly defined as a named value of the + /// type. + /// + /// + /// Only explicitly named values return true, as with IsDefined. Combined valid flag values of flags enums which are + /// not explicitly named will return false. + /// + public static bool FastIsDefined (this DimPercentMode _, int value) + { + return value switch + { + 0 => true, + 1 => true, + _ => false + }; + } +} diff --git a/Terminal.Gui/EnumExtensions/DimensionExtensions.cs b/Terminal.Gui/EnumExtensions/DimensionExtensions.cs new file mode 100644 index 000000000..ccbfbf5ed --- /dev/null +++ b/Terminal.Gui/EnumExtensions/DimensionExtensions.cs @@ -0,0 +1,51 @@ +#nullable enable + +using System.CodeDom.Compiler; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Terminal.Gui.EnumExtensions; + +/// Extension methods for the type. +[GeneratedCode ("Terminal.Gui.Analyzers.Internal", "1.0")] +[CompilerGenerated] +[DebuggerNonUserCode] +[ExcludeFromCodeCoverage (Justification = "Generated code is already tested.")] +public static class DimensionExtensions +{ + /// + /// Directly converts this value to an value with the same + /// binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static int AsInt32 (this Dimension e) => Unsafe.As (ref e); + + /// + /// Directly converts this value to a value with the same + /// binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static uint AsUInt32 (this Dimension e) => Unsafe.As (ref e); + + /// + /// Determines if the specified value is explicitly defined as a named value of the + /// type. + /// + /// + /// Only explicitly named values return true, as with IsDefined. Combined valid flag values of flags enums which are + /// not explicitly named will return false. + /// + public static bool FastIsDefined (this Dimension _, int value) + { + return value switch + { + 0 => true, + 1 => true, + 2 => true, + _ => false + }; + } +} diff --git a/Terminal.Gui/EnumExtensions/KeyBindingScopeExtensions.cs b/Terminal.Gui/EnumExtensions/KeyBindingScopeExtensions.cs new file mode 100644 index 000000000..6f42f4c82 --- /dev/null +++ b/Terminal.Gui/EnumExtensions/KeyBindingScopeExtensions.cs @@ -0,0 +1,93 @@ +#nullable enable + +using System.CodeDom.Compiler; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Terminal.Gui.EnumExtensions; + +/// Extension methods for the type. +[GeneratedCode ("Terminal.Gui.Analyzers.Internal", "1.0")] +[CompilerGenerated] +[DebuggerNonUserCode] +[ExcludeFromCodeCoverage (Justification = "Generated code is already tested.")] +[PublicAPI] +public static class KeyBindingScopeExtensions +{ + /// + /// Directly converts this value to an value with the + /// same binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static int AsInt32 (this KeyBindingScope e) => Unsafe.As (ref e); + + /// + /// Directly converts this value to a value with the + /// same binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static uint AsUInt32 (this KeyBindingScope e) => Unsafe.As (ref e); + + /// + /// Determines if the specified flags are set in the current value of this + /// . + /// + /// NO VALIDATION IS PERFORMED! + /// + /// True, if all flags present in are also present in the current value of the + /// .
Otherwise false. + ///
+ [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static bool FastHasFlags (this KeyBindingScope e, KeyBindingScope checkFlags) + { + ref uint enumCurrentValueRef = ref Unsafe.As (ref e); + ref uint checkFlagsValueRef = ref Unsafe.As (ref checkFlags); + + return (enumCurrentValueRef & checkFlagsValueRef) == checkFlagsValueRef; + } + + /// + /// Determines if the specified mask bits are set in the current value of this + /// . + /// + /// + /// The value to check against the + /// value. + /// + /// A mask to apply to the current value. + /// + /// True, if all bits set to 1 in the mask are also set to 1 in the current value of the + /// .
Otherwise false. + ///
+ /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static bool FastHasFlags (this KeyBindingScope e, int mask) + { + ref int enumCurrentValueRef = ref Unsafe.As (ref e); + + return (enumCurrentValueRef & mask) == mask; + } + + /// + /// Determines if the specified value is explicitly defined as a named value of the + /// type. + /// + /// + /// Only explicitly named values return true, as with IsDefined. Combined valid flag values of flags enums which are + /// not explicitly named will return false. + /// + public static bool FastIsDefined (this KeyBindingScope _, int value) + { + return value switch + { + 0 => true, + 1 => true, + 2 => true, + 4 => true, + _ => false + }; + } +} diff --git a/Terminal.Gui/EnumExtensions/SideExtensions.cs b/Terminal.Gui/EnumExtensions/SideExtensions.cs new file mode 100644 index 000000000..b50e12bdc --- /dev/null +++ b/Terminal.Gui/EnumExtensions/SideExtensions.cs @@ -0,0 +1,53 @@ +#nullable enable + +using System.CodeDom.Compiler; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Terminal.Gui.EnumExtensions; + +/// Extension methods for the type. +[GeneratedCode ("Terminal.Gui.Analyzers.Internal", "1.0")] +[CompilerGenerated] +[DebuggerNonUserCode] +[ExcludeFromCodeCoverage (Justification = "Generated code is already tested.")] +[PublicAPI] +public static class SideExtensions +{ + /// + /// Directly converts this value to an value with the same binary + /// representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static int AsInt32 (this Side e) => Unsafe.As (ref e); + + /// + /// Directly converts this value to a value with the same binary + /// representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static uint AsUInt32 (this Side e) => Unsafe.As (ref e); + + /// + /// Determines if the specified value is explicitly defined as a named value of the + /// type. + /// + /// + /// Only explicitly named values return true, as with IsDefined. Combined valid flag values of flags enums which are + /// not explicitly named will return false. + /// + public static bool FastIsDefined (this Side _, int value) + { + return value switch + { + 0 => true, + 1 => true, + 2 => true, + 3 => true, + _ => false + }; + } +} diff --git a/Terminal.Gui/EnumExtensions/ViewDiagnosticFlagsExtensions.cs b/Terminal.Gui/EnumExtensions/ViewDiagnosticFlagsExtensions.cs new file mode 100644 index 000000000..1aa18fe72 --- /dev/null +++ b/Terminal.Gui/EnumExtensions/ViewDiagnosticFlagsExtensions.cs @@ -0,0 +1,93 @@ +#nullable enable + +using System.CodeDom.Compiler; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace Terminal.Gui.EnumExtensions; + +/// Extension methods for the type. +[GeneratedCode ("Terminal.Gui.Analyzers.Internal", "1.0")] +[CompilerGenerated] +[DebuggerNonUserCode] +[ExcludeFromCodeCoverage (Justification = "Generated code is already tested.")] +[PublicAPI] +public static class ViewDiagnosticFlagsExtensions +{ + /// + /// Directly converts this value to an value with + /// the same binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static int AsInt32 (this ViewDiagnosticFlags e) => Unsafe.As (ref e); + + /// + /// Directly converts this value to a value with + /// the same binary representation. + /// + /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static uint AsUInt32 (this ViewDiagnosticFlags e) => Unsafe.As (ref e); + + /// + /// Determines if the specified flags are set in the current value of this + /// . + /// + /// NO VALIDATION IS PERFORMED! + /// + /// True, if all flags present in are also present in the current value of the + /// .
Otherwise false. + ///
+ [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static bool FastHasFlags (this ViewDiagnosticFlags e, ViewDiagnosticFlags checkFlags) + { + ref uint enumCurrentValueRef = ref Unsafe.As (ref e); + ref uint checkFlagsValueRef = ref Unsafe.As (ref checkFlags); + + return (enumCurrentValueRef & checkFlagsValueRef) == checkFlagsValueRef; + } + + /// + /// Determines if the specified mask bits are set in the current value of this + /// . + /// + /// + /// The value to check against the + /// value. + /// + /// A mask to apply to the current value. + /// + /// True, if all bits set to 1 in the mask are also set to 1 in the current value of the + /// .
Otherwise false. + ///
+ /// NO VALIDATION IS PERFORMED! + [MethodImpl (MethodImplOptions.AggressiveInlining)] + public static bool FastHasFlags (this ViewDiagnosticFlags e, uint mask) + { + ref uint enumCurrentValueRef = ref Unsafe.As (ref e); + + return (enumCurrentValueRef & mask) == mask; + } + + /// + /// Determines if the specified value is explicitly defined as a named value of the + /// type. + /// + /// + /// Only explicitly named values return true, as with IsDefined. Combined valid flag values of flags enums which are + /// not explicitly named will return false. + /// + public static bool FastIsDefined (this ViewDiagnosticFlags _, uint value) + { + return value switch + { + 0 => true, + 1 => true, + 2 => true, + 4 => true, + _ => false + }; + } +} From b55e3a9b193d5d4f45a42b35dd2cb30b5bce3b03 Mon Sep 17 00:00:00 2001 From: Brandon Thetford Date: Tue, 9 Jul 2024 21:47:34 -0700 Subject: [PATCH 45/48] Remove attributes and usings for old forms --- Terminal.Gui/Drawing/Alignment.cs | 4 +--- Terminal.Gui/Drawing/AlignmentModes.cs | 3 +-- Terminal.Gui/Input/KeyBindingScope.cs | 4 ++-- Terminal.Gui/Terminal.Gui.csproj | 3 ++- Terminal.Gui/View/Adornment/BorderSettings.cs | 4 ++-- Terminal.Gui/View/Layout/AddOrSubtract.cs | 5 +---- Terminal.Gui/View/Layout/DimAutoStyle.cs | 4 ++-- Terminal.Gui/View/Layout/DimPercentMode.cs | 4 +--- Terminal.Gui/View/Layout/Dimension.cs | 4 +--- Terminal.Gui/View/Layout/Side.cs | 3 +-- Terminal.Gui/View/ViewDiagnostics.cs | 3 +-- UICatalog/UICatalog.csproj | 1 + 12 files changed, 16 insertions(+), 26 deletions(-) diff --git a/Terminal.Gui/Drawing/Alignment.cs b/Terminal.Gui/Drawing/Alignment.cs index 40061a8c1..6a160096f 100644 --- a/Terminal.Gui/Drawing/Alignment.cs +++ b/Terminal.Gui/Drawing/Alignment.cs @@ -1,12 +1,10 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; + namespace Terminal.Gui; /// /// Determines the position of items when arranged in a container. /// -[GenerateEnumExtensionMethods (FastHasFlags = true)] - public enum Alignment { /// diff --git a/Terminal.Gui/Drawing/AlignmentModes.cs b/Terminal.Gui/Drawing/AlignmentModes.cs index 4de4d5c98..b7e0bb87e 100644 --- a/Terminal.Gui/Drawing/AlignmentModes.cs +++ b/Terminal.Gui/Drawing/AlignmentModes.cs @@ -1,4 +1,4 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; + namespace Terminal.Gui; @@ -6,7 +6,6 @@ namespace Terminal.Gui; /// Determines alignment modes for . /// [Flags] -[GenerateEnumExtensionMethods (FastHasFlags = true)] public enum AlignmentModes { /// diff --git a/Terminal.Gui/Input/KeyBindingScope.cs b/Terminal.Gui/Input/KeyBindingScope.cs index 3b6c53ebc..0c75299c7 100644 --- a/Terminal.Gui/Input/KeyBindingScope.cs +++ b/Terminal.Gui/Input/KeyBindingScope.cs @@ -1,4 +1,4 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; + namespace Terminal.Gui; @@ -10,7 +10,7 @@ namespace Terminal.Gui; /// Key bindings are scoped to the most-focused view () by default. /// [Flags] -[GenerateEnumExtensionMethods (FastHasFlags = true)] + public enum KeyBindingScope { /// The key binding is disabled. diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj index 21926c19d..3b312dcdd 100644 --- a/Terminal.Gui/Terminal.Gui.csproj +++ b/Terminal.Gui/Terminal.Gui.csproj @@ -1,4 +1,4 @@ - + @@ -68,6 +68,7 @@ + diff --git a/Terminal.Gui/View/Adornment/BorderSettings.cs b/Terminal.Gui/View/Adornment/BorderSettings.cs index 5829d1ed6..7b4846d34 100644 --- a/Terminal.Gui/View/Adornment/BorderSettings.cs +++ b/Terminal.Gui/View/Adornment/BorderSettings.cs @@ -1,4 +1,4 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; + namespace Terminal.Gui; @@ -6,7 +6,7 @@ namespace Terminal.Gui; /// Determines the settings for . /// [Flags] -[GenerateEnumExtensionMethods (FastHasFlags = true)] + public enum BorderSettings { /// diff --git a/Terminal.Gui/View/Layout/AddOrSubtract.cs b/Terminal.Gui/View/Layout/AddOrSubtract.cs index e03cfbcfd..83d1dd12c 100644 --- a/Terminal.Gui/View/Layout/AddOrSubtract.cs +++ b/Terminal.Gui/View/Layout/AddOrSubtract.cs @@ -1,11 +1,8 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; - -namespace Terminal.Gui; +namespace Terminal.Gui; /// /// Describes whether an operation should add or subtract values. /// -[GenerateEnumExtensionMethods] public enum AddOrSubtract { /// diff --git a/Terminal.Gui/View/Layout/DimAutoStyle.cs b/Terminal.Gui/View/Layout/DimAutoStyle.cs index f350e8045..ee712f0fb 100644 --- a/Terminal.Gui/View/Layout/DimAutoStyle.cs +++ b/Terminal.Gui/View/Layout/DimAutoStyle.cs @@ -1,4 +1,4 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; + namespace Terminal.Gui; @@ -6,7 +6,7 @@ namespace Terminal.Gui; /// Specifies how will compute the dimension. /// [Flags] -[GenerateEnumExtensionMethods (FastHasFlags = true)] + public enum DimAutoStyle { /// diff --git a/Terminal.Gui/View/Layout/DimPercentMode.cs b/Terminal.Gui/View/Layout/DimPercentMode.cs index 60a7da056..10077848b 100644 --- a/Terminal.Gui/View/Layout/DimPercentMode.cs +++ b/Terminal.Gui/View/Layout/DimPercentMode.cs @@ -1,12 +1,10 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; + namespace Terminal.Gui; /// /// Indicates the mode for a object. /// -[GenerateEnumExtensionMethods] - public enum DimPercentMode { /// diff --git a/Terminal.Gui/View/Layout/Dimension.cs b/Terminal.Gui/View/Layout/Dimension.cs index cc56ffd4b..8cfb3f7f0 100644 --- a/Terminal.Gui/View/Layout/Dimension.cs +++ b/Terminal.Gui/View/Layout/Dimension.cs @@ -1,12 +1,10 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; + namespace Terminal.Gui; /// /// Indicates the dimension for operations. /// - -[GenerateEnumExtensionMethods] public enum Dimension { /// diff --git a/Terminal.Gui/View/Layout/Side.cs b/Terminal.Gui/View/Layout/Side.cs index 6708904da..afdc5640e 100644 --- a/Terminal.Gui/View/Layout/Side.cs +++ b/Terminal.Gui/View/Layout/Side.cs @@ -1,4 +1,4 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; + namespace Terminal.Gui; @@ -6,7 +6,6 @@ namespace Terminal.Gui; /// Indicates the side for operations. /// /// -[GenerateEnumExtensionMethods] public enum Side { /// diff --git a/Terminal.Gui/View/ViewDiagnostics.cs b/Terminal.Gui/View/ViewDiagnostics.cs index 20899cfd0..c7ac7b851 100644 --- a/Terminal.Gui/View/ViewDiagnostics.cs +++ b/Terminal.Gui/View/ViewDiagnostics.cs @@ -1,11 +1,10 @@  -using Terminal.Gui.Analyzers.Internal.Attributes; + namespace Terminal.Gui; /// Enables diagnostic functions for . [Flags] -[GenerateEnumExtensionMethods(FastHasFlags = true)] public enum ViewDiagnosticFlags : uint { /// All diagnostics off diff --git a/UICatalog/UICatalog.csproj b/UICatalog/UICatalog.csproj index 1295f4f5a..df715a3de 100644 --- a/UICatalog/UICatalog.csproj +++ b/UICatalog/UICatalog.csproj @@ -45,6 +45,7 @@ + From 1fa3a6bbe3e9f09b81ef1bfc8132295a8abfb974 Mon Sep 17 00:00:00 2001 From: Brandon Thetford Date: Tue, 9 Jul 2024 21:51:26 -0700 Subject: [PATCH 46/48] Remove the Analyzers directory --- Analyzers/Directory.Build.props | 23 - .../Program.cs | 23 - ...al.Gui.Analyzers.Internal.Debugging.csproj | 29 -- .../EnumDefinitions/EnumMemberValues.cs | 42 -- .../WithGenerator/BetterEnum.cs | 51 -- .../WithGenerator/BetterEnum_ExplicitInt.cs | 51 -- .../BetterEnum_ExplicitInt_NoFastIsDefined.cs | 52 -- .../WithGenerator/BetterEnum_ExplicitUInt.cs | 51 -- ...BetterEnum_ExplicitUInt_NoFastIsDefined.cs | 51 -- .../BetterEnum_NoFastIsDefined.cs | 51 -- .../WithGenerator/BetterFlagsEnum.cs | 52 -- .../BetterFlagsEnum_ExplicitInt.cs | 53 -- .../BetterFlagsEnum_ExplicitUInt.cs | 52 -- .../WithoutGenerator/BasicEnum.cs | 48 -- .../WithoutGenerator/BasicEnum_ExplicitInt.cs | 50 -- .../BasicEnum_ExplicitUint.cs | 48 -- .../WithoutGenerator/FlagsEnum.cs | 45 -- .../WithoutGenerator/FlagsEnum_ExplicitInt.cs | 45 -- .../FlagsEnum_ExplicitUInt.cs | 45 -- ...tensionMethodsIncrementalGeneratorTests.cs | 306 ------------ .../GlobalSuppressions.cs | 3 - .../IndentedTextWriterExtensionsTests.cs | 111 ----- ...rminal.Gui.Analyzers.Internal.Tests.csproj | 52 -- ...nalyzers.Internal.Tests.csproj.DotSettings | 3 - .../AccessibilityExtensions.cs | 20 - .../AnalyzerReleases.Shipped.md | 8 - .../AnalyzerReleases.Unshipped.md | 4 - ...teEnumExtensionMethodsAttributeAnalyzer.cs | 117 ----- .../ApiCompatExcludedAttributes.txt | 3 - .../AssemblyExtendedEnumTypeAttribute.cs | 27 -- .../ExtensionsForEnumTypeAttribute.cs | 37 -- .../GenerateEnumExtensionMethodsAttribute.cs | 110 ----- .../IExtensionsForEnumTypeAttribute.cs | 14 - .../Compatibility/IEqualityOperators.cs | 11 - .../Compatibility/IntrinsicAttribute.cs | 6 - .../Compatibility/NumericExtensions.cs | 43 -- .../Constants/Strings.cs | 204 -------- .../Generators/EnumExtensions/CodeWriter.cs | 235 --------- .../EnumExtensionMethodsGenerationInfo.cs | 443 ----------------- ...numExtensionMethodsIncrementalGenerator.cs | 452 ------------------ .../GlobalSuppressions.cs | 3 - .../IGeneratedTypeMetadata.cs | 38 -- .../IStandardCSharpCodeGenerator.cs | 28 -- .../IndentedTextWriterExtensions.cs | 71 --- .../Properties/launchSettings.json | 8 - .../Terminal.Gui.Analyzers.Internal.csproj | 67 --- ....Gui.Analyzers.Internal.csproj.DotSettings | 4 - 47 files changed, 3290 deletions(-) delete mode 100644 Analyzers/Directory.Build.props delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Debugging/Program.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Debugging/Terminal.Gui.Analyzers.Internal.Debugging.csproj delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/EnumMemberValues.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitInt.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitInt_NoFastIsDefined.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitUInt.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitUInt_NoFastIsDefined.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_NoFastIsDefined.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum_ExplicitInt.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum_ExplicitUInt.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum_ExplicitInt.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum_ExplicitUint.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum_ExplicitInt.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum_ExplicitUInt.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumExtensionMethodsIncrementalGeneratorTests.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/GlobalSuppressions.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/IndentedTextWriterExtensionsTests.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Terminal.Gui.Analyzers.Internal.Tests.csproj delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Terminal.Gui.Analyzers.Internal.Tests.csproj.DotSettings delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/AccessibilityExtensions.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/AnalyzerReleases.Shipped.md delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/AnalyzerReleases.Unshipped.md delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/Analyzers/GenerateEnumExtensionMethodsAttributeAnalyzer.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/ApiCompatExcludedAttributes.txt delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/AssemblyExtendedEnumTypeAttribute.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/ExtensionsForEnumTypeAttribute.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/GenerateEnumExtensionMethodsAttribute.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/IExtensionsForEnumTypeAttribute.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IEqualityOperators.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IntrinsicAttribute.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/NumericExtensions.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/Constants/Strings.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/CodeWriter.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumExtensionMethodsGenerationInfo.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumExtensionMethodsIncrementalGenerator.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/GlobalSuppressions.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/IGeneratedTypeMetadata.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/IStandardCSharpCodeGenerator.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/IndentedTextWriterExtensions.cs delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/Properties/launchSettings.json delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/Terminal.Gui.Analyzers.Internal.csproj delete mode 100644 Analyzers/Terminal.Gui.Analyzers.Internal/Terminal.Gui.Analyzers.Internal.csproj.DotSettings diff --git a/Analyzers/Directory.Build.props b/Analyzers/Directory.Build.props deleted file mode 100644 index 6a6ec1589..000000000 --- a/Analyzers/Directory.Build.props +++ /dev/null @@ -1,23 +0,0 @@ - - - enable - latest-recommended - 8 - UTF-8 - true - true - $(DefineConstants);JETBRAINS_ANNOTATIONS;CONTRACTS_FULL;CODE_ANALYSIS - True - True - - - - - - - - - - - - \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Debugging/Program.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Debugging/Program.cs deleted file mode 100644 index 559424a1b..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Debugging/Program.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Terminal.Gui.Analyzers.Internal.Attributes; - -namespace Terminal.Gui.Analyzers.Internal.Debugging; - -static class Program -{ - static void Main (string [] args) - { - - } -} - -[GenerateEnumExtensionMethods] -[SuppressMessage ("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification = "It's not that deep")] -public enum TestEnum -{ - Zero = 0, - One, - Two = 2, - Three, - Six = 6 -} \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Debugging/Terminal.Gui.Analyzers.Internal.Debugging.csproj b/Analyzers/Terminal.Gui.Analyzers.Internal.Debugging/Terminal.Gui.Analyzers.Internal.Debugging.csproj deleted file mode 100644 index 5594dd0da..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Debugging/Terminal.Gui.Analyzers.Internal.Debugging.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - - Exe - net8.0 - enable - - - - - - - - - - - - - all - Analyzer - true - - - - - - - - diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/EnumMemberValues.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/EnumMemberValues.cs deleted file mode 100644 index 0846f8e90..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/EnumMemberValues.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions; -internal sealed class SignedEnumMemberValues -{ - internal const int Bit31 = ~0b_01111111_11111111_11111111_11111111; - internal const int Bit30 = 0b_01000000_00000000_00000000_00000000; - internal const int Bit29 = 0b_00100000_00000000_00000000_00000000; - internal const int Bit28 = 0b_00010000_00000000_00000000_00000000; - internal const int Bit27 = 0b_00001000_00000000_00000000_00000000; - internal const int Bit26 = 0b_00000100_00000000_00000000_00000000; - internal const int Bit25 = 0b_00000010_00000000_00000000_00000000; - internal const int Bit24 = 0b_00000001_00000000_00000000_00000000; - internal const int Bit23 = 0b_00000000_10000000_00000000_00000000; - internal const int Bit22 = 0b_00000000_01000000_00000000_00000000; - internal const int Bit21 = 0b_00000000_00100000_00000000_00000000; - internal const int Bit20 = 0b_00000000_00010000_00000000_00000000; - internal const int Bit19 = 0b_00000000_00001000_00000000_00000000; - internal const int Bit18 = 0b_00000000_00000100_00000000_00000000; - internal const int Bit17 = 0b_00000000_00000010_00000000_00000000; - internal const int Bit16 = 0b_00000000_00000001_00000000_00000000; - internal const int Bit15 = 0b_00000000_00000000_10000000_00000000; - internal const int Bit14 = 0b_00000000_00000000_01000000_00000000; - internal const int Bit13 = 0b_00000000_00000000_00100000_00000000; - internal const int Bit12 = 0b_00000000_00000000_00010000_00000000; - internal const int Bit11 = 0b_00000000_00000000_00001000_00000000; - internal const int Bit10 = 0b_00000000_00000000_00000100_00000000; - internal const int Bit09 = 0b_00000000_00000000_00000010_00000000; - internal const int Bit08 = 0b_00000000_00000000_00000001_00000000; - internal const int Bit07 = 0b_00000000_00000000_00000000_10000000; - internal const int Bit06 = 0b_00000000_00000000_00000000_01000000; - internal const int Bit05 = 0b_00000000_00000000_00000000_00100000; - internal const int Bit04 = 0b_00000000_00000000_00000000_00010000; - internal const int Bit03 = 0b_00000000_00000000_00000000_00001000; - internal const int Bit02 = 0b_00000000_00000000_00000000_00000100; - internal const int Bit01 = 0b_00000000_00000000_00000000_00000010; - internal const int Bit00 = 0b_00000000_00000000_00000000_00000001; - internal const int All_0 = 0; - internal const int All_1 = ~All_0; - internal const int Alternating_01 = 0b_01010101_01010101_01010101_01010101; - internal const int Alternating_10 = ~Alternating_01; - internal const int EvenBytesHigh = 0b_00000000_11111111_00000000_11111111; - internal const int OddBytesHigh = ~EvenBytesHigh; -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum.cs deleted file mode 100644 index 0fb09b8d9..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; - -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions; - -/// -/// Same as , but with applied. -/// -[GenerateEnumExtensionMethods] -[SuppressMessage ("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification = "Naming is intentional.")] -[SuppressMessage ("Roslynator", "RCS1154:Sort enum members", Justification = "Order is intentional.")] -public enum BetterEnum -{ - Bit31 = -0b_10000000_00000000_00000000_00000000, - Bit30 = 0b_01000000_00000000_00000000_00000000, - Bit29 = 0b_00100000_00000000_00000000_00000000, - Bit28 = 0b_00010000_00000000_00000000_00000000, - Bit27 = 0b_00001000_00000000_00000000_00000000, - Bit26 = 0b_00000100_00000000_00000000_00000000, - Bit25 = 0b_00000010_00000000_00000000_00000000, - Bit24 = 0b_00000001_00000000_00000000_00000000, - Bit23 = 0b_00000000_10000000_00000000_00000000, - Bit22 = 0b_00000000_01000000_00000000_00000000, - Bit21 = 0b_00000000_00100000_00000000_00000000, - Bit20 = 0b_00000000_00010000_00000000_00000000, - Bit19 = 0b_00000000_00001000_00000000_00000000, - Bit18 = 0b_00000000_00000100_00000000_00000000, - Bit17 = 0b_00000000_00000010_00000000_00000000, - Bit16 = 0b_00000000_00000001_00000000_00000000, - Bit15 = 0b_00000000_00000000_10000000_00000000, - Bit14 = 0b_00000000_00000000_01000000_00000000, - Bit13 = 0b_00000000_00000000_00100000_00000000, - Bit12 = 0b_00000000_00000000_00010000_00000000, - Bit11 = 0b_00000000_00000000_00001000_00000000, - Bit10 = 0b_00000000_00000000_00000100_00000000, - Bit09 = 0b_00000000_00000000_00000010_00000000, - Bit08 = 0b_00000000_00000000_00000001_00000000, - Bit07 = 0b_00000000_00000000_00000000_10000000, - Bit06 = 0b_00000000_00000000_00000000_01000000, - Bit05 = 0b_00000000_00000000_00000000_00100000, - Bit04 = 0b_00000000_00000000_00000000_00010000, - Bit03 = 0b_00000000_00000000_00000000_00001000, - Bit02 = 0b_00000000_00000000_00000000_00000100, - Bit01 = 0b_00000000_00000000_00000000_00000010, - Bit00 = 0b_00000000_00000000_00000000_00000001, - All_0 = 0, - All_1 = ~All_0, - Alternating_01 = 0b_01010101_01010101_01010101_01010101, - Alternating_10 = ~Alternating_01, - EvenBytesHigh = 0b_00000000_11111111_00000000_11111111, - OddBytesHigh = ~EvenBytesHigh, -} \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitInt.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitInt.cs deleted file mode 100644 index 535f0448f..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitInt.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; - -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions; - -/// -/// Same as , but with applied. -/// -[GenerateEnumExtensionMethods] -[SuppressMessage ("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification = "Naming is intentional.")] -[SuppressMessage ("Roslynator", "RCS1154:Sort enum members", Justification = "Order is intentional.")] -public enum BetterEnum_ExplicitInt -{ - Bit31 = BasicEnum_ExplicitInt.Bit31, - Bit30 = BasicEnum_ExplicitInt.Bit30, - Bit29 = BasicEnum_ExplicitInt.Bit29, - Bit28 = BasicEnum_ExplicitInt.Bit28, - Bit27 = BasicEnum_ExplicitInt.Bit27, - Bit26 = BasicEnum_ExplicitInt.Bit26, - Bit25 = BasicEnum_ExplicitInt.Bit25, - Bit24 = BasicEnum_ExplicitInt.Bit24, - Bit23 = BasicEnum_ExplicitInt.Bit23, - Bit22 = BasicEnum_ExplicitInt.Bit22, - Bit21 = BasicEnum_ExplicitInt.Bit21, - Bit20 = BasicEnum_ExplicitInt.Bit20, - Bit19 = BasicEnum_ExplicitInt.Bit19, - Bit18 = BasicEnum_ExplicitInt.Bit18, - Bit17 = BasicEnum_ExplicitInt.Bit17, - Bit16 = BasicEnum_ExplicitInt.Bit16, - Bit15 = BasicEnum_ExplicitInt.Bit15, - Bit14 = BasicEnum_ExplicitInt.Bit14, - Bit13 = BasicEnum_ExplicitInt.Bit13, - Bit12 = BasicEnum_ExplicitInt.Bit12, - Bit11 = BasicEnum_ExplicitInt.Bit11, - Bit10 = BasicEnum_ExplicitInt.Bit10, - Bit09 = BasicEnum_ExplicitInt.Bit09, - Bit08 = BasicEnum_ExplicitInt.Bit08, - Bit07 = BasicEnum_ExplicitInt.Bit07, - Bit06 = BasicEnum_ExplicitInt.Bit06, - Bit05 = BasicEnum_ExplicitInt.Bit05, - Bit04 = BasicEnum_ExplicitInt.Bit04, - Bit03 = BasicEnum_ExplicitInt.Bit03, - Bit02 = BasicEnum_ExplicitInt.Bit02, - Bit01 = BasicEnum_ExplicitInt.Bit01, - Bit00 = BasicEnum_ExplicitInt.Bit00, - All_0 = BasicEnum_ExplicitInt.All_0, - All_1 = BasicEnum_ExplicitInt.All_1, - Alternating_01 = BasicEnum_ExplicitInt.Alternating_01, - Alternating_10 = BasicEnum_ExplicitInt.Alternating_10, - EvenBytesHigh = BasicEnum_ExplicitInt.EvenBytesHigh, - OddBytesHigh = BasicEnum_ExplicitInt.OddBytesHigh -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitInt_NoFastIsDefined.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitInt_NoFastIsDefined.cs deleted file mode 100644 index 3b193b54c..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitInt_NoFastIsDefined.cs +++ /dev/null @@ -1,52 +0,0 @@ -// ReSharper disable EnumUnderlyingTypeIsInt -using Terminal.Gui.Analyzers.Internal.Attributes; - -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions; - -/// -/// Same as , but with = . -/// -[GenerateEnumExtensionMethods (FastIsDefined = false)] -[SuppressMessage ("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification = "Naming is intentional.")] -[SuppressMessage ("Roslynator", "RCS1154:Sort enum members", Justification = "Order is intentional.")] -public enum BetterEnum_ExplicitInt_NoFastIsDefined : int -{ - Bit31 = -0b_10000000_00000000_00000000_00000000, - Bit30 = 0b_01000000_00000000_00000000_00000000, - Bit29 = 0b_00100000_00000000_00000000_00000000, - Bit28 = 0b_00010000_00000000_00000000_00000000, - Bit27 = 0b_00001000_00000000_00000000_00000000, - Bit26 = 0b_00000100_00000000_00000000_00000000, - Bit25 = 0b_00000010_00000000_00000000_00000000, - Bit24 = 0b_00000001_00000000_00000000_00000000, - Bit23 = 0b_00000000_10000000_00000000_00000000, - Bit22 = 0b_00000000_01000000_00000000_00000000, - Bit21 = 0b_00000000_00100000_00000000_00000000, - Bit20 = 0b_00000000_00010000_00000000_00000000, - Bit19 = 0b_00000000_00001000_00000000_00000000, - Bit18 = 0b_00000000_00000100_00000000_00000000, - Bit17 = 0b_00000000_00000010_00000000_00000000, - Bit16 = 0b_00000000_00000001_00000000_00000000, - Bit15 = 0b_00000000_00000000_10000000_00000000, - Bit14 = 0b_00000000_00000000_01000000_00000000, - Bit13 = 0b_00000000_00000000_00100000_00000000, - Bit12 = 0b_00000000_00000000_00010000_00000000, - Bit11 = 0b_00000000_00000000_00001000_00000000, - Bit10 = 0b_00000000_00000000_00000100_00000000, - Bit09 = 0b_00000000_00000000_00000010_00000000, - Bit08 = 0b_00000000_00000000_00000001_00000000, - Bit07 = 0b_00000000_00000000_00000000_10000000, - Bit06 = 0b_00000000_00000000_00000000_01000000, - Bit05 = 0b_00000000_00000000_00000000_00100000, - Bit04 = 0b_00000000_00000000_00000000_00010000, - Bit03 = 0b_00000000_00000000_00000000_00001000, - Bit02 = 0b_00000000_00000000_00000000_00000100, - Bit01 = 0b_00000000_00000000_00000000_00000010, - Bit00 = 0b_00000000_00000000_00000000_00000001, - All_0 = 0, - All_1 = ~All_0, - Alternating_01 = 0b_01010101_01010101_01010101_01010101, - Alternating_10 = ~Alternating_01, - EvenBytesHigh = 0b_00000000_11111111_00000000_11111111, - OddBytesHigh = ~EvenBytesHigh, -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitUInt.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitUInt.cs deleted file mode 100644 index ca24165ef..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitUInt.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; - -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions; - -/// -/// Same as , but with applied. -/// -[GenerateEnumExtensionMethods] -[SuppressMessage ("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification = "Naming is intentional.")] -[SuppressMessage ("Roslynator", "RCS1154:Sort enum members", Justification = "Order is intentional.")] -public enum BetterEnum_ExplicitUInt : uint -{ - Bit31 = 0b_10000000_00000000_00000000_00000000u, - Bit30 = 0b_01000000_00000000_00000000_00000000u, - Bit29 = 0b_00100000_00000000_00000000_00000000u, - Bit28 = 0b_00010000_00000000_00000000_00000000u, - Bit27 = 0b_00001000_00000000_00000000_00000000u, - Bit26 = 0b_00000100_00000000_00000000_00000000u, - Bit25 = 0b_00000010_00000000_00000000_00000000u, - Bit24 = 0b_00000001_00000000_00000000_00000000u, - Bit23 = 0b_00000000_10000000_00000000_00000000u, - Bit22 = 0b_00000000_01000000_00000000_00000000u, - Bit21 = 0b_00000000_00100000_00000000_00000000u, - Bit20 = 0b_00000000_00010000_00000000_00000000u, - Bit19 = 0b_00000000_00001000_00000000_00000000u, - Bit18 = 0b_00000000_00000100_00000000_00000000u, - Bit17 = 0b_00000000_00000010_00000000_00000000u, - Bit16 = 0b_00000000_00000001_00000000_00000000u, - Bit15 = 0b_00000000_00000000_10000000_00000000u, - Bit14 = 0b_00000000_00000000_01000000_00000000u, - Bit13 = 0b_00000000_00000000_00100000_00000000u, - Bit12 = 0b_00000000_00000000_00010000_00000000u, - Bit11 = 0b_00000000_00000000_00001000_00000000u, - Bit10 = 0b_00000000_00000000_00000100_00000000u, - Bit09 = 0b_00000000_00000000_00000010_00000000u, - Bit08 = 0b_00000000_00000000_00000001_00000000u, - Bit07 = 0b_00000000_00000000_00000000_10000000u, - Bit06 = 0b_00000000_00000000_00000000_01000000u, - Bit05 = 0b_00000000_00000000_00000000_00100000u, - Bit04 = 0b_00000000_00000000_00000000_00010000u, - Bit03 = 0b_00000000_00000000_00000000_00001000u, - Bit02 = 0b_00000000_00000000_00000000_00000100u, - Bit01 = 0b_00000000_00000000_00000000_00000010u, - Bit00 = 0b_00000000_00000000_00000000_00000001u, - All_0 = 0, - All_1 = ~All_0, - Alternating_01 = 0b_01010101_01010101_01010101_01010101, - Alternating_10 = ~Alternating_01, - EvenBytesHigh = 0b_00000000_11111111_00000000_11111111, - OddBytesHigh = ~EvenBytesHigh, -} \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitUInt_NoFastIsDefined.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitUInt_NoFastIsDefined.cs deleted file mode 100644 index 01edea7a5..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_ExplicitUInt_NoFastIsDefined.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; - -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions; - -/// -/// Same as , but with = . -/// -[GenerateEnumExtensionMethods (FastIsDefined = false)] -[SuppressMessage ("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification = "Naming is intentional.")] -[SuppressMessage ("Roslynator", "RCS1154:Sort enum members", Justification = "Order is intentional.")] -public enum BetterEnum_ExplicitUInt_NoFastIsDefined : uint -{ - Bit31 = 0b_10000000_00000000_00000000_00000000u, - Bit30 = 0b_01000000_00000000_00000000_00000000u, - Bit29 = 0b_00100000_00000000_00000000_00000000u, - Bit28 = 0b_00010000_00000000_00000000_00000000u, - Bit27 = 0b_00001000_00000000_00000000_00000000u, - Bit26 = 0b_00000100_00000000_00000000_00000000u, - Bit25 = 0b_00000010_00000000_00000000_00000000u, - Bit24 = 0b_00000001_00000000_00000000_00000000u, - Bit23 = 0b_00000000_10000000_00000000_00000000u, - Bit22 = 0b_00000000_01000000_00000000_00000000u, - Bit21 = 0b_00000000_00100000_00000000_00000000u, - Bit20 = 0b_00000000_00010000_00000000_00000000u, - Bit19 = 0b_00000000_00001000_00000000_00000000u, - Bit18 = 0b_00000000_00000100_00000000_00000000u, - Bit17 = 0b_00000000_00000010_00000000_00000000u, - Bit16 = 0b_00000000_00000001_00000000_00000000u, - Bit15 = 0b_00000000_00000000_10000000_00000000u, - Bit14 = 0b_00000000_00000000_01000000_00000000u, - Bit13 = 0b_00000000_00000000_00100000_00000000u, - Bit12 = 0b_00000000_00000000_00010000_00000000u, - Bit11 = 0b_00000000_00000000_00001000_00000000u, - Bit10 = 0b_00000000_00000000_00000100_00000000u, - Bit09 = 0b_00000000_00000000_00000010_00000000u, - Bit08 = 0b_00000000_00000000_00000001_00000000u, - Bit07 = 0b_00000000_00000000_00000000_10000000u, - Bit06 = 0b_00000000_00000000_00000000_01000000u, - Bit05 = 0b_00000000_00000000_00000000_00100000u, - Bit04 = 0b_00000000_00000000_00000000_00010000u, - Bit03 = 0b_00000000_00000000_00000000_00001000u, - Bit02 = 0b_00000000_00000000_00000000_00000100u, - Bit01 = 0b_00000000_00000000_00000000_00000010u, - Bit00 = 0b_00000000_00000000_00000000_00000001u, - All_0 = 0, - All_1 = ~All_0, - Alternating_01 = 0b_01010101_01010101_01010101_01010101, - Alternating_10 = ~Alternating_01, - EvenBytesHigh = 0b_00000000_11111111_00000000_11111111, - OddBytesHigh = ~EvenBytesHigh, -} \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_NoFastIsDefined.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_NoFastIsDefined.cs deleted file mode 100644 index 04f6580ad..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterEnum_NoFastIsDefined.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; - -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions; - -/// -/// Same as , but with = . -/// -[GenerateEnumExtensionMethods (FastIsDefined = false)] -[SuppressMessage ("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification = "Naming is intentional.")] -[SuppressMessage ("Roslynator", "RCS1154:Sort enum members", Justification = "Order is intentional.")] -public enum BetterEnum_NoFastIsDefined -{ - Bit31 = -0b_10000000_00000000_00000000_00000000, - Bit30 = 0b_01000000_00000000_00000000_00000000, - Bit29 = 0b_00100000_00000000_00000000_00000000, - Bit28 = 0b_00010000_00000000_00000000_00000000, - Bit27 = 0b_00001000_00000000_00000000_00000000, - Bit26 = 0b_00000100_00000000_00000000_00000000, - Bit25 = 0b_00000010_00000000_00000000_00000000, - Bit24 = 0b_00000001_00000000_00000000_00000000, - Bit23 = 0b_00000000_10000000_00000000_00000000, - Bit22 = 0b_00000000_01000000_00000000_00000000, - Bit21 = 0b_00000000_00100000_00000000_00000000, - Bit20 = 0b_00000000_00010000_00000000_00000000, - Bit19 = 0b_00000000_00001000_00000000_00000000, - Bit18 = 0b_00000000_00000100_00000000_00000000, - Bit17 = 0b_00000000_00000010_00000000_00000000, - Bit16 = 0b_00000000_00000001_00000000_00000000, - Bit15 = 0b_00000000_00000000_10000000_00000000, - Bit14 = 0b_00000000_00000000_01000000_00000000, - Bit13 = 0b_00000000_00000000_00100000_00000000, - Bit12 = 0b_00000000_00000000_00010000_00000000, - Bit11 = 0b_00000000_00000000_00001000_00000000, - Bit10 = 0b_00000000_00000000_00000100_00000000, - Bit09 = 0b_00000000_00000000_00000010_00000000, - Bit08 = 0b_00000000_00000000_00000001_00000000, - Bit07 = 0b_00000000_00000000_00000000_10000000, - Bit06 = 0b_00000000_00000000_00000000_01000000, - Bit05 = 0b_00000000_00000000_00000000_00100000, - Bit04 = 0b_00000000_00000000_00000000_00010000, - Bit03 = 0b_00000000_00000000_00000000_00001000, - Bit02 = 0b_00000000_00000000_00000000_00000100, - Bit01 = 0b_00000000_00000000_00000000_00000010, - Bit00 = 0b_00000000_00000000_00000000_00000001, - All_0 = 0, - All_1 = ~All_0, - Alternating_01 = 0b_01010101_01010101_01010101_01010101, - Alternating_10 = ~Alternating_01, - EvenBytesHigh = 0b_00000000_11111111_00000000_11111111, - OddBytesHigh = ~EvenBytesHigh, -} \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum.cs deleted file mode 100644 index 2e468941c..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; - -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions; - -/// -/// Same as , but with applied. -/// -[Flags] -[GenerateEnumExtensionMethods] -[SuppressMessage ("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification = "Naming is intentional.")] -[SuppressMessage ("Roslynator", "RCS1154:Sort enum members", Justification = "Order is intentional.")] -public enum BetterFlagsEnum -{ - Bit31 = -0b_10000000_00000000_00000000_00000000, - Bit30 = 0b_01000000_00000000_00000000_00000000, - Bit29 = 0b_00100000_00000000_00000000_00000000, - Bit28 = 0b_00010000_00000000_00000000_00000000, - Bit27 = 0b_00001000_00000000_00000000_00000000, - Bit26 = 0b_00000100_00000000_00000000_00000000, - Bit25 = 0b_00000010_00000000_00000000_00000000, - Bit24 = 0b_00000001_00000000_00000000_00000000, - Bit23 = -0b_00000000_10000000_00000000_00000000, - Bit22 = 0b_00000000_01000000_00000000_00000000, - Bit21 = 0b_00000000_00100000_00000000_00000000, - Bit20 = 0b_00000000_00010000_00000000_00000000, - Bit19 = 0b_00000000_00001000_00000000_00000000, - Bit18 = 0b_00000000_00000100_00000000_00000000, - Bit17 = 0b_00000000_00000010_00000000_00000000, - Bit16 = 0b_00000000_00000001_00000000_00000000, - Bit15 = -0b_00000000_00000000_10000000_00000000, - Bit14 = 0b_00000000_00000000_01000000_00000000, - Bit13 = 0b_00000000_00000000_00100000_00000000, - Bit12 = 0b_00000000_00000000_00010000_00000000, - Bit11 = 0b_00000000_00000000_00001000_00000000, - Bit10 = 0b_00000000_00000000_00000100_00000000, - Bit09 = 0b_00000000_00000000_00000010_00000000, - Bit08 = 0b_00000000_00000000_00000001_00000000, - Bit07 = -0b_00000000_00000000_00000000_10000000, - Bit06 = 0b_00000000_00000000_00000000_01000000, - Bit05 = 0b_00000000_00000000_00000000_00100000, - Bit04 = 0b_00000000_00000000_00000000_00010000, - Bit03 = 0b_00000000_00000000_00000000_00001000, - Bit02 = 0b_00000000_00000000_00000000_00000100, - Bit01 = 0b_00000000_00000000_00000000_00000010, - Bit00 = 0b_00000000_00000000_00000000_00000001, - All_0 = 0, - All_1 = ~All_0, - Alternating_01 = 0b_01010101_01010101_01010101_01010101, - Alternating_10 = ~Alternating_01, - EvenBytesHigh = 0b_00000000_11111111_00000000_11111111, - OddBytesHigh = ~EvenBytesHigh, -} \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum_ExplicitInt.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum_ExplicitInt.cs deleted file mode 100644 index 00b1b9487..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum_ExplicitInt.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; - -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions; - -/// -/// -/// Same as , but with applied. -/// -[Flags] -[GenerateEnumExtensionMethods] -[SuppressMessage ("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification = "Naming is intentional.")] -[SuppressMessage ("Roslynator", "RCS1154:Sort enum members", Justification = "Order is intentional.")] -public enum BetterFlagsEnum_ExplicitInt : int -{ - Bit31 = -0b_10000000_00000000_00000000_00000000, - Bit30 = 0b_01000000_00000000_00000000_00000000, - Bit29 = 0b_00100000_00000000_00000000_00000000, - Bit28 = 0b_00010000_00000000_00000000_00000000, - Bit27 = 0b_00001000_00000000_00000000_00000000, - Bit26 = 0b_00000100_00000000_00000000_00000000, - Bit25 = 0b_00000010_00000000_00000000_00000000, - Bit24 = 0b_00000001_00000000_00000000_00000000, - Bit23 = -0b_00000000_10000000_00000000_00000000, - Bit22 = 0b_00000000_01000000_00000000_00000000, - Bit21 = 0b_00000000_00100000_00000000_00000000, - Bit20 = 0b_00000000_00010000_00000000_00000000, - Bit19 = 0b_00000000_00001000_00000000_00000000, - Bit18 = 0b_00000000_00000100_00000000_00000000, - Bit17 = 0b_00000000_00000010_00000000_00000000, - Bit16 = 0b_00000000_00000001_00000000_00000000, - Bit15 = -0b_00000000_00000000_10000000_00000000, - Bit14 = 0b_00000000_00000000_01000000_00000000, - Bit13 = 0b_00000000_00000000_00100000_00000000, - Bit12 = 0b_00000000_00000000_00010000_00000000, - Bit11 = 0b_00000000_00000000_00001000_00000000, - Bit10 = 0b_00000000_00000000_00000100_00000000, - Bit09 = 0b_00000000_00000000_00000010_00000000, - Bit08 = 0b_00000000_00000000_00000001_00000000, - Bit07 = -0b_00000000_00000000_00000000_10000000, - Bit06 = 0b_00000000_00000000_00000000_01000000, - Bit05 = 0b_00000000_00000000_00000000_00100000, - Bit04 = 0b_00000000_00000000_00000000_00010000, - Bit03 = 0b_00000000_00000000_00000000_00001000, - Bit02 = 0b_00000000_00000000_00000000_00000100, - Bit01 = 0b_00000000_00000000_00000000_00000010, - Bit00 = 0b_00000000_00000000_00000000_00000001, - All_0 = 0, - All_1 = ~All_0, - Alternating_01 = 0b_01010101_01010101_01010101_01010101, - Alternating_10 = ~Alternating_01, - EvenBytesHigh = 0b_00000000_11111111_00000000_11111111, - OddBytesHigh = ~EvenBytesHigh, -} \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum_ExplicitUInt.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum_ExplicitUInt.cs deleted file mode 100644 index 9edb067d8..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithGenerator/BetterFlagsEnum_ExplicitUInt.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; - -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions; - -/// -/// Same as , but with applied. -/// -[Flags] -[GenerateEnumExtensionMethods] -[SuppressMessage ("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification = "Naming is intentional.")] -[SuppressMessage ("Roslynator", "RCS1154:Sort enum members", Justification = "Order is intentional.")] -public enum BetterFlagsEnum_ExplicitUInt : uint -{ - Bit31 = 0b_10000000_00000000_00000000_00000000u, - Bit30 = 0b_01000000_00000000_00000000_00000000u, - Bit29 = 0b_00100000_00000000_00000000_00000000u, - Bit28 = 0b_00010000_00000000_00000000_00000000u, - Bit27 = 0b_00001000_00000000_00000000_00000000u, - Bit26 = 0b_00000100_00000000_00000000_00000000u, - Bit25 = 0b_00000010_00000000_00000000_00000000u, - Bit24 = 0b_00000001_00000000_00000000_00000000u, - Bit23 = 0b_00000000_10000000_00000000_00000000u, - Bit22 = 0b_00000000_01000000_00000000_00000000u, - Bit21 = 0b_00000000_00100000_00000000_00000000u, - Bit20 = 0b_00000000_00010000_00000000_00000000u, - Bit19 = 0b_00000000_00001000_00000000_00000000u, - Bit18 = 0b_00000000_00000100_00000000_00000000u, - Bit17 = 0b_00000000_00000010_00000000_00000000u, - Bit16 = 0b_00000000_00000001_00000000_00000000u, - Bit15 = 0b_00000000_00000000_10000000_00000000u, - Bit14 = 0b_00000000_00000000_01000000_00000000u, - Bit13 = 0b_00000000_00000000_00100000_00000000u, - Bit12 = 0b_00000000_00000000_00010000_00000000u, - Bit11 = 0b_00000000_00000000_00001000_00000000u, - Bit10 = 0b_00000000_00000000_00000100_00000000u, - Bit09 = 0b_00000000_00000000_00000010_00000000u, - Bit08 = 0b_00000000_00000000_00000001_00000000u, - Bit07 = 0b_00000000_00000000_00000000_10000000u, - Bit06 = 0b_00000000_00000000_00000000_01000000u, - Bit05 = 0b_00000000_00000000_00000000_00100000u, - Bit04 = 0b_00000000_00000000_00000000_00010000u, - Bit03 = 0b_00000000_00000000_00000000_00001000u, - Bit02 = 0b_00000000_00000000_00000000_00000100u, - Bit01 = 0b_00000000_00000000_00000000_00000010u, - Bit00 = 0b_00000000_00000000_00000000_00000001u, - All_0 = 0, - All_1 = ~All_0, - Alternating_01 = 0b_01010101_01010101_01010101_01010101, - Alternating_10 = ~Alternating_01, - EvenBytesHigh = 0b_00000000_11111111_00000000_11111111, - OddBytesHigh = ~EvenBytesHigh, -} \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum.cs deleted file mode 100644 index bfb743df6..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions; - -/// -/// Basic enum without explicitly-defined backing type and no attributes on the enum or any of its members. -/// -[SuppressMessage ("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Naming is intentional.")] -[SuppressMessage ("Roslynator", "RCS1154:Sort enum members", Justification = "Order is intentional.")] -public enum BasicEnum -{ - Bit31 = -0b_10000000_00000000_00000000_00000000, - Bit30 = 0b_01000000_00000000_00000000_00000000, - Bit29 = 0b_00100000_00000000_00000000_00000000, - Bit28 = 0b_00010000_00000000_00000000_00000000, - Bit27 = 0b_00001000_00000000_00000000_00000000, - Bit26 = 0b_00000100_00000000_00000000_00000000, - Bit25 = 0b_00000010_00000000_00000000_00000000, - Bit24 = 0b_00000001_00000000_00000000_00000000, - Bit23 = 0b_00000000_10000000_00000000_00000000, - Bit22 = 0b_00000000_01000000_00000000_00000000, - Bit21 = 0b_00000000_00100000_00000000_00000000, - Bit20 = 0b_00000000_00010000_00000000_00000000, - Bit19 = 0b_00000000_00001000_00000000_00000000, - Bit18 = 0b_00000000_00000100_00000000_00000000, - Bit17 = 0b_00000000_00000010_00000000_00000000, - Bit16 = 0b_00000000_00000001_00000000_00000000, - Bit15 = 0b_00000000_00000000_10000000_00000000, - Bit14 = 0b_00000000_00000000_01000000_00000000, - Bit13 = 0b_00000000_00000000_00100000_00000000, - Bit12 = 0b_00000000_00000000_00010000_00000000, - Bit11 = 0b_00000000_00000000_00001000_00000000, - Bit10 = 0b_00000000_00000000_00000100_00000000, - Bit09 = 0b_00000000_00000000_00000010_00000000, - Bit08 = 0b_00000000_00000000_00000001_00000000, - Bit07 = 0b_00000000_00000000_00000000_10000000, - Bit06 = 0b_00000000_00000000_00000000_01000000, - Bit05 = 0b_00000000_00000000_00000000_00100000, - Bit04 = 0b_00000000_00000000_00000000_00010000, - Bit03 = 0b_00000000_00000000_00000000_00001000, - Bit02 = 0b_00000000_00000000_00000000_00000100, - Bit01 = 0b_00000000_00000000_00000000_00000010, - Bit00 = 0b_00000000_00000000_00000000_00000001, - All_0 = 0, - All_1 = -1, - Alternating_01 = 0b_01010101_01010101_01010101_01010101, - Alternating_10 = unchecked((int)0b_10101010_10101010_10101010_10101010), - OddBytesHigh = unchecked((int)0b_11111111_00000000_11111111_00000000), - EvenBytesHigh = 0b_00000000_11111111_00000000_11111111, -} \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum_ExplicitInt.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum_ExplicitInt.cs deleted file mode 100644 index 8a400ab14..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum_ExplicitInt.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Terminal.Gui.Analyzers.Internal.Attributes; - -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions; - -/// -/// Basic enum with explicitly-defined backing type of int and no attributes on the enum or any of its members. -/// -[SuppressMessage ("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Naming is intentional.")] -[SuppressMessage ("Roslynator", "RCS1154:Sort enum members", Justification = "Order is intentional.")] -public enum BasicEnum_ExplicitInt : int -{ - Bit31 = -0b_10000000_00000000_00000000_00000000, - Bit30 = 0b_01000000_00000000_00000000_00000000, - Bit29 = 0b_00100000_00000000_00000000_00000000, - Bit28 = 0b_00010000_00000000_00000000_00000000, - Bit27 = 0b_00001000_00000000_00000000_00000000, - Bit26 = 0b_00000100_00000000_00000000_00000000, - Bit25 = 0b_00000010_00000000_00000000_00000000, - Bit24 = 0b_00000001_00000000_00000000_00000000, - Bit23 = 0b_00000000_10000000_00000000_00000000, - Bit22 = 0b_00000000_01000000_00000000_00000000, - Bit21 = 0b_00000000_00100000_00000000_00000000, - Bit20 = 0b_00000000_00010000_00000000_00000000, - Bit19 = 0b_00000000_00001000_00000000_00000000, - Bit18 = 0b_00000000_00000100_00000000_00000000, - Bit17 = 0b_00000000_00000010_00000000_00000000, - Bit16 = 0b_00000000_00000001_00000000_00000000, - Bit15 = 0b_00000000_00000000_10000000_00000000, - Bit14 = 0b_00000000_00000000_01000000_00000000, - Bit13 = 0b_00000000_00000000_00100000_00000000, - Bit12 = 0b_00000000_00000000_00010000_00000000, - Bit11 = 0b_00000000_00000000_00001000_00000000, - Bit10 = 0b_00000000_00000000_00000100_00000000, - Bit09 = 0b_00000000_00000000_00000010_00000000, - Bit08 = 0b_00000000_00000000_00000001_00000000, - Bit07 = 0b_00000000_00000000_00000000_10000000, - Bit06 = 0b_00000000_00000000_00000000_01000000, - Bit05 = 0b_00000000_00000000_00000000_00100000, - Bit04 = 0b_00000000_00000000_00000000_00010000, - Bit03 = 0b_00000000_00000000_00000000_00001000, - Bit02 = 0b_00000000_00000000_00000000_00000100, - Bit01 = 0b_00000000_00000000_00000000_00000010, - Bit00 = 0b_00000000_00000000_00000000_00000001, - All_0 = 0, - All_1 = -1, - Alternating_01 = 0b_01010101_01010101_01010101_01010101, - Alternating_10 = unchecked((int)0b_10101010_10101010_10101010_10101010), - OddBytesHigh = unchecked((int)0b_11111111_00000000_11111111_00000000), - EvenBytesHigh = unchecked((int)0b_00000000_11111111_00000000_11111111), -} \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum_ExplicitUint.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum_ExplicitUint.cs deleted file mode 100644 index 911e64c9c..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/BasicEnum_ExplicitUint.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions; - -/// -/// Basic enum with explicitly-defined backing type of uint and no attributes on the enum or any of its members. -/// -[SuppressMessage ("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Naming is intentional.")] -[SuppressMessage ("Roslynator", "RCS1154:Sort enum members", Justification = "Order is intentional.")] -public enum BasicEnum_ExplicitUInt : uint -{ - Bit31 = 0b_10000000_00000000_00000000_00000000u, - Bit30 = 0b_01000000_00000000_00000000_00000000u, - Bit29 = 0b_00100000_00000000_00000000_00000000u, - Bit28 = 0b_00010000_00000000_00000000_00000000u, - Bit27 = 0b_00001000_00000000_00000000_00000000u, - Bit26 = 0b_00000100_00000000_00000000_00000000u, - Bit25 = 0b_00000010_00000000_00000000_00000000u, - Bit24 = 0b_00000001_00000000_00000000_00000000u, - Bit23 = 0b_00000000_10000000_00000000_00000000u, - Bit22 = 0b_00000000_01000000_00000000_00000000u, - Bit21 = 0b_00000000_00100000_00000000_00000000u, - Bit20 = 0b_00000000_00010000_00000000_00000000u, - Bit19 = 0b_00000000_00001000_00000000_00000000u, - Bit18 = 0b_00000000_00000100_00000000_00000000u, - Bit17 = 0b_00000000_00000010_00000000_00000000u, - Bit16 = 0b_00000000_00000001_00000000_00000000u, - Bit15 = 0b_00000000_00000000_10000000_00000000u, - Bit14 = 0b_00000000_00000000_01000000_00000000u, - Bit13 = 0b_00000000_00000000_00100000_00000000u, - Bit12 = 0b_00000000_00000000_00010000_00000000u, - Bit11 = 0b_00000000_00000000_00001000_00000000u, - Bit10 = 0b_00000000_00000000_00000100_00000000u, - Bit09 = 0b_00000000_00000000_00000010_00000000u, - Bit08 = 0b_00000000_00000000_00000001_00000000u, - Bit07 = 0b_00000000_00000000_00000000_10000000u, - Bit06 = 0b_00000000_00000000_00000000_01000000u, - Bit05 = 0b_00000000_00000000_00000000_00100000u, - Bit04 = 0b_00000000_00000000_00000000_00010000u, - Bit03 = 0b_00000000_00000000_00000000_00001000u, - Bit02 = 0b_00000000_00000000_00000000_00000100u, - Bit01 = 0b_00000000_00000000_00000000_00000010u, - Bit00 = 0b_00000000_00000000_00000000_00000001u, - All_0 = 0b_00000000_00000000_00000000_00000000u, - All_1 = 0b_11111111_11111111_11111111_11111111u, - Alternating_01 = 0b_01010101_01010101_01010101_01010101u, - Alternating_10 = 0b_10101010_10101010_10101010_10101010u, - OddBytesHigh = 0b_11111111_00000000_11111111_00000000u, - EvenBytesHigh = 0b_00000000_11111111_00000000_11111111u, -} \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum.cs deleted file mode 100644 index b69fcd057..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions; - -/// -/// Flags enum without explicitly-defined backing type and only a on the enum declaration No other attributes on the enum or its members.. -/// -[Flags] -[SuppressMessage ("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Naming is intentional.")] -[SuppressMessage ("Roslynator", "RCS1154:Sort enum members", Justification = "Order is intentional.")] -public enum FlagsEnum -{ - Bit31 = -0b_10000000_00000000_00000000_00000000, - Bit30 = 0b_01000000_00000000_00000000_00000000, - Bit29 = 0b_00100000_00000000_00000000_00000000, - Bit28 = 0b_00010000_00000000_00000000_00000000, - Bit27 = 0b_00001000_00000000_00000000_00000000, - Bit26 = 0b_00000100_00000000_00000000_00000000, - Bit25 = 0b_00000010_00000000_00000000_00000000, - Bit24 = 0b_00000001_00000000_00000000_00000000, - Bit23 = -0b_00000000_10000000_00000000_00000000, - Bit22 = 0b_00000000_01000000_00000000_00000000, - Bit21 = 0b_00000000_00100000_00000000_00000000, - Bit20 = 0b_00000000_00010000_00000000_00000000, - Bit19 = 0b_00000000_00001000_00000000_00000000, - Bit18 = 0b_00000000_00000100_00000000_00000000, - Bit17 = 0b_00000000_00000010_00000000_00000000, - Bit16 = 0b_00000000_00000001_00000000_00000000, - Bit15 = -0b_00000000_00000000_10000000_00000000, - Bit14 = 0b_00000000_00000000_01000000_00000000, - Bit13 = 0b_00000000_00000000_00100000_00000000, - Bit12 = 0b_00000000_00000000_00010000_00000000, - Bit11 = 0b_00000000_00000000_00001000_00000000, - Bit10 = 0b_00000000_00000000_00000100_00000000, - Bit09 = 0b_00000000_00000000_00000010_00000000, - Bit08 = 0b_00000000_00000000_00000001_00000000, - Bit07 = -0b_00000000_00000000_00000000_10000000, - Bit06 = 0b_00000000_00000000_00000000_01000000, - Bit05 = 0b_00000000_00000000_00000000_00100000, - Bit04 = 0b_00000000_00000000_00000000_00010000, - Bit03 = 0b_00000000_00000000_00000000_00001000, - Bit02 = 0b_00000000_00000000_00000000_00000100, - Bit01 = 0b_00000000_00000000_00000000_00000010, - Bit00 = 0b_00000000_00000000_00000000_00000001, - All_0 = 0, - All_1 = -1 -} \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum_ExplicitInt.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum_ExplicitInt.cs deleted file mode 100644 index a01174e71..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum_ExplicitInt.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions; - -/// -/// Flags enum with explicitly-defined backing type of int and only a on the enum declaration No other attributes on the enum or its members.. -/// -[Flags] -[SuppressMessage ("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Naming is intentional.")] -[SuppressMessage ("Roslynator", "RCS1154:Sort enum members", Justification = "Order is intentional.")] -public enum FlagsEnum_ExplicitInt : int -{ - Bit31 = -0b_10000000_00000000_00000000_00000000, - Bit30 = 0b_01000000_00000000_00000000_00000000, - Bit29 = 0b_00100000_00000000_00000000_00000000, - Bit28 = 0b_00010000_00000000_00000000_00000000, - Bit27 = 0b_00001000_00000000_00000000_00000000, - Bit26 = 0b_00000100_00000000_00000000_00000000, - Bit25 = 0b_00000010_00000000_00000000_00000000, - Bit24 = 0b_00000001_00000000_00000000_00000000, - Bit23 = -0b_00000000_10000000_00000000_00000000, - Bit22 = 0b_00000000_01000000_00000000_00000000, - Bit21 = 0b_00000000_00100000_00000000_00000000, - Bit20 = 0b_00000000_00010000_00000000_00000000, - Bit19 = 0b_00000000_00001000_00000000_00000000, - Bit18 = 0b_00000000_00000100_00000000_00000000, - Bit17 = 0b_00000000_00000010_00000000_00000000, - Bit16 = 0b_00000000_00000001_00000000_00000000, - Bit15 = -0b_00000000_00000000_10000000_00000000, - Bit14 = 0b_00000000_00000000_01000000_00000000, - Bit13 = 0b_00000000_00000000_00100000_00000000, - Bit12 = 0b_00000000_00000000_00010000_00000000, - Bit11 = 0b_00000000_00000000_00001000_00000000, - Bit10 = 0b_00000000_00000000_00000100_00000000, - Bit09 = 0b_00000000_00000000_00000010_00000000, - Bit08 = 0b_00000000_00000000_00000001_00000000, - Bit07 = -0b_00000000_00000000_00000000_10000000, - Bit06 = 0b_00000000_00000000_00000000_01000000, - Bit05 = 0b_00000000_00000000_00000000_00100000, - Bit04 = 0b_00000000_00000000_00000000_00010000, - Bit03 = 0b_00000000_00000000_00000000_00001000, - Bit02 = 0b_00000000_00000000_00000000_00000100, - Bit01 = 0b_00000000_00000000_00000000_00000010, - Bit00 = 0b_00000000_00000000_00000000_00000001, - All_0 = 0, - All_1 = -1 -} \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum_ExplicitUInt.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum_ExplicitUInt.cs deleted file mode 100644 index 39285e26d..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumDefinitions/WithoutGenerator/FlagsEnum_ExplicitUInt.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions.EnumDefinitions; - -/// -/// Flags enum with explicitly-defined backing type of uint and only a on the enum declaration No other attributes on the enum or its members.. -/// -[Flags] -[SuppressMessage ("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Naming is intentional.")] -[SuppressMessage ("Roslynator", "RCS1154:Sort enum members", Justification = "Order is intentional.")] -public enum FlagsEnum_ExplicitUInt : uint -{ - Bit31 = 0b_10000000_00000000_00000000_00000000u, - Bit30 = 0b_01000000_00000000_00000000_00000000u, - Bit29 = 0b_00100000_00000000_00000000_00000000u, - Bit28 = 0b_00010000_00000000_00000000_00000000u, - Bit27 = 0b_00001000_00000000_00000000_00000000u, - Bit26 = 0b_00000100_00000000_00000000_00000000u, - Bit25 = 0b_00000010_00000000_00000000_00000000u, - Bit24 = 0b_00000001_00000000_00000000_00000000u, - Bit23 = 0b_00000000_10000000_00000000_00000000u, - Bit22 = 0b_00000000_01000000_00000000_00000000u, - Bit21 = 0b_00000000_00100000_00000000_00000000u, - Bit20 = 0b_00000000_00010000_00000000_00000000u, - Bit19 = 0b_00000000_00001000_00000000_00000000u, - Bit18 = 0b_00000000_00000100_00000000_00000000u, - Bit17 = 0b_00000000_00000010_00000000_00000000u, - Bit16 = 0b_00000000_00000001_00000000_00000000u, - Bit15 = 0b_00000000_00000000_10000000_00000000u, - Bit14 = 0b_00000000_00000000_01000000_00000000u, - Bit13 = 0b_00000000_00000000_00100000_00000000u, - Bit12 = 0b_00000000_00000000_00010000_00000000u, - Bit11 = 0b_00000000_00000000_00001000_00000000u, - Bit10 = 0b_00000000_00000000_00000100_00000000u, - Bit09 = 0b_00000000_00000000_00000010_00000000u, - Bit08 = 0b_00000000_00000000_00000001_00000000u, - Bit07 = 0b_00000000_00000000_00000000_10000000u, - Bit06 = 0b_00000000_00000000_00000000_01000000u, - Bit05 = 0b_00000000_00000000_00000000_00100000u, - Bit04 = 0b_00000000_00000000_00000000_00010000u, - Bit03 = 0b_00000000_00000000_00000000_00001000u, - Bit02 = 0b_00000000_00000000_00000000_00000100u, - Bit01 = 0b_00000000_00000000_00000000_00000010u, - Bit00 = 0b_00000000_00000000_00000000_00000001u, - All_0 = 0b_00000000_00000000_00000000_00000000u, - All_1 = 0b_11111111_11111111_11111111_11111111u -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumExtensionMethodsIncrementalGeneratorTests.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumExtensionMethodsIncrementalGeneratorTests.cs deleted file mode 100644 index c134ab9fa..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Generators/EnumExtensions/EnumExtensionMethodsIncrementalGeneratorTests.cs +++ /dev/null @@ -1,306 +0,0 @@ -using System.Collections.Concurrent; -using System.Collections.ObjectModel; -using System.Reflection; -using NUnit.Framework.Interfaces; -using NUnit.Framework.Internal; -using Terminal.Gui.Analyzers.Internal.Attributes; -using Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions; -// ReSharper disable InconsistentNaming - -namespace Terminal.Gui.Analyzers.Internal.Tests.Generators.EnumExtensions; - -[TestFixture] -[Category ("Source Generators")] -[TestOf (typeof (EnumExtensionMethodsIncrementalGenerator))] -[Parallelizable (ParallelScope.Children)] -[SuppressMessage ("ReSharper", "ExceptionNotDocumented")] -public class EnumExtensionMethodsIncrementalGeneratorTests -{ - private static bool _isInitialized; - - /// All enum types declared in the test assembly. - private static readonly ObservableCollection _allEnumTypes = []; - - /// - /// All enum types without a , - /// - private static readonly HashSet _boringEnumTypes = []; - - /// All extension classes generated for enums with our attribute. - private static readonly ObservableCollection _enumExtensionClasses = []; - - private static readonly ConcurrentDictionary _extendedEnumTypeMappings = []; - private static IEnumerable ExtendedEnumTypes => _extendedEnumTypeMappings.Keys; - - private static readonly ReaderWriterLockSlim _initializationLock = new (); - - private static IEnumerable GetAssemblyExtendedEnumTypeAttributes () => - Assembly.GetExecutingAssembly () - .GetCustomAttributes (); - - private static IEnumerable Get_AssemblyExtendedEnumTypeAttribute_EnumHasGeneratorAttribute_Cases () - { - return GetAssemblyExtendedEnumTypeAttributes () - .Select ( - static attr => new TestCaseData (attr) - { - TestName = $"{nameof (AssemblyExtendedEnumTypeAttribute_EnumHasGeneratorAttribute)}({attr.EnumType.Name},{attr.ExtensionClass.Name})", - HasExpectedResult = true, - ExpectedResult = true - }); - } - - [Test] - [Category ("Attributes")] - [TestCaseSource (nameof (Get_AssemblyExtendedEnumTypeAttribute_EnumHasGeneratorAttribute_Cases))] - public bool AssemblyExtendedEnumTypeAttribute_EnumHasGeneratorAttribute (AssemblyExtendedEnumTypeAttribute attr) - { - Assume.That (attr, Is.Not.Null); - Assume.That (attr.EnumType, Is.Not.Null); - Assume.That (attr.EnumType.IsEnum); - - return attr.EnumType.IsDefined (typeof (GenerateEnumExtensionMethodsAttribute)); - } - - [Test] - [Category("Attributes")] - public void AssemblyExtendedEnumTypeAttribute_ExtensionClassHasExpectedReverseMappingAttribute ([ValueSource(nameof(GetAssemblyExtendedEnumTypeAttributes))]AssemblyExtendedEnumTypeAttribute attr) - { - Assume.That (attr, Is.Not.Null); - Assume.That (attr.ExtensionClass, Is.Not.Null); - Assume.That (attr.ExtensionClass.IsClass); - Assume.That (attr.ExtensionClass.IsSealed); - - Assert.That (attr.ExtensionClass.IsDefined (typeof (ExtensionsForEnumTypeAttribute<>))); - } - - [Test] - [Category("Attributes")] - public void ExtendedEnum_AssemblyHasMatchingAttribute ([ValueSource(nameof(GetExtendedEnum_EnumData))]EnumData enumData) - { - Assume.That (enumData, Is.Not.Null); - Assume.That (enumData.EnumType, Is.Not.Null); - Assume.That (enumData.EnumType.IsEnum); - - Assert.That (enumData.EnumType, Has.Attribute ()); - } - - [Test] - public void BoringEnum_DoesNotHaveExtensions ([ValueSource (nameof (_boringEnumTypes))] Type enumType) - { - Assume.That (enumType.IsEnum); - - Assert.That (enumType, Has.No.Attribute ()); - } - - [Test] - public void ExtendedEnum_FastIsDefinedFalse_DoesNotHaveFastIsDefined ([ValueSource (nameof (GetExtendedEnumTypes_FastIsDefinedFalse))] EnumData enumData) - { - Assume.That (enumData.EnumType.IsEnum); - Assume.That (enumData.EnumType, Has.Attribute ()); - Assume.That (enumData.GeneratorAttribute, Is.Not.Null); - Assume.That (enumData.GeneratorAttribute, Is.EqualTo (enumData.EnumType.GetCustomAttribute ())); - Assume.That (enumData.GeneratorAttribute, Has.Property ("FastIsDefined").False); - Assume.That (enumData.ExtensionClass, Is.Not.Null); - - Assert.That (enumData.ExtensionClass!.GetMethod ("FastIsDefined"), Is.Null); - } - - [Test] - public void ExtendedEnum_StaticExtensionClassExists ([ValueSource (nameof (ExtendedEnumTypes))] Type enumType) - { - Assume.That (enumType.IsEnum); - Assume.That (enumType, Has.Attribute ()); - Assume.That (enumType, Has.Attribute ()); - } - - [Test] - public void ExtendedEnum_FastIsDefinedTrue_HasFastIsDefined ([ValueSource (nameof (GetExtendedEnumTypes_FastIsDefinedTrue))] EnumData enumData) - { - Assume.That (enumData.EnumType, Is.Not.Null); - Assume.That (enumData.EnumType.IsEnum); - Assume.That (enumData.EnumType, Has.Attribute ()); - Assume.That (enumData.ExtensionClass, Is.Not.Null); - TypeWrapper extensionClassTypeInfo = new(enumData.ExtensionClass!); - Assume.That (extensionClassTypeInfo.IsStaticClass); - Assume.That (enumData.GeneratorAttribute, Is.Not.Null); - Assume.That (enumData.GeneratorAttribute, Is.EqualTo (enumData.EnumType.GetCustomAttribute ())); - Assume.That (enumData.GeneratorAttribute, Has.Property ("FastIsDefined").True); - - MethodInfo? fastIsDefinedMethod = enumData.ExtensionClass!.GetMethod ("FastIsDefined"); - - Assert.That (fastIsDefinedMethod, Is.Not.Null); - Assert.That (fastIsDefinedMethod, Has.Attribute ()); - extensionClassTypeInfo.GetMethodsWithAttribute (false); - - - } - - private static IEnumerable GetExtendedEnum_EnumData () - { - _initializationLock.EnterUpgradeableReadLock (); - - try - { - if (!_isInitialized) - { - Initialize (); - } - - return _extendedEnumTypeMappings.Values; - } - finally - { - _initializationLock.ExitUpgradeableReadLock (); - } - } - - private static IEnumerable GetExtendedEnumTypes_FastIsDefinedFalse () - { - _initializationLock.EnterUpgradeableReadLock (); - - try - { - if (!_isInitialized) - { - Initialize (); - } - - return _extendedEnumTypeMappings.Values.Where (static t => t.GeneratorAttribute?.FastIsDefined is false); - } - finally - { - _initializationLock.ExitUpgradeableReadLock (); - } - } - - private static IEnumerable GetExtendedEnumTypes_FastIsDefinedTrue () - { - _initializationLock.EnterUpgradeableReadLock (); - - try - { - if (!_isInitialized) - { - Initialize (); - } - - return _extendedEnumTypeMappings.Values.Where (static t => t.GeneratorAttribute?.FastIsDefined is true); - } - finally - { - _initializationLock.ExitUpgradeableReadLock (); - } - } - - private static void Initialize () - { - if (!_initializationLock.IsUpgradeableReadLockHeld || !_initializationLock.TryEnterWriteLock (5000)) - { - return; - } - - try - { - if (_isInitialized) - { - return; - } - - _allEnumTypes.CollectionChanged += AllEnumTypes_CollectionChanged; - _enumExtensionClasses.CollectionChanged += EnumExtensionClasses_OnCollectionChanged; - - Type [] allAssemblyTypes = Assembly - .GetExecutingAssembly () - .GetTypes (); - - foreach (Type type in allAssemblyTypes.Where (IsDefinedEnum)) - { - _allEnumTypes.Add (type); - } - - foreach (Type type in allAssemblyTypes.Where (HasExtensionForEnumTypeAttribute)) - { - _enumExtensionClasses.Add (type); - } - - _isInitialized = true; - } - finally - { - _initializationLock.ExitWriteLock (); - } - - return; - - static bool IsDefinedEnum (Type t) { return t is { IsEnum: true, IsGenericType: false, IsConstructedGenericType: false, IsTypeDefinition: true }; } - - static void AllEnumTypes_CollectionChanged (object? sender, NotifyCollectionChangedEventArgs e) - { - if (e.Action is not NotifyCollectionChangedAction.Add and not NotifyCollectionChangedAction.Replace || e.NewItems is null) - { - return; - } - - foreach (Type enumType in e.NewItems.OfType ()) - { - if (enumType.GetCustomAttribute () is not { } generatorAttribute) - { - _boringEnumTypes.Add (enumType); - - continue; - } - - _extendedEnumTypeMappings.AddOrUpdate ( - enumType, - CreateNewEnumData, - UpdateGeneratorAttributeProperty, - generatorAttribute); - } - } - - static EnumData CreateNewEnumData (Type tEnum, GenerateEnumExtensionMethodsAttribute attr) { return new (tEnum, attr); } - - static EnumData UpdateGeneratorAttributeProperty (Type tEnum, EnumData data, GenerateEnumExtensionMethodsAttribute attr) - { - data.GeneratorAttribute ??= attr; - - return data; - } - - static void EnumExtensionClasses_OnCollectionChanged (object? sender, NotifyCollectionChangedEventArgs e) - { - if (e.Action != NotifyCollectionChangedAction.Add) - { - return; - } - - foreach (Type extensionClassType in e.NewItems!.OfType ()) - { - if (extensionClassType.GetCustomAttribute (typeof (ExtensionsForEnumTypeAttribute<>), false) is not IExtensionsForEnumTypeAttributes - { - EnumType.IsEnum: true - } extensionForAttribute) - { - continue; - } - - _extendedEnumTypeMappings [extensionForAttribute.EnumType].ExtensionClass ??= extensionClassType; - } - } - } - - private static bool HasExtensionForEnumTypeAttribute (Type t) => t.IsClass && t.IsDefined (typeof (ExtensionsForEnumTypeAttribute<>)); - - public sealed record EnumData ( - Type EnumType, - GenerateEnumExtensionMethodsAttribute? GeneratorAttribute = null, - Type? ExtensionClass = null, - IExtensionsForEnumTypeAttributes? ExtensionForEnumTypeAttribute = null) - { - public Type? ExtensionClass { get; set; } = ExtensionClass; - - public IExtensionsForEnumTypeAttributes? ExtensionForEnumTypeAttribute { get; set; } = ExtensionForEnumTypeAttribute; - public GenerateEnumExtensionMethodsAttribute? GeneratorAttribute { get; set; } = GeneratorAttribute; - } -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/GlobalSuppressions.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/GlobalSuppressions.cs deleted file mode 100644 index aba37def0..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/GlobalSuppressions.cs +++ /dev/null @@ -1,3 +0,0 @@ -[assembly: SuppressMessage ("Naming", "CA1707:Identifiers should not contain underscores", Scope = "module", Justification = "Naming is intentional.")] -[assembly: SuppressMessage ("Roslynator", "RCS1154:Sort enum members", Scope = "module", Justification = "Order is intentional.")] -[assembly: SuppressMessage ("Naming", "CA1711:Identifiers should not have incorrect suffix", Scope = "module", Justification = "Naming is intentional.")] diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/IndentedTextWriterExtensionsTests.cs b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/IndentedTextWriterExtensionsTests.cs deleted file mode 100644 index 250971d58..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/IndentedTextWriterExtensionsTests.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System.CodeDom.Compiler; -using System.Text; - -namespace Terminal.Gui.Analyzers.Internal.Tests; - -[TestFixture] -[Category ("Extension Methods")] -[TestOf (typeof (IndentedTextWriterExtensions))] -[Parallelizable (ParallelScope.Children)] -public class IndentedTextWriterExtensionsTests -{ - [Test] - public void Pop_Decrements () - { - StringBuilder sb = new (0); - using var sw = new StringWriter (sb); - using var writer = new IndentedTextWriter (sw); - writer.Indent = 5; - - Assume.That (writer.Indent, Is.EqualTo (5)); - - writer.Pop (); - Assert.That (writer.Indent, Is.EqualTo (4)); - } - - [Test] - public void Pop_WithClosing_WritesAndPops ([Values ("}", ")", "]")] string scopeClosing) - { - StringBuilder sb = new (256); - using var sw = new StringWriter (sb); - using var writer = new IndentedTextWriter (sw, " "); - writer.Indent = 5; - writer.Flush (); - Assume.That (writer.Indent, Is.EqualTo (5)); - Assume.That (sb.Length, Is.Zero); - - // Need to write something first, or IndentedTextWriter won't emit the indentation for the first call. - // So we'll write an empty line. - writer.WriteLine (); - - for (ushort indentCount = 5; indentCount > 0;) - { - writer.Pop (scopeClosing); - Assert.That (writer.Indent, Is.EqualTo (--indentCount)); - } - - writer.Flush (); - var result = sb.ToString (); - - Assert.That ( - result, - Is.EqualTo ( - $""" - - {scopeClosing} - {scopeClosing} - {scopeClosing} - {scopeClosing} - {scopeClosing} - - """)); - } - - [Test] - public void Push_Increments () - { - StringBuilder sb = new (32); - using var sw = new StringWriter (sb); - using var writer = new IndentedTextWriter (sw, " "); - - for (int indentCount = 0; indentCount < 5; indentCount++) - { - writer.Push (); - Assert.That (writer.Indent, Is.EqualTo (indentCount + 1)); - } - } - - [Test] - public void Push_WithOpening_WritesAndPushes ([Values ('{', '(', '[')] char scopeOpening) - { - StringBuilder sb = new (256); - using var sw = new StringWriter (sb); - using var writer = new IndentedTextWriter (sw, " "); - - for (ushort indentCount = 0; indentCount < 5;) - { - writer.Push ("Opening UninterestingEnum", scopeOpening); - Assert.That (writer.Indent, Is.EqualTo (++indentCount)); - } - - writer.Flush (); - var result = sb.ToString (); - - Assert.That ( - result, - Is.EqualTo ( - $""" - Opening UninterestingEnum - {scopeOpening} - Opening UninterestingEnum - {scopeOpening} - Opening UninterestingEnum - {scopeOpening} - Opening UninterestingEnum - {scopeOpening} - Opening UninterestingEnum - {scopeOpening} - - """)); - } -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Terminal.Gui.Analyzers.Internal.Tests.csproj b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Terminal.Gui.Analyzers.Internal.Tests.csproj deleted file mode 100644 index 42445111d..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Terminal.Gui.Analyzers.Internal.Tests.csproj +++ /dev/null @@ -1,52 +0,0 @@ - - - - net8.0 - enable - 12 - false - true - true - portable - $(DefineConstants);JETBRAINS_ANNOTATIONS;CONTRACTS_FULL;CODE_ANALYSIS - enable - true - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - all - Analyzer - true - - - - - - - - - - - - - - - - diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Terminal.Gui.Analyzers.Internal.Tests.csproj.DotSettings b/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Terminal.Gui.Analyzers.Internal.Tests.csproj.DotSettings deleted file mode 100644 index cd5ef68b8..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal.Tests/Terminal.Gui.Analyzers.Internal.Tests.csproj.DotSettings +++ /dev/null @@ -1,3 +0,0 @@ - - True - True \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/AccessibilityExtensions.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/AccessibilityExtensions.cs deleted file mode 100644 index fb80ebe87..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/AccessibilityExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Microsoft.CodeAnalysis; - -namespace Terminal.Gui.Analyzers.Internal; - -internal static class AccessibilityExtensions -{ - internal static string ToCSharpString (this Accessibility value) - { - return value switch - { - Accessibility.Public => "public", - Accessibility.Internal => "internal", - Accessibility.Private => "private", - Accessibility.Protected => "protected", - Accessibility.ProtectedAndInternal => "private protected", - Accessibility.ProtectedOrInternal => "protected internal", - _ => string.Empty - }; - } -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/AnalyzerReleases.Shipped.md b/Analyzers/Terminal.Gui.Analyzers.Internal/AnalyzerReleases.Shipped.md deleted file mode 100644 index 9316c42e0..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/AnalyzerReleases.Shipped.md +++ /dev/null @@ -1,8 +0,0 @@ -## Release 1.0 - -### New Rules - -Rule ID | Category | Severity | Notes ---------|----------|----------|-------------------- -TG0001 | Usage | Error | TG0001_GlobalNamespaceNotSupported -TG0002 | Usage | Error | TG0002_UnderlyingTypeNotSupported \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/AnalyzerReleases.Unshipped.md b/Analyzers/Terminal.Gui.Analyzers.Internal/AnalyzerReleases.Unshipped.md deleted file mode 100644 index cb4c8a8b9..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/AnalyzerReleases.Unshipped.md +++ /dev/null @@ -1,4 +0,0 @@ -### New Rules - -Rule ID | Category | Severity | Notes ---------|----------|----------|-------------------- diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/Analyzers/GenerateEnumExtensionMethodsAttributeAnalyzer.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/Analyzers/GenerateEnumExtensionMethodsAttributeAnalyzer.cs deleted file mode 100644 index d49fd37d1..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/Analyzers/GenerateEnumExtensionMethodsAttributeAnalyzer.cs +++ /dev/null @@ -1,117 +0,0 @@ -#define JETBRAINS_ANNOTATIONS -using System.Collections.Immutable; -using System.Linq; -using JetBrains.Annotations; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Diagnostics; -using Terminal.Gui.Analyzers.Internal.Attributes; -using Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions; - -namespace Terminal.Gui.Analyzers.Internal.Analyzers; - -/// -/// Design-time analyzer that checks for proper use of . -/// -[DiagnosticAnalyzer (LanguageNames.CSharp)] -[UsedImplicitly] -internal sealed class GenerateEnumExtensionMethodsAttributeAnalyzer : DiagnosticAnalyzer -{ - // ReSharper disable once InconsistentNaming - private static readonly DiagnosticDescriptor TG0001_GlobalNamespaceNotSupported = new ( - // ReSharper restore InconsistentNaming - "TG0001", - $"{nameof (GenerateEnumExtensionMethodsAttribute)} not supported on global enums", - "{0} is in the global namespace, which is not supported by the source generator ({1}) used by {2}. Move the enum to a namespace or remove the attribute.", - "Usage", - DiagnosticSeverity.Error, - true, - null, - null, - WellKnownDiagnosticTags.NotConfigurable, - WellKnownDiagnosticTags.Compiler); - - // ReSharper disable once InconsistentNaming - private static readonly DiagnosticDescriptor TG0002_UnderlyingTypeNotSupported = new ( - "TG0002", - $"{nameof (GenerateEnumExtensionMethodsAttribute)} not supported for this enum type", - "{0} has an underlying type of {1}, which is not supported by the source generator ({2}) used by {3}. Only enums backed by int or uint are supported.", - "Usage", - DiagnosticSeverity.Error, - true, - null, - null, - WellKnownDiagnosticTags.NotConfigurable, - WellKnownDiagnosticTags.Compiler); - - /// - public override ImmutableArray SupportedDiagnostics { get; } = - [ - TG0001_GlobalNamespaceNotSupported, - TG0002_UnderlyingTypeNotSupported - ]; - - /// - public override void Initialize (AnalysisContext context) - { - context.ConfigureGeneratedCodeAnalysis (GeneratedCodeAnalysisFlags.None); - context.EnableConcurrentExecution (); - - context.RegisterSyntaxNodeAction (CheckAttributeLocations, SyntaxKind.EnumDeclaration); - - return; - - static void CheckAttributeLocations (SyntaxNodeAnalysisContext analysisContext) - { - ISymbol? symbol = analysisContext.SemanticModel.GetDeclaredSymbol (analysisContext.Node) as INamedTypeSymbol; - - if (symbol is not INamedTypeSymbol { EnumUnderlyingType: { } } enumSymbol) - { - // Somehow not even an enum declaration. - // Skip it. - return; - } - - // Check attributes for those we care about and react accordingly. - foreach (AttributeData attributeData in enumSymbol.GetAttributes ()) - { - if (attributeData.AttributeClass?.Name != nameof (GenerateEnumExtensionMethodsAttribute)) - { - // Just skip - not an interesting attribute. - continue; - } - - // Check enum underlying type for supported types (int and uint, currently) - // Report TG0002 if unsupported underlying type. - if (enumSymbol.EnumUnderlyingType is not { SpecialType: SpecialType.System_Int32 or SpecialType.System_UInt32 }) - { - analysisContext.ReportDiagnostic ( - Diagnostic.Create ( - TG0002_UnderlyingTypeNotSupported, - enumSymbol.Locations.FirstOrDefault (), - enumSymbol.Name, - enumSymbol.EnumUnderlyingType.Name, - nameof (EnumExtensionMethodsIncrementalGenerator), - nameof (GenerateEnumExtensionMethodsAttribute) - ) - ); - } - - // Check enum namespace (only non-global supported, currently) - // Report TG0001 if in the global namespace. - if (enumSymbol.ContainingSymbol is not INamespaceSymbol { IsGlobalNamespace: false }) - { - analysisContext.ReportDiagnostic ( - Diagnostic.Create ( - TG0001_GlobalNamespaceNotSupported, - enumSymbol.Locations.FirstOrDefault (), - enumSymbol.Name, - nameof (EnumExtensionMethodsIncrementalGenerator), - nameof (GenerateEnumExtensionMethodsAttribute) - ) - ); - } - } - } - } -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/ApiCompatExcludedAttributes.txt b/Analyzers/Terminal.Gui.Analyzers.Internal/ApiCompatExcludedAttributes.txt deleted file mode 100644 index 503f1f0bb..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/ApiCompatExcludedAttributes.txt +++ /dev/null @@ -1,3 +0,0 @@ -N:System.Runtime.CompilerServices -N:System.Diagnostics.CodeAnalysis -N:System.Numerics \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/AssemblyExtendedEnumTypeAttribute.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/AssemblyExtendedEnumTypeAttribute.cs deleted file mode 100644 index da340e075..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/AssemblyExtendedEnumTypeAttribute.cs +++ /dev/null @@ -1,27 +0,0 @@ -// ReSharper disable ClassNeverInstantiated.Global -// ReSharper disable once RedundantNullableDirective -#nullable enable - -namespace Terminal.Gui.Analyzers.Internal.Attributes; - -/// Assembly attribute declaring a known pairing of an type to an extension class. -/// This attribute should only be written by internal source generators for Terminal.Gui. No other usage of any kind is supported. -[System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = true)] -internal sealed class AssemblyExtendedEnumTypeAttribute : System.Attribute -{ - /// Creates a new instance of from the provided parameters. - /// The of an decorated with a . - /// The of the decorated with an referring to the same type as . - public AssemblyExtendedEnumTypeAttribute (System.Type enumType, System.Type extensionClass) - { - EnumType = enumType; - ExtensionClass = extensionClass; - } - ///An type that has been extended by Terminal.Gui source generators. - public System.Type EnumType { get; init; } - ///A class containing extension methods for . - public System.Type ExtensionClass { get; init; } - - /// - public override string ToString () => $"{EnumType.Name},{ExtensionClass.Name}"; -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/ExtensionsForEnumTypeAttribute.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/ExtensionsForEnumTypeAttribute.cs deleted file mode 100644 index be4b6eef4..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/ExtensionsForEnumTypeAttribute.cs +++ /dev/null @@ -1,37 +0,0 @@ -// ReSharper disable RedundantNameQualifier -// ReSharper disable RedundantNullableDirective -// ReSharper disable UnusedType.Global -#pragma warning disable IDE0001, IDE0240 -#nullable enable - -namespace Terminal.Gui.Analyzers.Internal.Attributes; - -/// -/// Attribute written by the source generator for extension classes, for easier analysis and reflection. -/// -/// -/// Properties are just convenient shortcuts to properties of . -/// -[System.AttributeUsage (System.AttributeTargets.Class | System.AttributeTargets.Interface)] -internal sealed class ExtensionsForEnumTypeAttribute: System.Attribute, IExtensionsForEnumTypeAttributes where TEnum : struct, System.Enum -{ - /// - /// The namespace-qualified name of . - /// - public string EnumFullName => EnumType.FullName!; - - /// - /// The unqualified name of . - /// - public string EnumName => EnumType.Name; - - /// - /// The namespace containing . - /// - public string EnumNamespace => EnumType.Namespace!; - - /// - /// The given by (). - /// - public System.Type EnumType => typeof (TEnum); -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/GenerateEnumExtensionMethodsAttribute.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/GenerateEnumExtensionMethodsAttribute.cs deleted file mode 100644 index 507c45102..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/GenerateEnumExtensionMethodsAttribute.cs +++ /dev/null @@ -1,110 +0,0 @@ -// ReSharper disable RedundantNullableDirective -// ReSharper disable RedundantUsingDirective -// ReSharper disable ClassNeverInstantiated.Global - -#nullable enable -using System; -using Attribute = System.Attribute; -using AttributeUsageAttribute = System.AttributeUsageAttribute; -using AttributeTargets = System.AttributeTargets; - -namespace Terminal.Gui.Analyzers.Internal.Attributes; - -/// -/// Used to enable source generation of a common set of extension methods for enum types. -/// -[AttributeUsage (AttributeTargets.Enum)] -internal sealed class GenerateEnumExtensionMethodsAttribute : Attribute -{ - /// - /// The name of the generated static class. - /// - /// - /// If unspecified, null, empty, or only whitespace, defaults to the name of the enum plus "Extensions".
- /// No other validation is performed, so illegal values will simply result in compiler errors. - /// - /// Explicitly specifying a default value is unnecessary and will result in unnecessary processing. - /// - ///
- public string? ClassName { get; set; } - - /// - /// The namespace in which to place the generated static class containing the extension methods. - /// - /// - /// If unspecified, null, empty, or only whitespace, defaults to the namespace of the enum.
- /// No other validation is performed, so illegal values will simply result in compiler errors. - /// - /// Explicitly specifying a default value is unnecessary and will result in unnecessary processing. - /// - ///
- public string? ClassNamespace { get; set; } - - /// - /// Whether to generate a fast, zero-allocation, non-boxing, and reflection-free alternative to the built-in - /// method. - /// - /// - /// - /// Default: false - /// - /// - /// If the enum is not decorated with , this option has no effect. - /// - /// - /// If multiple members have the same value, the first member with that value will be used and subsequent members - /// with the same value will be skipped. - /// - /// - /// Overloads taking the enum type itself as well as the underlying type of the enum will be generated, enabling - /// avoidance of implicit or explicit cast overhead. - /// - /// - /// Explicitly specifying a default value is unnecessary and will result in unnecessary processing. - /// - /// - public bool FastHasFlags { get; set; } - - /// - /// Whether to generate a fast, zero-allocation, and reflection-free alternative to the built-in - /// method, - /// using a switch expression as a hard-coded reverse mapping of numeric values to explicitly-named members. - /// - /// - /// - /// Default: true - /// - /// - /// If multiple members have the same value, the first member with that value will be used and subsequent members - /// with the same value will be skipped. - /// - /// - /// As with the source generator only considers explicitly-named members.
- /// Generation of values which represent valid bitwise combinations of members of enums decorated with - /// is not affected by this property. - ///
- ///
- public bool FastIsDefined { get; init; } = true; - - /// - /// Gets a value indicating if this instance - /// contains default values only. See remarks of this method or documentation on properties of this type for details. - /// - /// - /// A value indicating if all property values are default for this - /// instance. - /// - /// - /// Default values that will result in a return value are:
- /// && ! && - /// && - /// - ///
- public override bool IsDefaultAttribute () - { - return FastIsDefined - && !FastHasFlags - && ClassName is null - && ClassNamespace is null; - } -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/IExtensionsForEnumTypeAttribute.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/IExtensionsForEnumTypeAttribute.cs deleted file mode 100644 index 4ae8875b7..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/Attributes/IExtensionsForEnumTypeAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -// ReSharper disable All - -using System; - -namespace Terminal.Gui.Analyzers.Internal.Attributes; - -/// -/// Interface to simplify general enumeration of constructed generic types for -/// -/// -internal interface IExtensionsForEnumTypeAttributes -{ - Type EnumType { get; } -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IEqualityOperators.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IEqualityOperators.cs deleted file mode 100644 index 63493a738..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IEqualityOperators.cs +++ /dev/null @@ -1,11 +0,0 @@ -// ReSharper disable once CheckNamespace -namespace System.Numerics; -/// -/// Included for compatibility with .net7+, but has no members. -/// Thus it cannot be explicitly used in generator code. -/// Use it for static analysis only. -/// -/// The left operand type. -/// The right operand type. -/// The return type. -internal interface IEqualityOperators; \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IntrinsicAttribute.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IntrinsicAttribute.cs deleted file mode 100644 index 06cd5b3d5..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/IntrinsicAttribute.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace System.Runtime.CompilerServices; - -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field, Inherited = false)] -public sealed class IntrinsicAttribute : Attribute -{ -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/NumericExtensions.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/NumericExtensions.cs deleted file mode 100644 index 8a6df7be9..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/Compatibility/NumericExtensions.cs +++ /dev/null @@ -1,43 +0,0 @@ -// ReSharper disable once CheckNamespace -namespace Terminal.Gui.Analyzers.Internal.Compatibility; - -/// -/// Extension methods for and types. -/// -/// -/// This is mostly just for backward compatibility with netstandard2.0. -/// -public static class NumericExtensions -{ - /// - /// Gets the population count (number of bits set to 1) of this 32-bit value. - /// - /// The value to get the population count of. - /// - /// The algorithm is the well-known SWAR (SIMD Within A Register) method for population count.
- /// Included for hardware- and runtime- agnostic support for the equivalent of the x86 popcnt instruction, since - /// System.Numerics.Intrinsics isn't available in netstandard2.0.
- /// It performs the operation simultaneously on 4 bytes at a time, rather than the naive method of testing all 32 bits - /// individually.
- /// Most compilers can recognize this and turn it into a single platform-specific instruction, when available. - ///
- /// - /// An unsigned 32-bit integer value containing the population count of . - /// - [MethodImpl (MethodImplOptions.AggressiveInlining)] - public static uint GetPopCount (this uint value) - { - unchecked - { - value -= (value >> 1) & 0x55555555; - value = (value & 0x33333333) + ((value >> 2) & 0x33333333); - value = (value + (value >> 4)) & 0x0F0F0F0F; - - return (value * 0x01010101) >> 24; - } - } - - /// - [MethodImpl (MethodImplOptions.AggressiveInlining)] - public static uint GetPopCount (this int value) { return GetPopCount (Unsafe.As (ref value)); } -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/Constants/Strings.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/Constants/Strings.cs deleted file mode 100644 index 3ffb234a6..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/Constants/Strings.cs +++ /dev/null @@ -1,204 +0,0 @@ -// ReSharper disable MemberCanBePrivate.Global - -using System; -using System.CodeDom.Compiler; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using Terminal.Gui.Analyzers.Internal.Attributes; - -namespace Terminal.Gui.Analyzers.Internal.Constants; - -/// String constants for frequently-used boilerplate. -/// These are for performance, instead of using Roslyn to build it all during execution of analyzers. -internal static class Strings -{ - internal const string AnalyzersAttributesNamespace = $"{InternalAnalyzersNamespace}.Attributes"; - - internal const string AssemblyExtendedEnumTypeAttributeFullName = $"{AnalyzersAttributesNamespace}.{nameof (AssemblyExtendedEnumTypeAttribute)}"; - - internal const string DefaultTypeNameSuffix = "Extensions"; - - internal const string FallbackClassNamespace = $"{TerminalGuiRootNamespace}"; - - internal const string InternalAnalyzersNamespace = $"{AnalyzersRootNamespace}.Internal"; - - internal const string TerminalGuiRootNamespace = "Terminal.Gui"; - - private const string AnalyzersRootNamespace = $"{TerminalGuiRootNamespace}.Analyzers"; - private const string NetStandard20CompatibilityNamespace = $"{InternalAnalyzersNamespace}.Compatibility"; - - /// - /// Names of dotnet namespaces and types. Included as compile-time constants to avoid unnecessary work for the Roslyn - /// source generators. - /// - /// Implemented as nested static types because XmlDoc doesn't work on namespaces. - internal static class DotnetNames - { - /// Fully-qualified attribute type names. Specific applications (uses) are in . - internal static class Attributes - { - /// - internal const string CompilerGenerated = $"{Namespaces.System_Runtime_CompilerServices}.{nameof (CompilerGeneratedAttribute)}"; - - /// - internal const string DebuggerNonUserCode = $"{Namespaces.System_Diagnostics}.{nameof (DebuggerNonUserCodeAttribute)}"; - - /// - internal const string ExcludeFromCodeCoverage = $"{Namespaces.System_Diagnostics_CodeAnalysis}.{nameof (ExcludeFromCodeCoverageAttribute)}"; - - internal const string Flags = $"{Namespaces.SystemNs}.{nameof (FlagsAttribute)}"; - - internal const string GeneratedCode = $"{Namespaces.System_CodeDom_Compiler}.{nameof (GeneratedCodeAttribute)}"; - - /// - /// Use of this attribute should be carefully evaluated. - internal const string MethodImpl = $"{Namespaces.System_Runtime_CompilerServices}.{nameof (MethodImplAttribute)}"; - - /// Attributes formatted for use in code, including square brackets. - internal static class Applications - { - // ReSharper disable MemberHidesStaticFromOuterClass - internal const string Flags = $"[{Attributes.Flags}]"; - - /// - internal const string GeneratedCode = $"""[{Attributes.GeneratedCode}("{InternalAnalyzersNamespace}","1.0")]"""; - - /// - /// Use of this attribute should be carefully evaluated. - internal const string AggressiveInlining = $"[{MethodImpl}({Types.MethodImplOptions}.{nameof (MethodImplOptions.AggressiveInlining)})]"; - - /// - internal const string DebuggerNonUserCode = $"[{Attributes.DebuggerNonUserCode}]"; - - /// - internal const string CompilerGenerated = $"[{Attributes.CompilerGenerated}]"; - - /// - internal const string ExcludeFromCodeCoverage = $"[{Attributes.ExcludeFromCodeCoverage}]"; - - // ReSharper restore MemberHidesStaticFromOuterClass - } - } - - /// Names of dotnet namespaces. - internal static class Namespaces - { - internal const string SystemNs = nameof (System); - // ReSharper disable InconsistentNaming - internal const string System_CodeDom = $"{SystemNs}.{nameof (System.CodeDom)}"; - internal const string System_CodeDom_Compiler = $"{System_CodeDom}.{nameof (System.CodeDom.Compiler)}"; - internal const string System_ComponentModel = $"{SystemNs}.{nameof (System.ComponentModel)}"; - internal const string System_Diagnostics = $"{SystemNs}.{nameof (System.Diagnostics)}"; - internal const string System_Diagnostics_CodeAnalysis = $"{System_Diagnostics}.{nameof (System.Diagnostics.CodeAnalysis)}"; - internal const string System_Numerics = $"{SystemNs}.{nameof (System.Numerics)}"; - internal const string System_Runtime = $"{SystemNs}.{nameof (System.Runtime)}"; - internal const string System_Runtime_CompilerServices = $"{System_Runtime}.{nameof (System.Runtime.CompilerServices)}"; - // ReSharper restore InconsistentNaming - } - - internal static class Types - { - internal const string Attribute = $"{Namespaces.SystemNs}.{nameof (System.Attribute)}"; - internal const string AttributeTargets = $"{Namespaces.SystemNs}.{nameof (System.AttributeTargets)}"; - internal const string AttributeUsageAttribute = $"{Namespaces.SystemNs}.{nameof (System.AttributeUsageAttribute)}"; - - internal const string MethodImplOptions = - $"{Namespaces.System_Runtime_CompilerServices}.{nameof (System.Runtime.CompilerServices.MethodImplOptions)}"; - } - } - - internal static class Templates - { - internal const string AutoGeneratedCommentBlock = $""" - //------------------------------------------------------------------------------ - // - // This file and the code it contains was generated by a source generator in - // the {InternalAnalyzersNamespace} library. - // - // Modifications to this file are not supported and will be lost when - // source generation is triggered, either implicitly or explicitly. - // - //------------------------------------------------------------------------------ - """; - - /// - /// A set of explicit type aliases to work around Terminal.Gui having name collisions with types like - /// . - /// - internal const string DotnetExplicitTypeAliasUsingDirectives = $""" - using Attribute = {DotnetNames.Types.Attribute}; - using AttributeUsageAttribute = {DotnetNames.Types.AttributeUsageAttribute}; - using GeneratedCode = {DotnetNames.Attributes.GeneratedCode}; - """; - - /// Using directives for common namespaces in generated code. - internal const string DotnetNamespaceUsingDirectives = $""" - using {DotnetNames.Namespaces.SystemNs}; - using {DotnetNames.Namespaces.System_CodeDom}; - using {DotnetNames.Namespaces.System_CodeDom_Compiler}; - using {DotnetNames.Namespaces.System_ComponentModel}; - using {DotnetNames.Namespaces.System_Numerics}; - using {DotnetNames.Namespaces.System_Runtime}; - using {DotnetNames.Namespaces.System_Runtime_CompilerServices}; - """; - - /// - /// A set of empty namespaces that MAY be referenced in generated code, especially in using statements, - /// which are always included to avoid additional complexity due to conditional compilation. - /// - internal const string DummyNamespaceDeclarations = $$""" - // These are dummy declarations to avoid complexity with conditional compilation. - #pragma warning disable IDE0079 // Remove unnecessary suppression - #pragma warning disable RCS1259 // Remove empty syntax - namespace {{TerminalGuiRootNamespace}} { } - namespace {{AnalyzersRootNamespace}} { } - namespace {{InternalAnalyzersNamespace}} { } - namespace {{NetStandard20CompatibilityNamespace}} { } - namespace {{AnalyzersAttributesNamespace}} { } - #pragma warning restore RCS1259 // Remove empty syntax - #pragma warning restore IDE0079 // Remove unnecessary suppression - """; - - internal const string StandardHeader = $""" - {AutoGeneratedCommentBlock} - // ReSharper disable RedundantUsingDirective - // ReSharper disable once RedundantNullableDirective - {NullableContextDirective} - - {StandardUsingDirectivesText} - """; - - /// - /// Standard set of using directives for generated extension method class files. - /// Not all are always needed, but all are included so we don't have to worry about it. - /// - internal const string StandardUsingDirectivesText = $""" - {DotnetNamespaceUsingDirectives} - {DotnetExplicitTypeAliasUsingDirectives} - using {TerminalGuiRootNamespace}; - using {AnalyzersRootNamespace}; - using {InternalAnalyzersNamespace}; - using {AnalyzersAttributesNamespace}; - using {NetStandard20CompatibilityNamespace}; - """; - - internal const string AttributesForGeneratedInterfaces = $""" - {DotnetNames.Attributes.Applications.GeneratedCode} - {DotnetNames.Attributes.Applications.CompilerGenerated} - """; - - internal const string AttributesForGeneratedTypes = $""" - {DotnetNames.Attributes.Applications.GeneratedCode} - {DotnetNames.Attributes.Applications.CompilerGenerated} - {DotnetNames.Attributes.Applications.DebuggerNonUserCode} - {DotnetNames.Attributes.Applications.ExcludeFromCodeCoverage} - """; - - /// - /// Preprocessor directive to enable nullability context for generated code.
- /// This should always be emitted, as it applies only to generated code.
- /// As such, generated code MUST be properly annotated. - ///
- internal const string NullableContextDirective = "#nullable enable"; - } -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/CodeWriter.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/CodeWriter.cs deleted file mode 100644 index f35e20d88..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/CodeWriter.cs +++ /dev/null @@ -1,235 +0,0 @@ -using System; -using System.CodeDom.Compiler; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Text; -using Microsoft.CodeAnalysis.Text; -using Terminal.Gui.Analyzers.Internal.Constants; - -namespace Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions; - -/// -/// The class responsible for turning an -/// into actual C# code. -/// -/// Try to use this type as infrequently as possible. -/// -/// A reference to an which will be used -/// to generate the extension class code. The object will not be validated, -/// so it is critical that it be correct and remain unchanged while in use -/// by an instance of this class. Behavior if those rules are not followed -/// is undefined. -/// -[SuppressMessage ("CodeQuality", "IDE0079", Justification = "Suppressions here are intentional and the warnings they disable are just noise.")] -internal sealed class CodeWriter (in EnumExtensionMethodsGenerationInfo metadata) : IStandardCSharpCodeGenerator -{ - // Using the null suppression operator here because this will always be - // initialized to non-null before a reference to it is returned. - private SourceText _sourceText = null!; - - /// - public EnumExtensionMethodsGenerationInfo Metadata - { - [MethodImpl (MethodImplOptions.AggressiveInlining)] - [return: NotNull] - get; - [param: DisallowNull] - set; - } = metadata; - - /// - public ref readonly SourceText GenerateSourceText (Encoding? encoding = null) - { - encoding ??= Encoding.UTF8; - _sourceText = SourceText.From (GetFullSourceText (), encoding); - - return ref _sourceText; - } - - /// - /// Gets the using directive for the namespace containing the enum, - /// if different from the extension class namespace, or an empty string, if they are the same. - /// - private string EnumNamespaceUsingDirective => Metadata.TargetTypeNamespace != Metadata.GeneratedTypeNamespace - - // ReSharper disable once HeapView.ObjectAllocation - ? $"using {Metadata.TargetTypeNamespace};" - : string.Empty; - - private string EnumTypeKeyword => Metadata.EnumBackingTypeCode switch - { - TypeCode.Int32 => "int", - TypeCode.UInt32 => "uint", - _ => string.Empty - }; - - /// Gets the class declaration line. - private string ExtensionClassDeclarationLine => $"public static partial class {Metadata.GeneratedTypeName}"; - - // ReSharper disable once HeapView.ObjectAllocation - /// Gets the XmlDoc for the extension class declaration. - private string ExtensionClassDeclarationXmlDoc => - $"/// Extension methods for the type."; - - // ReSharper disable once HeapView.ObjectAllocation - /// Gets the extension class file-scoped namespace directive. - private string ExtensionClassNamespaceDirective => $"namespace {Metadata.GeneratedTypeNamespace};"; - - /// - /// An attribute to decorate the extension class with for easy mapping back to the target enum type, for reflection and - /// analysis. - /// - private string ExtensionsForTypeAttributeLine => $"[ExtensionsForEnumType<{Metadata.TargetTypeFullName}>]"; - - /// - /// Creates the code for the FastHasFlags method. - /// - /// - /// Since the generator already only writes code for enums backed by and , - /// this method is safe, as we'll always be using a DWORD. - /// - /// An instance of an to write to. - private void GetFastHasFlagsMethods (IndentedTextWriter w) - { - // The version taking the same enum type as the check value. - w.WriteLine ( - $"/// Determines if the specified flags are set in the current value of this ."); - w.WriteLine ("/// NO VALIDATION IS PERFORMED!"); - - w.WriteLine ( - $"/// True, if all flags present in are also present in the current value of the .
Otherwise false.
"); - w.WriteLine (Strings.DotnetNames.Attributes.Applications.AggressiveInlining); - - w.Push ( - $"{Metadata.Accessibility.ToCSharpString ()} static bool FastHasFlags (this {Metadata.TargetTypeFullName} e, {Metadata.TargetTypeFullName} checkFlags)"); - w.WriteLine ($"ref uint enumCurrentValueRef = ref Unsafe.As<{Metadata.TargetTypeFullName},uint> (ref e);"); - w.WriteLine ($"ref uint checkFlagsValueRef = ref Unsafe.As<{Metadata.TargetTypeFullName},uint> (ref checkFlags);"); - w.WriteLine ("return (enumCurrentValueRef & checkFlagsValueRef) == checkFlagsValueRef;"); - w.Pop (); - - // The version taking the underlying type of the enum as the check value. - w.WriteLine ( - $"/// Determines if the specified mask bits are set in the current value of this ."); - - w.WriteLine ( - $"/// The value to check against the value."); - w.WriteLine ("/// A mask to apply to the current value."); - - w.WriteLine ( - $"/// True, if all bits set to 1 in the mask are also set to 1 in the current value of the .
Otherwise false.
"); - w.WriteLine ("/// NO VALIDATION IS PERFORMED!"); - w.WriteLine (Strings.DotnetNames.Attributes.Applications.AggressiveInlining); - - w.Push ( - $"{Metadata.Accessibility.ToCSharpString ()} static bool FastHasFlags (this {Metadata.TargetTypeFullName} e, {EnumTypeKeyword} mask)"); - w.WriteLine ($"ref {EnumTypeKeyword} enumCurrentValueRef = ref Unsafe.As<{Metadata.TargetTypeFullName},{EnumTypeKeyword}> (ref e);"); - w.WriteLine ("return (enumCurrentValueRef & mask) == mask;"); - w.Pop (); - } - - /// - /// Creates the code for the FastIsDefined method. - /// - [SuppressMessage ("ReSharper", "SwitchStatementHandlesSomeKnownEnumValuesWithDefault", Justification = "Only need to handle int and uint.")] - [SuppressMessage ("ReSharper", "SwitchStatementMissingSomeEnumCasesNoDefault", Justification = "Only need to handle int and uint.")] - private void GetFastIsDefinedMethod (IndentedTextWriter w) - { - w.WriteLine ( - $"/// Determines if the specified value is explicitly defined as a named value of the type."); - - w.WriteLine ( - "/// Only explicitly named values return true, as with IsDefined. Combined valid flag values of flags enums which are not explicitly named will return false."); - - w.Push ( - $"{Metadata.Accessibility.ToCSharpString ()} static bool FastIsDefined (this {Metadata.TargetTypeFullName} e, {EnumTypeKeyword} value)"); - w.Push ("return value switch"); - - switch (Metadata.EnumBackingTypeCode) - { - case TypeCode.Int32: - foreach (int definedValue in Metadata._intMembers) - { - w.WriteLine ($"{definedValue:D} => true,"); - } - - break; - case TypeCode.UInt32: - foreach (uint definedValue in Metadata._uIntMembers) - { - w.WriteLine ($"{definedValue:D} => true,"); - } - - break; - } - - w.WriteLine ("_ => false"); - - w.Pop ("};"); - w.Pop (); - } - - private string GetFullSourceText () - { - StringBuilder sb = new ( - $""" - {Strings.Templates.StandardHeader} - - [assembly: {Strings.AssemblyExtendedEnumTypeAttributeFullName} (typeof({Metadata.TargetTypeFullName}), typeof({Metadata.GeneratedTypeFullName}))] - - {EnumNamespaceUsingDirective} - {ExtensionClassNamespaceDirective} - {ExtensionClassDeclarationXmlDoc} - {Strings.Templates.AttributesForGeneratedTypes} - {ExtensionsForTypeAttributeLine} - {ExtensionClassDeclarationLine} - - """, - 4096); - - using IndentedTextWriter w = new (new StringWriter (sb)); - w.Push (); - - GetNamedValuesToInt32Method (w); - GetNamedValuesToUInt32Method (w); - - if (Metadata.GenerateFastIsDefined) - { - GetFastIsDefinedMethod (w); - } - - if (Metadata.GenerateFastHasFlags) - { - GetFastHasFlagsMethods (w); - } - - w.Pop (); - - w.Flush (); - - return sb.ToString (); - } - - [MethodImpl (MethodImplOptions.AggressiveInlining)] - private void GetNamedValuesToInt32Method (IndentedTextWriter w) - { - w.WriteLine ( - $"/// Directly converts this value to an value with the same binary representation."); - w.WriteLine ("/// NO VALIDATION IS PERFORMED!"); - w.WriteLine (Strings.DotnetNames.Attributes.Applications.AggressiveInlining); - w.Push ($"{Metadata.Accessibility.ToCSharpString ()} static int AsInt32 (this {Metadata.TargetTypeFullName} e)"); - w.WriteLine ($"return Unsafe.As<{Metadata.TargetTypeFullName},int> (ref e);"); - w.Pop (); - } - - [MethodImpl (MethodImplOptions.AggressiveInlining)] - private void GetNamedValuesToUInt32Method (IndentedTextWriter w) - { - w.WriteLine ( - $"/// Directly converts this value to a value with the same binary representation."); - w.WriteLine ("/// NO VALIDATION IS PERFORMED!"); - w.WriteLine (Strings.DotnetNames.Attributes.Applications.AggressiveInlining); - w.Push ($"{Metadata.Accessibility.ToCSharpString ()} static uint AsUInt32 (this {Metadata.TargetTypeFullName} e)"); - w.WriteLine ($"return Unsafe.As<{Metadata.TargetTypeFullName},uint> (ref e);"); - w.Pop (); - } -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumExtensionMethodsGenerationInfo.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumExtensionMethodsGenerationInfo.cs deleted file mode 100644 index cab633cbf..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumExtensionMethodsGenerationInfo.cs +++ /dev/null @@ -1,443 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading; -using JetBrains.Annotations; -using Microsoft.CodeAnalysis; -using Terminal.Gui.Analyzers.Internal.Attributes; -using Terminal.Gui.Analyzers.Internal.Constants; - -namespace Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions; - -/// -/// Type containing the information necessary to generate code according to the declared attribute values, -/// as well as the actual code to create the corresponding source code text, to be used in the -/// source generator pipeline. -/// -/// -/// Minimal validation is performed by this type.
-/// Errors in analyzed source code will result in generation failure or broken output.
-/// This type is not intended for use outside of Terminal.Gui library development. -///
-internal sealed record EnumExtensionMethodsGenerationInfo : IGeneratedTypeMetadata, - IEqualityOperators -{ - private const int ExplicitFastHasFlagsMask = 0b_0100; - private const int ExplicitFastIsDefinedMask = 0b_1000; - private const int ExplicitNameMask = 0b_0010; - private const int ExplicitNamespaceMask = 0b_0001; - private const string GeneratorAttributeFullyQualifiedName = $"{GeneratorAttributeNamespace}.{GeneratorAttributeName}"; - private const string GeneratorAttributeName = nameof (GenerateEnumExtensionMethodsAttribute); - private const string GeneratorAttributeNamespace = Strings.AnalyzersAttributesNamespace; - - /// - /// Type containing the information necessary to generate code according to the declared attribute values, - /// as well as the actual code to create the corresponding source code text, to be used in the - /// source generator pipeline. - /// - /// The fully-qualified namespace of the enum type, without assembly name. - /// - /// The name of the enum type, as would be given by on the enum's type - /// declaration. - /// - /// - /// The fully-qualified namespace in which to place the generated code, without assembly name. If omitted or explicitly - /// null, uses the value provided in . - /// - /// - /// The name of the generated class. If omitted or explicitly null, appends "Extensions" to the value of - /// . - /// - /// The backing type of the enum. Defaults to . - /// - /// Whether to generate a fast HasFlag alternative. (Default: true) Ignored if the enum does not also have - /// . - /// - /// Whether to generate a fast IsDefined alternative. (Default: true) - /// - /// Minimal validation is performed by this type.
- /// Errors in analyzed source code will result in generation failure or broken output.
- /// This type is not intended for use outside of Terminal.Gui library development. - ///
- public EnumExtensionMethodsGenerationInfo ( - string enumNamespace, - string enumTypeName, - string? typeNamespace = null, - string? typeName = null, - TypeCode enumBackingTypeCode = TypeCode.Int32, - bool generateFastHasFlags = true, - bool generateFastIsDefined = true - ) : this (enumNamespace, enumTypeName, enumBackingTypeCode) - { - GeneratedTypeNamespace = typeNamespace ?? enumNamespace; - GeneratedTypeName = typeName ?? string.Concat (enumTypeName, Strings.DefaultTypeNameSuffix); - GenerateFastHasFlags = generateFastHasFlags; - GenerateFastIsDefined = generateFastIsDefined; - } - - public EnumExtensionMethodsGenerationInfo (string enumNamespace, string enumTypeName, TypeCode enumBackingType) - { - // Interning these since they're rather unlikely to change. - string enumInternedNamespace = string.Intern (enumNamespace); - string enumInternedName = string.Intern (enumTypeName); - TargetTypeNamespace = enumInternedNamespace; - TargetTypeName = enumInternedName; - EnumBackingTypeCode = enumBackingType; - } - - [AccessedThroughProperty (nameof (EnumBackingTypeCode))] - private readonly TypeCode _enumBackingTypeCode; - - [AccessedThroughProperty (nameof (GeneratedTypeName))] - private string? _generatedTypeName; - - [AccessedThroughProperty (nameof (GeneratedTypeNamespace))] - private string? _generatedTypeNamespace; - - private BitVector32 _discoveredProperties = new (0); - - /// The name of the extension class. - public string? GeneratedTypeName - { - get => _generatedTypeName ?? string.Concat (TargetTypeName, Strings.DefaultTypeNameSuffix); - set => _generatedTypeName = value ?? string.Concat (TargetTypeName, Strings.DefaultTypeNameSuffix); - } - - /// The namespace for the extension class. - /// - /// Value is not validated by the set accessor.
- /// Get accessor will never return null and is thus marked [NotNull] for static analysis, even though the property is - /// declared as a nullable .
If the backing field for this property is null, the get - /// accessor will return instead. - ///
- public string? GeneratedTypeNamespace - { - get => _generatedTypeNamespace ?? TargetTypeNamespace; - set => _generatedTypeNamespace = value ?? TargetTypeNamespace; - } - - /// - public string TargetTypeFullName => string.Concat (TargetTypeNamespace, ".", TargetTypeName); - - /// - public Accessibility Accessibility - { - get; - [UsedImplicitly] - internal set; - } = Accessibility.Public; - - /// - public TypeKind TypeKind => TypeKind.Class; - - /// - public bool IsRecord => false; - - /// - public bool IsClass => true; - - /// - public bool IsStruct => false; - - /// - public bool IsByRefLike => false; - - /// - public bool IsSealed => false; - - /// - public bool IsAbstract => false; - - /// - public bool IsEnum => false; - - /// - public bool IsStatic => true; - - /// - public bool IncludeInterface => false; - - public string GeneratedTypeFullName => $"{GeneratedTypeNamespace}.{GeneratedTypeName}"; - - /// Whether to generate the extension class as partial (Default: true) - public bool IsPartial => true; - - /// The fully-qualified namespace of the source enum type. - public string TargetTypeNamespace - { - get; - [UsedImplicitly] - set; - } - - /// The UNQUALIFIED name of the source enum type. - public string TargetTypeName - { - get; - [UsedImplicitly] - set; - } - - /// - /// The backing type for the enum. - /// - /// For simplicity and formality, only System.Int32 and System.UInt32 are supported at this time. - public TypeCode EnumBackingTypeCode - { - get => _enumBackingTypeCode; - init - { - if (value is not TypeCode.Int32 and not TypeCode.UInt32) - { - throw new NotSupportedException ("Only System.Int32 and System.UInt32 are supported at this time."); - } - - _enumBackingTypeCode = value; - } - } - - /// - /// Whether a fast alternative to the built-in Enum.HasFlag method will be generated (Default: false) - /// - public bool GenerateFastHasFlags { [UsedImplicitly] get; set; } - - /// Whether a switch-based IsDefined replacement will be generated (Default: true) - public bool GenerateFastIsDefined { [UsedImplicitly]get; set; } = true; - - internal ImmutableHashSet? _intMembers; - internal ImmutableHashSet? _uIntMembers; - - /// - /// Fully-qualified name of the extension class - /// - internal string FullyQualifiedClassName => $"{GeneratedTypeNamespace}.{GeneratedTypeName}"; - - /// - /// Whether a Flags was found on the enum type. - /// - internal bool HasFlagsAttribute {[UsedImplicitly] get; set; } - - private static readonly SymbolDisplayFormat FullyQualifiedSymbolDisplayFormatWithoutGlobal = - SymbolDisplayFormat.FullyQualifiedFormat - .WithGlobalNamespaceStyle ( - SymbolDisplayGlobalNamespaceStyle.Omitted); - - - internal bool TryConfigure (INamedTypeSymbol enumSymbol, CancellationToken cancellationToken) - { - using var cts = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken); - cts.Token.ThrowIfCancellationRequested (); - - ImmutableArray attributes = enumSymbol.GetAttributes (); - - // This is theoretically impossible, but guarding just in case and canceling if it does happen. - if (attributes.Length == 0) - { - cts.Cancel (true); - - return false; - } - - // Check all attributes provided for anything interesting. - // Attributes can be in any order, so just check them all and adjust at the end if necessary. - // Note that we do not perform as strict validation on actual usage of the attribute, at this stage, - // because the analyzer should have already thrown errors for invalid uses like global namespace - // or unsupported enum underlying types. - foreach (AttributeData attr in attributes) - { - cts.Token.ThrowIfCancellationRequested (); - string? attributeFullyQualifiedName = attr.AttributeClass?.ToDisplayString (FullyQualifiedSymbolDisplayFormatWithoutGlobal); - - // Skip if null or not possibly an attribute we care about - if (attributeFullyQualifiedName is null or not { Length: >= 5 }) - { - continue; - } - - switch (attributeFullyQualifiedName) - { - // For Flags enums - case Strings.DotnetNames.Attributes.Flags: - { - HasFlagsAttribute = true; - } - - continue; - - // For the attribute that started this whole thing - case GeneratorAttributeFullyQualifiedName: - - { - // If we can't successfully complete this method, - // something is wrong enough that we may as well just stop now. - if (!TryConfigure (attr, cts.Token)) - { - if (cts.Token.CanBeCanceled) - { - cts.Cancel (); - } - - return false; - } - } - - continue; - } - } - - // Now get the members, if we know we'll need them. - if (GenerateFastIsDefined || GenerateFastHasFlags) - { - if (EnumBackingTypeCode == TypeCode.Int32) - { - PopulateIntMembersHashSet (enumSymbol); - } - else if (EnumBackingTypeCode == TypeCode.UInt32) - { - PopulateUIntMembersHashSet (enumSymbol); - } - } - - return true; - } - - private void PopulateIntMembersHashSet (INamedTypeSymbol enumSymbol) - { - ImmutableArray enumMembers = enumSymbol.GetMembers (); - IEnumerable fieldSymbols = enumMembers.OfType (); - _intMembers = fieldSymbols.Select (static m => m.HasConstantValue ? (int)m.ConstantValue : 0).ToImmutableHashSet (); - } - private void PopulateUIntMembersHashSet (INamedTypeSymbol enumSymbol) - { - _uIntMembers = enumSymbol.GetMembers ().OfType ().Select (static m => (uint)m.ConstantValue).ToImmutableHashSet (); - } - - private bool HasExplicitFastHasFlags - { - [UsedImplicitly]get => _discoveredProperties [ExplicitFastHasFlagsMask]; - set => _discoveredProperties [ExplicitFastHasFlagsMask] = value; - } - - private bool HasExplicitFastIsDefined - { - [UsedImplicitly]get => _discoveredProperties [ExplicitFastIsDefinedMask]; - set => _discoveredProperties [ExplicitFastIsDefinedMask] = value; - } - - private bool HasExplicitTypeName - { - get => _discoveredProperties [ExplicitNameMask]; - set => _discoveredProperties [ExplicitNameMask] = value; - } - - private bool HasExplicitTypeNamespace - { - get => _discoveredProperties [ExplicitNamespaceMask]; - set => _discoveredProperties [ExplicitNamespaceMask] = value; - } - - [MemberNotNullWhen (true, nameof (_generatedTypeName), nameof (_generatedTypeNamespace))] - private bool TryConfigure (AttributeData attr, CancellationToken cancellationToken) - { - using var cts = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken); - cts.Token.ThrowIfCancellationRequested (); - - if (attr is not { NamedArguments.Length: > 0 }) - { - // Just a naked attribute, so configure with appropriate defaults. - GeneratedTypeNamespace = TargetTypeNamespace; - GeneratedTypeName = string.Concat (TargetTypeName, Strings.DefaultTypeNameSuffix); - - return true; - } - - cts.Token.ThrowIfCancellationRequested (); - - foreach (KeyValuePair kvp in attr.NamedArguments) - { - string? propName = kvp.Key; - TypedConstant propValue = kvp.Value; - - cts.Token.ThrowIfCancellationRequested (); - - // For every property name and value pair, set associated metadata - // property, if understood. - switch (propName, propValue) - { - // Null or empty string doesn't make sense, so skip if it happens. - case (null, _): - case ("", _): - continue; - - // ClassName is specified, not explicitly null, and at least 1 character long. - case (AttributeProperties.TypeNamePropertyName, { IsNull: false, Value: string { Length: > 1 } classNameProvidedValue }): - if (string.IsNullOrWhiteSpace (classNameProvidedValue)) - { - return false; - } - - GeneratedTypeName = classNameProvidedValue; - HasExplicitTypeName = true; - - continue; - - // Class namespace is specified, not explicitly null, and at least 1 character long. - case (AttributeProperties.TypeNamespacePropertyName, { IsNull: false, Value: string { Length: > 1 } classNamespaceProvidedValue }): - - if (string.IsNullOrWhiteSpace (classNamespaceProvidedValue)) - { - return false; - } - - GeneratedTypeNamespace = classNamespaceProvidedValue; - HasExplicitTypeNamespace = true; - - continue; - - // FastHasFlags is specified - case (AttributeProperties.FastHasFlagsPropertyName, { IsNull: false } fastHasFlagsConstant): - GenerateFastHasFlags = fastHasFlagsConstant.Value is true; - HasExplicitFastHasFlags = true; - - continue; - - // FastIsDefined is specified - case (AttributeProperties.FastIsDefinedPropertyName, { IsNull: false } fastIsDefinedConstant): - GenerateFastIsDefined = fastIsDefinedConstant.Value is true; - HasExplicitFastIsDefined = true; - - continue; - } - } - - // The rest is simple enough it's not really worth worrying about cancellation, so don't bother from here on... - - // Configure anything that wasn't specified that doesn't have an implicitly safe default - if (!HasExplicitTypeName || _generatedTypeName is null) - { - _generatedTypeName = string.Concat (TargetTypeName, Strings.DefaultTypeNameSuffix); - } - - if (!HasExplicitTypeNamespace || _generatedTypeNamespace is null) - { - _generatedTypeNamespace = TargetTypeNamespace; - } - - if (!HasFlagsAttribute) - { - GenerateFastHasFlags = false; - } - - return true; - } - - private static class AttributeProperties - { - internal const string FastHasFlagsPropertyName = nameof (GenerateEnumExtensionMethodsAttribute.FastHasFlags); - internal const string FastIsDefinedPropertyName = nameof (GenerateEnumExtensionMethodsAttribute.FastIsDefined); - internal const string TypeNamePropertyName = nameof (GenerateEnumExtensionMethodsAttribute.ClassName); - internal const string TypeNamespacePropertyName = nameof (GenerateEnumExtensionMethodsAttribute.ClassNamespace); - } -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumExtensionMethodsIncrementalGenerator.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumExtensionMethodsIncrementalGenerator.cs deleted file mode 100644 index 7629fd8c2..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/Generators/EnumExtensions/EnumExtensionMethodsIncrementalGenerator.cs +++ /dev/null @@ -1,452 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Text; -using System.Threading; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Text; -using Terminal.Gui.Analyzers.Internal.Attributes; -using Terminal.Gui.Analyzers.Internal.Constants; - -namespace Terminal.Gui.Analyzers.Internal.Generators.EnumExtensions; - -/// -/// Incremental code generator for enums decorated with . -/// -[SuppressMessage ("CodeQuality", "IDE0079", Justification = "Suppressions here are intentional and the warnings they disable are just noise.")] -[Generator (LanguageNames.CSharp)] -public sealed class EnumExtensionMethodsIncrementalGenerator : IIncrementalGenerator -{ - private const string ExtensionsForEnumTypeAttributeFullyQualifiedName = $"{Strings.AnalyzersAttributesNamespace}.{ExtensionsForEnumTypeAttributeName}"; - private const string ExtensionsForEnumTypeAttributeName = "ExtensionsForEnumTypeAttribute"; - private const string GeneratorAttributeFullyQualifiedName = $"{Strings.AnalyzersAttributesNamespace}.{GeneratorAttributeName}"; - private const string GeneratorAttributeName = nameof (GenerateEnumExtensionMethodsAttribute); - - /// Fully-qualified symbol name format without the "global::" prefix. - private static readonly SymbolDisplayFormat _fullyQualifiedSymbolDisplayFormatWithoutGlobal = - SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle (SymbolDisplayGlobalNamespaceStyle.Omitted); - - /// - /// - /// - /// Basically, this method is called once by the compiler, and is responsible for wiring up - /// everything important about how source generation works. - /// - /// - /// See in-line comments for specifics of what's going on. - /// - /// - /// Note that is everything in the compilation, - /// except for code generated by this generator or generators which have not yet executed.
- /// The methods registered to perform generation get called on-demand by the host (the IDE, - /// compiler, etc), sometimes as often as every single keystroke. - ///
- ///
- public void Initialize (IncrementalGeneratorInitializationContext context) - { - // Write out namespaces that may be used later. Harmless to declare them now and will avoid - // additional processing and potential omissions later on. - context.RegisterPostInitializationOutput (GenerateDummyNamespaces); - - // This executes the delegate given to it immediately after Roslyn gets all set up. - // - // As written, this will result in the GenerateEnumExtensionMethodsAttribute code - // being added to the environment, so that it can be used without having to actually - // be declared explicitly in the target project. - // This is important, as it guarantees the type will exist and also guarantees it is - // defined exactly as the generator expects it to be defined. - context.RegisterPostInitializationOutput (GenerateAttributeSources); - - // Next up, we define our pipeline. - // To do so, we create one or more IncrementalValuesProvider objects, each of which - // defines on stage of analysis or generation as needed. - // - // Critically, these must be as fast and efficient as reasonably possible because, - // once the pipeline is registered, this stuff can get called A LOT. - // - // Note that declaring these doesn't really do much of anything unless they are given to the - // RegisterSourceOutput method at the end of this method. - // - // The delegates are not actually evaluated right here. That is triggered by changes being - // made to the source code. - - // This provider grabs attributes that pass our filter and then creates lightweight - // metadata objects to be used in the final code generation step. - // It also preemptively removes any nulls from the collection before handing things off - // to the code generation logic. - IncrementalValuesProvider enumGenerationInfos = - context - .SyntaxProvider - - // This method is a highly-optimized (and highly-recommended) filter on the incoming - // code elements that only bothers to present code that is annotated with the specified - // attribute, by its fully-qualified name, as a string, which is the first parameter. - // - // Two delegates are passed to it, in the second and third parameters. - // - // The second parameter is a filter predicate taking each SyntaxNode that passes the - // name filter above, and then refines that result. - // - // It is critical that the filter predicate be as simple and fast as possible, as it - // will be called a ton, triggered by keystrokes or anything else that modifies code - // in or even related to (in either direction) the pre-filtered code. - // It should collect metadata only and not actually generate any code. - // It must return a boolean indicating whether the supplied SyntaxNode should be - // given to the transform delegate at all. - // - // The third parameter is the "transform" delegate. - // That one only runs when code is changed that passed both the attribute name filter - // and the filter predicate in the second parameter. - // It will be called for everything that passes both of those, so it can still happen - // a lot, but should at least be pretty close. - // In our case, it should be 100% accurate, since we're using OUR attribute, which can - // only be applied to enum types in the first place. - // - // That delegate is responsible for creating some sort of lightweight data structure - // which can later be used to generate the actual source code for output. - // - // THIS DELEGATE DOES NOT GENERATE CODE! - // However, it does need to return instances of the metadata class in use that are either - // null or complete enough to generate meaningful code from, later on. - // - // We then filter out any that were null with the .Where call at the end, so that we don't - // know or care about them when it's time to generate code. - // - // While the syntax of that .Where call is the same as LINQ, that is actually a - // highly-optimized implementation specifically for this use. - .ForAttributeWithMetadataName ( - GeneratorAttributeFullyQualifiedName, - IsPotentiallyInterestingDeclaration, - GatherMetadataForCodeGeneration - ) - .WithTrackingName ("CollectEnumMetadata") - .Where (static eInfo => eInfo is { }); - - // Finally, we wire up any IncrementalValuesProvider instances above to the appropriate - // delegate that takes the SourceProductionContext that is current at run-time and an instance of - // our metadata type and takes appropriate action. - // Typically that means generating code from that metadata and adding it to the compilation via - // the received context object. - // - // As with everything else , the delegate will be invoked once for each item that passed - // all of the filters above, so we get to write that method from the perspective of a single - // enum type declaration. - - context.RegisterSourceOutput (enumGenerationInfos, GenerateSourceFromGenerationInfo); - } - - private static EnumExtensionMethodsGenerationInfo? GatherMetadataForCodeGeneration ( - GeneratorAttributeSyntaxContext context, - CancellationToken cancellationToken - ) - { - var cts = CancellationTokenSource.CreateLinkedTokenSource (cancellationToken); - cancellationToken.ThrowIfCancellationRequested (); - - // If it's not an enum symbol, we don't care. - // EnumUnderlyingType is null for non-enums, so this validates it's an enum declaration. - if (context.TargetSymbol is not INamedTypeSymbol { EnumUnderlyingType: { } } namedSymbol) - { - return null; - } - - INamespaceSymbol? enumNamespaceSymbol = namedSymbol.ContainingNamespace; - - if (enumNamespaceSymbol is null or { IsGlobalNamespace: true }) - { - // Explicitly choosing not to support enums in the global namespace. - // The corresponding analyzer will report this. - return null; - } - - string enumName = namedSymbol.Name; - - string enumNamespace = enumNamespaceSymbol.ToDisplayString (_fullyQualifiedSymbolDisplayFormatWithoutGlobal); - - TypeCode enumTypeCode = namedSymbol.EnumUnderlyingType.Name switch - { - "UInt32" => TypeCode.UInt32, - "Int32" => TypeCode.Int32, - _ => TypeCode.Empty - }; - - EnumExtensionMethodsGenerationInfo info = new ( - enumNamespace, - enumName, - enumTypeCode - ); - - if (!info.TryConfigure (namedSymbol, cts.Token)) - { - cts.Cancel (); - cts.Token.ThrowIfCancellationRequested (); - } - - return info; - } - - - private static void GenerateAttributeSources (IncrementalGeneratorPostInitializationContext postInitializationContext) - { - postInitializationContext - .AddSource ( - $"{nameof (IExtensionsForEnumTypeAttributes)}.g.cs", - SourceText.From ( - $$""" - // ReSharper disable All - {{Strings.Templates.AutoGeneratedCommentBlock}} - using System; - - namespace {{Strings.AnalyzersAttributesNamespace}}; - - /// - /// Interface to simplify general enumeration of constructed generic types for - /// - /// - {{Strings.Templates.AttributesForGeneratedInterfaces}} - public interface IExtensionsForEnumTypeAttributes - { - System.Type EnumType { get; } - } - - """, - Encoding.UTF8)); - - postInitializationContext - .AddSource ( - $"{nameof (AssemblyExtendedEnumTypeAttribute)}.g.cs", - SourceText.From ( - $$""" - // ReSharper disable All - #nullable enable - {{Strings.Templates.AutoGeneratedCommentBlock}} - - namespace {{Strings.AnalyzersAttributesNamespace}}; - - /// Assembly attribute declaring a known pairing of an type to an extension class. - /// This attribute should only be written by internal source generators for Terminal.Gui. No other usage of any kind is supported. - {{Strings.Templates.AttributesForGeneratedTypes}} - [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple = true)] - public sealed class {{nameof(AssemblyExtendedEnumTypeAttribute)}} : System.Attribute - { - /// Creates a new instance of from the provided parameters. - /// The of an decorated with a . - /// The of the decorated with an referring to the same type as . - public AssemblyExtendedEnumTypeAttribute (System.Type enumType, System.Type extensionClass) - { - EnumType = enumType; - ExtensionClass = extensionClass; - } - /// An type that has been extended by Terminal.Gui source generators. - public System.Type EnumType { get; init; } - /// A class containing extension methods for . - public System.Type ExtensionClass { get; init; } - /// - public override string ToString () => $"{EnumType.Name},{ExtensionClass.Name}"; - } - - """, - Encoding.UTF8)); - - postInitializationContext - .AddSource ( - $"{GeneratorAttributeFullyQualifiedName}.g.cs", - SourceText.From ( - $$""" - {{Strings.Templates.StandardHeader}} - - namespace {{Strings.AnalyzersAttributesNamespace}}; - - /// - /// Used to enable source generation of a common set of extension methods for enum types. - /// - {{Strings.Templates.AttributesForGeneratedTypes}} - [{{Strings.DotnetNames.Types.AttributeUsageAttribute}} ({{Strings.DotnetNames.Types.AttributeTargets}}.Enum)] - public sealed class {{GeneratorAttributeName}} : {{Strings.DotnetNames.Types.Attribute}} - { - /// - /// The name of the generated static class. - /// - /// - /// If unspecified, null, empty, or only whitespace, defaults to the name of the enum plus "Extensions".
- /// No other validation is performed, so illegal values will simply result in compiler errors. - /// - /// Explicitly specifying a default value is unnecessary and will result in unnecessary processing. - /// - ///
- public string? ClassName { get; set; } - - /// - /// The namespace in which to place the generated static class containing the extension methods. - /// - /// - /// If unspecified, null, empty, or only whitespace, defaults to the namespace of the enum.
- /// No other validation is performed, so illegal values will simply result in compiler errors. - /// - /// Explicitly specifying a default value is unnecessary and will result in unnecessary processing. - /// - ///
- public string? ClassNamespace { get; set; } - - /// - /// Whether to generate a fast, zero-allocation, non-boxing, and reflection-free alternative to the built-in - /// method. - /// - /// - /// - /// Default: false - /// - /// - /// If the enum is not decorated with , this option has no effect. - /// - /// - /// If multiple members have the same value, the first member with that value will be used and subsequent members - /// with the same value will be skipped. - /// - /// - /// Overloads taking the enum type itself as well as the underlying type of the enum will be generated, enabling - /// avoidance of implicit or explicit cast overhead. - /// - /// - /// Explicitly specifying a default value is unnecessary and will result in unnecessary processing. - /// - /// - public bool FastHasFlags { get; set; } - - /// - /// Whether to generate a fast, zero-allocation, and reflection-free alternative to the built-in - /// method, - /// using a switch expression as a hard-coded reverse mapping of numeric values to explicitly-named members. - /// - /// - /// - /// Default: true - /// - /// - /// If multiple members have the same value, the first member with that value will be used and subsequent members - /// with the same value will be skipped. - /// - /// - /// As with the source generator only considers explicitly-named members.
- /// Generation of values which represent valid bitwise combinations of members of enums decorated with - /// is not affected by this property. - ///
- ///
- public bool FastIsDefined { get; init; } = true; - - /// - /// Gets a value indicating if this instance - /// contains default values only. See remarks of this method or documentation on properties of this type for details. - /// - /// - /// A value indicating if all property values are default for this - /// instance. - /// - /// - /// Default values that will result in a return value are:
- /// && ! && - /// && - /// - ///
- public override bool IsDefaultAttribute () - { - return FastIsDefined - && !FastHasFlags - && ClassName is null - && ClassNamespace is null; - } - } - - """, - Encoding.UTF8)); - - postInitializationContext - .AddSource ( - $"{ExtensionsForEnumTypeAttributeFullyQualifiedName}.g.cs", - SourceText.From ( - $$""" - // ReSharper disable RedundantNameQualifier - // ReSharper disable RedundantNullableDirective - // ReSharper disable UnusedType.Global - {{Strings.Templates.AutoGeneratedCommentBlock}} - #nullable enable - - namespace {{Strings.AnalyzersAttributesNamespace}}; - - /// - /// Attribute written by the source generator for enum extension classes, for easier analysis and reflection. - /// - /// - /// Properties are just convenient shortcuts to properties of . - /// - {{Strings.Templates.AttributesForGeneratedTypes}} - [System.AttributeUsageAttribute (System.AttributeTargets.Class | System.AttributeTargets.Interface)] - public sealed class {{ExtensionsForEnumTypeAttributeName}}: System.Attribute, IExtensionsForEnumTypeAttributes where TEnum : struct, Enum - { - /// - /// The namespace-qualified name of . - /// - public string EnumFullName => EnumType.FullName!; - - /// - /// The unqualified name of . - /// - public string EnumName => EnumType.Name; - - /// - /// The namespace containing . - /// - public string EnumNamespace => EnumType.Namespace!; - - /// - /// The given by (). - /// - public Type EnumType => typeof (TEnum); - } - - """, - Encoding.UTF8)); - } - - [SuppressMessage ("Roslynator", "RCS1267", Justification = "Intentionally used so that Spans are used.")] - private static void GenerateDummyNamespaces (IncrementalGeneratorPostInitializationContext postInitializeContext) - { - postInitializeContext.AddSource ( - string.Concat (Strings.InternalAnalyzersNamespace, "Namespaces.g.cs"), - SourceText.From (Strings.Templates.DummyNamespaceDeclarations, Encoding.UTF8)); - } - - private static void GenerateSourceFromGenerationInfo (SourceProductionContext context, EnumExtensionMethodsGenerationInfo? enumInfo) - { - // Just in case we still made it this far with a null... - if (enumInfo is not { }) - { - return; - } - - CodeWriter writer = new (enumInfo); - - context.AddSource ($"{enumInfo.FullyQualifiedClassName}.g.cs", writer.GenerateSourceText ()); - } - - /// - /// Returns true if is an EnumDeclarationSyntax - /// whose parent is a NamespaceDeclarationSyntax, FileScopedNamespaceDeclarationSyntax, or a - /// (Class|Struct)DeclarationSyntax.
- /// Additional filtering is performed in later stages. - ///
- private static bool IsPotentiallyInterestingDeclaration (SyntaxNode syntaxNode, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested (); - - return syntaxNode is - { - RawKind: 8858, //(int)SyntaxKind.EnumDeclaration, - Parent.RawKind: 8845 //(int)SyntaxKind.FileScopedNamespaceDeclaration - or 8842 //(int)SyntaxKind.NamespaceDeclaration - or 8855 //(int)SyntaxKind.ClassDeclaration - or 8856 //(int)SyntaxKind.StructDeclaration - or 9068 //(int)SyntaxKind.RecordStructDeclaration - or 9063 //(int)SyntaxKind.RecordDeclaration - }; - } -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/GlobalSuppressions.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/GlobalSuppressions.cs deleted file mode 100644 index ce2fa970b..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/GlobalSuppressions.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage ("Naming", "CA1708:Names should differ by more than case", Scope = "module", Justification = "That's coming from an external generator.")] diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/IGeneratedTypeMetadata.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/IGeneratedTypeMetadata.cs deleted file mode 100644 index c72a8cc44..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/IGeneratedTypeMetadata.cs +++ /dev/null @@ -1,38 +0,0 @@ -using JetBrains.Annotations; -using Microsoft.CodeAnalysis; - -namespace Terminal.Gui.Analyzers.Internal; - -/// -/// Interface for all generators to use for their metadata classes. -/// -/// The type implementing this interface. -internal interface IGeneratedTypeMetadata where TSelf : IGeneratedTypeMetadata -{ - [UsedImplicitly] - string GeneratedTypeNamespace { get; } - [UsedImplicitly] - string? GeneratedTypeName { get; } - [UsedImplicitly] - string GeneratedTypeFullName { get; } - [UsedImplicitly] - string TargetTypeNamespace { get; } - [UsedImplicitly] - string TargetTypeName { get; } - string TargetTypeFullName { get; } - [UsedImplicitly] - Accessibility Accessibility { get; } - TypeKind TypeKind { get; } - bool IsRecord { get; } - bool IsClass { get; } - bool IsStruct { get; } - [UsedImplicitly] - bool IsPartial { get; } - bool IsByRefLike { get; } - bool IsSealed { get; } - bool IsAbstract { get; } - bool IsEnum { get; } - bool IsStatic { get; } - [UsedImplicitly] - bool IncludeInterface { get; } -} \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/IStandardCSharpCodeGenerator.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/IStandardCSharpCodeGenerator.cs deleted file mode 100644 index a0e3d584d..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/IStandardCSharpCodeGenerator.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Text; -using JetBrains.Annotations; -using Microsoft.CodeAnalysis.Text; - -namespace Terminal.Gui.Analyzers.Internal; - -internal interface IStandardCSharpCodeGenerator where T : IGeneratedTypeMetadata -{ - /// - /// Generates and returns the full source text corresponding to , - /// in the requested or if not provided. - /// - /// - /// The of the generated source text or if not - /// provided. - /// - /// - [UsedImplicitly] - [SkipLocalsInit] - ref readonly SourceText GenerateSourceText (Encoding? encoding = null); - - /// - /// A type implementing which - /// will be used for source generation. - /// - [UsedImplicitly] - T Metadata { get; set; } -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/IndentedTextWriterExtensions.cs b/Analyzers/Terminal.Gui.Analyzers.Internal/IndentedTextWriterExtensions.cs deleted file mode 100644 index 90105d582..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/IndentedTextWriterExtensions.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.CodeDom.Compiler; - -namespace Terminal.Gui.Analyzers.Internal; - -/// -/// Just a simple set of extension methods to increment and decrement the indentation -/// level of an via push and pop terms, and to avoid having -/// explicit values all over the place. -/// -public static class IndentedTextWriterExtensions -{ - /// - /// Decrements by 1, but only if it is greater than 0. - /// - /// - /// The resulting indentation level of the . - /// - [MethodImpl (MethodImplOptions.AggressiveInlining)] - public static int Pop (this IndentedTextWriter w, string endScopeDelimiter = "}") - { - if (w.Indent > 0) - { - w.Indent--; - w.WriteLine (endScopeDelimiter); - } - return w.Indent; - } - - /// - /// Decrements by 1 and then writes a closing curly brace. - /// - [MethodImpl (MethodImplOptions.AggressiveInlining)] - public static void PopCurly (this IndentedTextWriter w, bool withSemicolon = false) - { - w.Indent--; - - if (withSemicolon) - { - w.WriteLine ("};"); - } - else - { - w.WriteLine ('}'); - } - } - - /// - /// Increments by 1, with optional parameters to customize the scope push. - /// - /// An instance of an . - /// - /// The first line to be written before indenting and before the optional line or - /// null if not needed. - /// - /// - /// An opening delimiter to write. Written before the indentation and after (if provided). Default is an opening curly brace. - /// - /// Calling with no parameters will write an opening curly brace and a line break at the current indentation and then increment. - [MethodImpl (MethodImplOptions.AggressiveInlining)] - public static void Push (this IndentedTextWriter w, string? declaration = null, char scopeDelimiter = '{') - { - if (declaration is { Length: > 0 }) - { - w.WriteLine (declaration); - } - - w.WriteLine (scopeDelimiter); - - w.Indent++; - } -} diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/Properties/launchSettings.json b/Analyzers/Terminal.Gui.Analyzers.Internal/Properties/launchSettings.json deleted file mode 100644 index 639272733..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "InternalAnalyzers Debug": { - "commandName": "DebugRoslynComponent", - "targetProject": "..\\Terminal.Gui.Analyzers.Internal.Debugging\\Terminal.Gui.Analyzers.Internal.Debugging.csproj" - } - } -} \ No newline at end of file diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/Terminal.Gui.Analyzers.Internal.csproj b/Analyzers/Terminal.Gui.Analyzers.Internal/Terminal.Gui.Analyzers.Internal.csproj deleted file mode 100644 index 06ce0794b..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/Terminal.Gui.Analyzers.Internal.csproj +++ /dev/null @@ -1,67 +0,0 @@ - - - - netstandard2.0 - - - - Library - 12 - Terminal.Gui.Analyzers.Internal - disable - true - true - True - true - true - true - True - true - true - - - - - - - - - - - - - $(NoWarn);nullable;CA1067 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Analyzers/Terminal.Gui.Analyzers.Internal/Terminal.Gui.Analyzers.Internal.csproj.DotSettings b/Analyzers/Terminal.Gui.Analyzers.Internal/Terminal.Gui.Analyzers.Internal.csproj.DotSettings deleted file mode 100644 index 6c2c0e27d..000000000 --- a/Analyzers/Terminal.Gui.Analyzers.Internal/Terminal.Gui.Analyzers.Internal.csproj.DotSettings +++ /dev/null @@ -1,4 +0,0 @@ - - CSharp120 - InternalsOnly - False \ No newline at end of file From 6b72b3f56bdd738bb36ce3808c95367f4be28c06 Mon Sep 17 00:00:00 2001 From: Brandon Thetford Date: Wed, 10 Jul 2024 14:22:18 -0700 Subject: [PATCH 47/48] Remove PowerShell module for analyzers --- .../Terminal.Gui.PowerShell.Analyzers.psd1 | 117 ------------------ .../Terminal.Gui.PowerShell.Analyzers.psm1 | 96 -------------- Scripts/Terminal.Gui.PowerShell.Core.psm1 | 6 - Scripts/Terminal.Gui.PowerShell.psd1 | 2 +- 4 files changed, 1 insertion(+), 220 deletions(-) delete mode 100644 Scripts/Terminal.Gui.PowerShell.Analyzers.psd1 delete mode 100644 Scripts/Terminal.Gui.PowerShell.Analyzers.psm1 diff --git a/Scripts/Terminal.Gui.PowerShell.Analyzers.psd1 b/Scripts/Terminal.Gui.PowerShell.Analyzers.psd1 deleted file mode 100644 index c94a3e242..000000000 --- a/Scripts/Terminal.Gui.PowerShell.Analyzers.psd1 +++ /dev/null @@ -1,117 +0,0 @@ -# -# Module manifest for module 'Terminal.Gui.Powershell.Analyzers' -# -# Generated by: Brandon Thetford (GitHub @dodexahedron) -# -# Generated on: 4/24/2024 -# - -@{ - -# Script module or binary module file associated with this manifest. -RootModule = '' - -# Version number of this module. -ModuleVersion = '1.0.0' - -# Supported PSEditions -CompatiblePSEditions = @('Core') - -# ID used to uniquely identify this module -GUID = '3e85001d-6539-4cf1-b71c-ec9e983f7fc8' - -# Author of this module -Author = 'Brandon Thetford (GitHub @dodexahedron)' - -# Company or vendor of this module -CompanyName = 'The Terminal.Gui Project' - -# Copyright statement for this module -Copyright = '(c) Brandon Thetford (GitHub @dodexahedron). Provided to the Terminal.Gui project and you under the terms of the MIT License.' - -# Description of the functionality provided by this module -Description = 'Operations involving Terminal.Gui analyzer projects, fur use during development of Terminal.Gui' - -# Minimum version of the PowerShell engine required by this module -PowerShellVersion = '7.4.0' - -# Name of the PowerShell host required by this module -PowerShellHostName = 'ConsoleHost' - -# Minimum version of the PowerShell host required by this module -# PowerShellHostVersion = '' - -# Processor architecture (None, X86, Amd64) required by this module -ProcessorArchitecture = '' - -# Modules that must be imported into the global environment prior to importing this module -RequiredModules = @('Microsoft.PowerShell.Management','Microsoft.PowerShell.Utility','./Terminal.Gui.PowerShell.Core.psd1') - -# Assemblies that must be loaded prior to importing this module -# RequiredAssemblies = @() - -# Script files (.ps1) that are run in the caller's environment prior to importing this module. -# ScriptsToProcess = @() - -# Type files (.ps1xml) to be loaded when importing this module -# TypesToProcess = @() - -# Format files (.ps1xml) to be loaded when importing this module -# FormatsToProcess = @() - -# Modules to import as nested modules. -NestedModules = @('./Terminal.Gui.PowerShell.Analyzers.psm1') - -# Functions to export from this module. -FunctionsToExport = @('Build-Analyzers') - -# Cmdlets to export from this module. -CmdletsToExport = @() - -# Variables to export from this module -VariablesToExport = @() - -# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. -AliasesToExport = @() - -# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. -PrivateData = @{ - - PSData = @{ - - # Tags applied to this module. These help with module discovery in online galleries. - # Tags = @() - - # A URL to the license for this module. - LicenseUri = 'https://github.com/gui-cs/Terminal.Gui/Scripts/COPYRIGHT' - - # A URL to the main website for this project. - ProjectUri = 'https://github.com/gui-cs/Terminal.Gui' - - # A URL to an icon representing this module. - # IconUri = '' - - # ReleaseNotes of this module - # ReleaseNotes = '' - - # Prerelease string of this module - # Prerelease = '' - - # Flag to indicate whether the module requires explicit user acceptance for install/update/save - # RequireLicenseAcceptance = $false - - # External dependent modules of this module - # ExternalModuleDependencies = @() - - } # End of PSData hashtable - -} # End of PrivateData hashtable - -# HelpInfo URI of this module -# HelpInfoURI = '' - -# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. -# DefaultCommandPrefix = '' - -} - diff --git a/Scripts/Terminal.Gui.PowerShell.Analyzers.psm1 b/Scripts/Terminal.Gui.PowerShell.Analyzers.psm1 deleted file mode 100644 index 8e3a8dc58..000000000 --- a/Scripts/Terminal.Gui.PowerShell.Analyzers.psm1 +++ /dev/null @@ -1,96 +0,0 @@ -<# - .SYNOPSIS - Builds all analyzer projects in Debug and Release configurations. - .DESCRIPTION - Uses dotnet build to build all analyzer projects, with optional behavior changes via switch parameters. - .PARAMETER AutoClose - Automatically close running Visual Studio processes which have the Terminal.sln solution loaded, before taking any other actions. - .PARAMETER AutoLaunch - Automatically start a new Visual Studio process and load the solution after completion. - .PARAMETER Force - Carry out operations unconditionally and do not prompt for confirmation. - .PARAMETER NoClean - Do not delete the bin and obj folders before building the analyzers. Usually best not to use this, but can speed up the builds slightly. - .PARAMETER Quiet - Write less text output to the terminal. - .INPUTS - None - .OUTPUTS - None -#> -Function Build-Analyzers { - [CmdletBinding()] - param( - [Parameter(Mandatory=$false, HelpMessage="Automatically close running Visual Studio processes which have the Terminal.sln solution loaded, before taking any other actions.")] - [switch]$AutoClose, - [Parameter(Mandatory=$false, HelpMessage="Automatically start a new Visual Studio process and load the solution after completion.")] - [switch]$AutoLaunch, - [Parameter(Mandatory=$false, HelpMessage="Carry out operations unconditionally and do not prompt for confirmation.")] - [switch]$Force, - [Parameter(Mandatory=$false, HelpMessage="Do not delete the bin and obj folders before building the analyzers.")] - [switch]$NoClean, - [Parameter(Mandatory=$false, HelpMessage="Write less text output to the terminal.")] - [switch]$Quiet - ) - - if($AutoClose) { - if(!$Quiet) { - Write-Host Closing Visual Studio processes - } - Close-Solution - } - - if($Force){ - $response = 'Y' - } - elseif(!$Force && $NoClean){ - $response = ($r = Read-Host "Pre-build Terminal.Gui.InternalAnalyzers without removing old build artifacts? [Y/n]") ? $r : 'Y' - } - else{ - $response = ($r = Read-Host "Delete bin and obj folders for Terminal.Gui and Terminal.Gui.InternalAnalyzers and pre-build Terminal.Gui.InternalAnalyzers? [Y/n]") ? $r : 'Y' - } - - if (($response -ne 'Y')) { - Write-Host Took no action - return - } - - Push-Location $InternalAnalyzersProjectDirectory - - if(!$NoClean) { - if(!$Quiet) { - Write-Host Deleting bin and obj folders for Terminal.Gui - } - Remove-Item -Recurse -Force $TerminalGuiProjectDirectory/bin -ErrorAction SilentlyContinue - Remove-Item -Recurse -Force $TerminalGuiProjectDirectory/obj -ErrorAction SilentlyContinue - - if(!$Quiet) { - Write-Host Deleting bin and obj folders for Terminal.Gui.InternalAnalyzers - } - Remove-Item -Recurse -Force $InternalAnalyzersProjectDirectory/bin -ErrorAction SilentlyContinue - Remove-Item -Recurse -Force $InternalAnalyzersProjectDirectory/obj -ErrorAction SilentlyContinue - } - - if(!$Quiet) { - Write-Host Building analyzers in Debug configuration - } - dotnet build $InternalAnalyzersProjectFilePath --no-incremental --nologo --force --configuration Debug - - if(!$Quiet) { - Write-Host Building analyzers in Release configuration - } - dotnet build $InternalAnalyzersProjectFilePath --no-incremental --nologo --force --configuration Release - - Pop-Location - - if(!$AutoLaunch) { - Write-Host -ForegroundColor Green Finished. Restart Visual Studio for changes to take effect. - } else { - if(!$Quiet) { - Write-Host -ForegroundColor Green Finished. Re-loading Terminal.sln. - } - Open-Solution - } - - return -} diff --git a/Scripts/Terminal.Gui.PowerShell.Core.psm1 b/Scripts/Terminal.Gui.PowerShell.Core.psm1 index f4df84049..6a53e10c5 100644 --- a/Scripts/Terminal.Gui.PowerShell.Core.psm1 +++ b/Scripts/Terminal.Gui.PowerShell.Core.psm1 @@ -69,9 +69,6 @@ Function Set-PowerShellEnvironment { New-Variable -Name SolutionFilePath -Value (Join-Path -Resolve $RepositoryRootDirectory "Terminal.sln") -Option ReadOnly -Scope Global -Visibility Public New-Variable -Name TerminalGuiProjectDirectory -Value (Join-Path -Resolve $RepositoryRootDirectory "Terminal.Gui") -Option ReadOnly -Scope Global -Visibility Public New-Variable -Name TerminalGuiProjectFilePath -Value (Join-Path -Resolve $TerminalGuiProjectDirectory "Terminal.Gui.csproj") -Option ReadOnly -Scope Global -Visibility Public - New-Variable -Name AnalyzersDirectory -Value (Join-Path -Resolve $RepositoryRootDirectory "Analyzers") -Option ReadOnly -Scope Global -Visibility Public - New-Variable -Name InternalAnalyzersProjectDirectory -Value (Join-Path -Resolve $AnalyzersDirectory "Terminal.Gui.Analyzers.Internal") -Option ReadOnly -Scope Global -Visibility Public - New-Variable -Name InternalAnalyzersProjectFilePath -Value (Join-Path -Resolve $InternalAnalyzersProjectDirectory "Terminal.Gui.Analyzers.Internal.csproj") -Option ReadOnly -Scope Global -Visibility Public # Save existing PSModulePath for optional reset later. # If it is already saved, do not overwrite, but continue anyway. @@ -137,9 +134,6 @@ Function Reset-PowerShellEnvironment { Remove-Variable -Name TerminalGuiProjectDirectory -Scope Global -Force -ErrorAction SilentlyContinue Remove-Variable -Name TerminalGuiProjectFilePath -Scope Global -Force -ErrorAction SilentlyContinue Remove-Variable -Name ScriptsDirectory -Scope Global -Force -ErrorAction SilentlyContinue - Remove-Variable -Name AnalyzersDirectory -Scope Global -Force -ErrorAction SilentlyContinue - Remove-Variable -Name InternalAnalyzersProjectDirectory -Scope Global -Force -ErrorAction SilentlyContinue - Remove-Variable -Name InternalAnalyzersProjectFilePath -Scope Global -Force -ErrorAction SilentlyContinue } # This ensures the environment is reset when unloading the module. diff --git a/Scripts/Terminal.Gui.PowerShell.psd1 b/Scripts/Terminal.Gui.PowerShell.psd1 index ca182f375..3d190a4ac 100644 --- a/Scripts/Terminal.Gui.PowerShell.psd1 +++ b/Scripts/Terminal.Gui.PowerShell.psd1 @@ -81,7 +81,7 @@ RequiredModules = @( # Modules to import as nested modules of this module. # This module is just a shortcut that loads all of our modules. -NestedModules = @('./Terminal.Gui.PowerShell.Core.psd1', './Terminal.Gui.PowerShell.Analyzers.psd1', './Terminal.Gui.PowerShell.Git.psd1', './Terminal.Gui.PowerShell.Build.psd1') +NestedModules = @('./Terminal.Gui.PowerShell.Core.psd1', './Terminal.Gui.PowerShell.Git.psd1', './Terminal.Gui.PowerShell.Build.psd1') # Functions to export from this module. # Not filtered, so exports all functions exported by all nested modules. From 93c2425bed62cc9e9a03382492c920043db2e640 Mon Sep 17 00:00:00 2001 From: Brandon Thetford Date: Wed, 10 Jul 2024 14:22:29 -0700 Subject: [PATCH 48/48] Remove analyzers from CI build --- .github/workflows/publish.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index faf867527..bc4a69196 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -43,7 +43,6 @@ jobs: - name: Build Release run: | dotnet-gitversion /updateprojectfiles - dotnet build ./Analyzers/Terminal.Gui.Analyzers.Internal --no-incremental --nologo --force --configuration Release dotnet build --no-incremental --nologo --force --configuration Release - name: Pack