Browse Source

LineCanvas support for gradient fill

tznind 1 year ago
parent
commit
3171df8e02

+ 29 - 0
Terminal.Gui/Drawing/FillPair.cs

@@ -0,0 +1,29 @@
+
+using Terminal.Gui.TextEffects;
+
+namespace Terminal.Gui;
+
+
+/// <summary>
+/// Describes a pair of <see cref="IFill"/> which cooperate in creating
+/// <see cref="Attribute"/>.  One gives foreground color while other gives background.
+/// </summary>
+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)
+            );
+    }
+}

+ 15 - 0
Terminal.Gui/Drawing/IFill.cs

@@ -0,0 +1,15 @@
+
+namespace Terminal.Gui;
+
+/// <summary>
+/// Describes an area fill (e.g. solid color or gradient).
+/// </summary>
+public interface IFill
+{
+    /// <summary>
+    /// Returns the color that should be used at the given point
+    /// </summary>
+    /// <param name="point"></param>
+    /// <returns></returns>
+    Color GetColor (Point point);
+}

+ 4 - 1
Terminal.Gui/Drawing/LineCanvas.cs

@@ -4,6 +4,7 @@ namespace Terminal.Gui;
 /// <summary>Facilitates box drawing and line intersection detection and rendering.  Does not support diagonal lines.</summary>
 /// <summary>Facilitates box drawing and line intersection detection and rendering.  Does not support diagonal lines.</summary>
 public class LineCanvas : IDisposable
 public class LineCanvas : IDisposable
 {
 {
+    public FillPair? Fill { get; set; }
     private readonly List<StraightLine> _lines = [];
     private readonly List<StraightLine> _lines = [];
 
 
     private readonly Dictionary<IntersectionRuneType, IntersectionRuneResolver> _runeResolvers = new ()
     private readonly Dictionary<IntersectionRuneType, IntersectionRuneResolver> _runeResolvers = new ()
@@ -324,7 +325,9 @@ public class LineCanvas : IDisposable
     /// <returns></returns>
     /// <returns></returns>
     private bool Exactly (HashSet<IntersectionType> intersects, params IntersectionType [] types) { return intersects.SetEquals (types); }
     private bool Exactly (HashSet<IntersectionType> 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)
     private Cell? GetCellForIntersects (ConsoleDriver driver, IntersectionDefinition? [] intersects)
     {
     {

+ 5 - 12
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 class GradientDirectionParser
 {
 {
-    public static GradientDirection Parse (string arg)
+    public static Gradient.Direction Parse (string arg)
     {
     {
         return arg.ToLower () switch
         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."),
             _ => throw new ArgumentException ($"invalid gradient direction: '{arg}' is not a valid gradient direction. Choices are diagonal, horizontal, vertical, or radial."),
         };
         };
     }
     }

+ 2 - 2
Terminal.Gui/TextEffects/BaseEffect.cs

@@ -3,7 +3,7 @@
 public abstract class BaseEffectIterator<T>  where T : EffectConfig, new()
 public abstract class BaseEffectIterator<T>  where T : EffectConfig, new()
 {
 {
     protected T Config { get; set; }
     protected T Config { get; set; }
-    protected Terminal Terminal { get; set; }
+    protected TerminalA Terminal { get; set; }
     protected List<EffectCharacter> ActiveCharacters { get; set; } = new List<EffectCharacter> ();
     protected List<EffectCharacter> ActiveCharacters { get; set; } = new List<EffectCharacter> ();
 
 
     protected BaseEffect<T> Effect { get; }
     protected BaseEffect<T> Effect { get; }
@@ -14,7 +14,7 @@ public abstract class BaseEffectIterator<T>  where T : EffectConfig, new()
     {
     {
         Effect = effect;
         Effect = effect;
         Config = effect.EffectConfig;
         Config = effect.EffectConfig;
-        Terminal = new Terminal (effect.InputData, effect.TerminalConfig);
+        Terminal = new TerminalA (effect.InputData, effect.TerminalConfig);
 
 
     }
     }
 
 

+ 31 - 0
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;
+
+/// <summary>
+/// Implementation of <see cref="IFill"/> that uses a color gradient (including
+/// radial, diagonal etc).
+/// </summary>
+public class GradientFill : IFill
+{
+    private Dictionary<Point, Terminal.Gui.Color> _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];
+    }
+}

+ 19 - 0
Terminal.Gui/TextEffects/New/SolidFill.cs

@@ -0,0 +1,19 @@
+namespace Terminal.Gui.TextEffects;
+
+
+/// <summary>
+/// <see cref="IFill"/> implementation that uses a solid color for all points
+/// </summary>
+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;
+    }
+}

+ 2 - 2
Terminal.Gui/TextEffects/Terminal.cs

@@ -57,13 +57,13 @@ public class Canvas
     }
     }
 }
 }
 
 
-public class Terminal
+public class TerminalA
 {
 {
     public TerminalConfig Config { get; }
     public TerminalConfig Config { get; }
     public Canvas Canvas { get; private set; }
     public Canvas Canvas { get; private set; }
     private Dictionary<Coord, EffectCharacter> CharacterByInputCoord = new Dictionary<Coord, EffectCharacter> ();
     private Dictionary<Coord, EffectCharacter> CharacterByInputCoord = new Dictionary<Coord, EffectCharacter> ();
 
 
-    public Terminal (string input, TerminalConfig config = null)
+    public TerminalA (string input, TerminalConfig config = null)
     {
     {
         Config = config ?? new TerminalConfig ();
         Config = config ?? new TerminalConfig ();
         var dimensions = GetTerminalDimensions ();
         var dimensions = GetTerminalDimensions ();

+ 57 - 1
UICatalog/Scenarios/TextEffectsScenario.cs

@@ -15,6 +15,8 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Colors")]
 [ScenarioCategory ("Colors")]
 public class TextEffectsScenario : Scenario
 public class TextEffectsScenario : Scenario
 {
 {
+    private TabView tabView;
+
     public override void Main ()
     public override void Main ()
     {
     {
         Application.Init ();
         Application.Init ();
@@ -24,8 +26,30 @@ public class TextEffectsScenario : Scenario
             Height = Dim.Fill (),
             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.
         // Creates a window that occupies the entire terminal with a title.
-        var tabView = new TabView ()
+        tabView = new TabView ()
         {
         {
             Width = Dim.Fill (),
             Width = Dim.Fill (),
             Height = Dim.Fill (),
             Height = Dim.Fill (),
@@ -61,6 +85,38 @@ public class TextEffectsScenario : Scenario
         Application.Shutdown ();
         Application.Shutdown ();
         this.Dispose ();
         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<Color> stops, out List<int> steps)
+    {
+        // Define the colors of the gradient stops with more appealing colors
+        stops = new List<Color>
+        {
+            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<int> { 15, 15, 15, 15 }; // 15 steps between each color
+    }
 }
 }