From cc88cb44e1f15d0467540b80bdebd2464bffa1bd Mon Sep 17 00:00:00 2001 From: Miguel de Icaza Date: Fri, 15 Dec 2017 22:29:40 -0500 Subject: [PATCH] Add support for clipping --- Application.cs | 134 +++++++++++++++++++++++++++++++------------------ demo.cs | 2 +- driver.cs | 74 ++++++++++++++++++++------- 3 files changed, 140 insertions(+), 70 deletions(-) diff --git a/Application.cs b/Application.cs index 4884568f9..dd8bf75b9 100644 --- a/Application.cs +++ b/Application.cs @@ -16,19 +16,19 @@ namespace Terminal { public bool HasFocus { get; internal set; } // Key handling - public virtual void KeyDown (Event.Key kb) {} + public virtual void KeyDown (Event.Key kb) { } // Mouse events - public virtual void MouseEvent (Event.Mouse me) {} + public virtual void MouseEvent (Event.Mouse me) { } } public class View : Responder { View container = null; View focused = null; public static ConsoleDriver Driver = Application.Driver; - public static IList empty = new List(0).AsReadOnly (); + public static IList empty = new List (0).AsReadOnly (); List subviews; - public IList Subviews => subviews == null ? empty : subviews.AsReadOnly (); + public IList Subviews => subviews == null ? empty : subviews.AsReadOnly (); internal bool NeedDisplay { get; private set; } = true; // The frame for the object @@ -38,7 +38,7 @@ namespace Terminal { Point offset; // The frame for this view - public Rect Frame { + public Rect Frame { get => frame; set { frame = value; @@ -46,6 +46,13 @@ namespace Terminal { } } + public Rect Bounds { + get => new Rect (Point.Empty, Frame.Size); + set { + Frame = new Rect (frame.Location, value.Size); + } + } + public View (Rect frame) { this.Frame = frame; @@ -92,7 +99,7 @@ namespace Terminal { var view = subviews [0]; Remove (view); subviews.RemoveAt (0); - } + } } /// @@ -126,7 +133,7 @@ namespace Terminal { var w = Frame.Width; for (int line = 0; line < h; line++) { Move (0, line); - for (int col = 0; col < w; col++) + for (int col = 0; col < w; col++) Driver.AddCh (' '); } } @@ -144,7 +151,7 @@ namespace Terminal { rrow = row + frame.X; rcol = col + frame.Y; var ccontainer = container; - while (ccontainer != null){ + while (ccontainer != null) { rrow += ccontainer.frame.Y; rcol += ccontainer.frame.X; ccontainer = ccontainer.container; @@ -157,7 +164,21 @@ namespace Terminal { } } - + Rect RectToScreen (Rect rect) + { + ViewToScreen (rect.X, rect.Y, out var x, out var y, clipped: false); + return new Rect (x, y, rect.Width, rect.Height); + } + + Rect ScreenClip (Rect rect) + { + var x = rect.X < 0 ? 0 : rect.X; + var y = rect.Y < 0 ? 0 : rect.Y; + var w = rect.X + rect.Width >= Driver.Cols ? Driver.Cols - rect.X : rect.Width; + var h = rect.Y + rect.Height >= Driver.Rows ? Driver.Rows - rect.Y : rect.Height; + + return new Rect (x, y, w, h); + } /// /// Draws a frame in the current view, clipped by the boundary of this view @@ -166,8 +187,11 @@ namespace Terminal { /// If set to true it fill will the contents. public void DrawFrame (Rect rect, bool fill = false) { - ViewToScreen (rect.X, rect.Y, out var x, out var y, clipped: false); - Driver.DrawFrame (new Rect (x, y, rect.Width, rect.Height), fill); + var scrRect = RectToScreen (rect); + var savedClip = Driver.Clip; + Driver.Clip = ScreenClip (RectToScreen (Bounds)); + Driver.DrawFrame (scrRect, fill); + Driver.Clip = savedClip; } /// @@ -203,7 +227,7 @@ namespace Terminal { { if (row < 0 || col < 0) return; - if (row > frame.Height-1 || col > frame.Width-1) + if (row > frame.Height - 1 || col > frame.Width - 1) return; Move (col, row); Driver.AddCh (ch); @@ -249,7 +273,7 @@ namespace Terminal { break; if (c == null) throw new ArgumentException ("the specified view is not part of the hierarchy of this view"); - + if (focused != null) focused.HasFocus = false; focused = view; @@ -273,8 +297,8 @@ namespace Terminal { /// public void FocusFirst () { - foreach (var view in subviews){ - if (view.CanFocus){ + foreach (var view in subviews) { + if (view.CanFocus) { SetFocus (view); return; } @@ -286,11 +310,11 @@ namespace Terminal { /// public void FocusLast () { - for (int i = subviews.Count; i > 0; ){ + for (int i = subviews.Count; i > 0;) { i--; View v = subviews [i]; - if (v.CanFocus){ + if (v.CanFocus) { SetFocus (v); return; } @@ -303,32 +327,32 @@ namespace Terminal { /// true, if previous was focused, false otherwise. public bool FocusPrev () { - if (focused == null){ + if (focused == null) { FocusLast (); return true; } int focused_idx = -1; - for (int i = subviews.Count; i > 0; ){ + for (int i = subviews.Count; i > 0;) { i--; View w = subviews [i]; - if (w.HasFocus){ + if (w.HasFocus) { if (w.FocusPrev ()) - return true; + return true; focused_idx = i; continue; } - if (w.CanFocus && focused_idx != -1){ + if (w.CanFocus && focused_idx != -1) { focused.HasFocus = false; if (w.CanFocus) - w.FocusLast (); - + w.FocusLast (); + SetFocus (w); return true; } } - if (focused != null){ + if (focused != null) { focused.HasFocus = false; focused = null; } @@ -340,35 +364,35 @@ namespace Terminal { /// /// true, if next was focused, false otherwise. public bool FocusNext () - { - if (focused == null){ - FocusFirst (); + { + if (focused == null) { + FocusFirst (); return focused != null; } int n = subviews.Count; int focused_idx = -1; - for (int i = 0; i < n; i++){ - View w = subviews [i]; - - if (w.HasFocus){ - if (w.FocusNext ()) - return true; - focused_idx = i; - continue; - } - if (w.CanFocus && focused_idx != -1){ - focused.HasFocus = false; + for (int i = 0; i < n; i++) { + View w = subviews [i]; - if (w != null && w.CanFocus) - w.FocusFirst (); - - SetFocus (w); + if (w.HasFocus) { + if (w.FocusNext ()) return true; - } - } - if (focused != null){ + focused_idx = i; + continue; + } + if (w.CanFocus && focused_idx != -1) { focused.HasFocus = false; - focused = null; + + if (w != null && w.CanFocus) + w.FocusFirst (); + + SetFocus (w); + return true; + } + } + if (focused != null) { + focused.HasFocus = false; + focused = null; } return false; } @@ -388,10 +412,21 @@ namespace Terminal { { } - public static Toplevel Create () + public static Toplevel Create () { return new Toplevel (new Rect (0, 0, Driver.Cols, Driver.Rows)); } + +#if false + public override void Redraw () + { + base.Redraw (); + for (int i = 0; i < Driver.Cols; i++) { + Driver.Move (0, i); + Driver.AddStr ("Line: " + i); + } + } +#endif } /// @@ -419,13 +454,12 @@ namespace Terminal { void DrawFrame () { - DrawFrame (new Rect (Point.Empty, Frame.Size), true); + DrawFrame (new Rect(0, 0, Frame.Width, Frame.Height), true); } public override void Redraw () { Driver.SetColor (Colors.Base.Normal); - Clear (); DrawFrame (); if (HasFocus) Driver.SetColor (Colors.Dialog.Normal); diff --git a/demo.cs b/demo.cs index cb9d8cee8..5df80176e 100644 --- a/demo.cs +++ b/demo.cs @@ -5,7 +5,7 @@ class Demo { { Application.Init (); var top = Application.Top; - top.Add (new Window (new Rect (10, 10, 20, 10), "Hello")); + top.Add (new Window (new Rect (0, 0, 80, 24), "Hello")); Application.Run (); } } \ No newline at end of file diff --git a/driver.cs b/driver.cs index 6fbab20c9..74ec9fabb 100644 --- a/driver.cs +++ b/driver.cs @@ -42,16 +42,53 @@ namespace Terminal { public abstract void SetColor (Color c); public abstract void DrawFrame (Rect region, bool fill); - // Colors used for widgets + Rect clip; + public Rect Clip { + get => clip; + set => this.clip = value; + } } public class CursesDriver : ConsoleDriver { public override int Cols => Curses.Cols; public override int Rows => Curses.Lines; - public override void Move(int col, int row) => Curses.move (row, col); - public override void AddCh(int ch) => Curses.addch (ch); - public override void AddStr (string str) => Curses.addstr (str); + // Current row, and current col, tracked by Move/AddCh only + int ccol, crow; + bool needMove; + public override void Move (int col, int row) + { + ccol = col; + crow = row; + + if (Clip.Contains (col, row)) { + Curses.move (row, col); + needMove = false; + } else { + Curses.move (Clip.Y, Clip.X); + needMove = true; + } + } + + public override void AddCh (int ch) + { + if (Clip.Contains (ccol, crow)) { + if (needMove) { + Curses.move (crow, ccol); + needMove = false; + } + Curses.addch (ch); + } else + needMove = true; + ccol++; + } + + public override void AddStr (string str) + { + foreach (var c in str) + AddCh ((int) c); + } + public override void Refresh() => Curses.refresh (); public override void End() => Curses.endwin (); public override void RedrawTop() => window.redrawwin (); @@ -76,27 +113,26 @@ namespace Terminal { int height = region.Height; int b; - Curses.move (region.Y, region.X); - Curses.addch (Curses.ACS_ULCORNER); + Move (region.X, region.Y); + AddCh (Curses.ACS_ULCORNER); for (b = 0; b < width - 2; b++) - Curses.addch (Curses.ACS_HLINE); - Curses.addch (Curses.ACS_URCORNER); - + AddCh (Curses.ACS_HLINE); + AddCh (Curses.ACS_URCORNER); for (b = 1; b < height - 1; b++) { - Curses.move (region.Y + b, region.X); - Curses.addch (Curses.ACS_VLINE); + Move (region.X, region.Y + b); + AddCh (Curses.ACS_VLINE); if (fill) { for (int x = 1; x < width - 1; x++) - Curses.addch (' '); + AddCh (' '); } else - Curses.move (region.Y + b, region.X + width - 1); - Curses.addch (Curses.ACS_VLINE); + Move (region.X + width - 1, region.Y + b); + AddCh (Curses.ACS_VLINE); } - Curses.move (region.Y + height - 1, region.X); - Curses.addch (Curses.ACS_LLCORNER); + Move (region.X, region.Y + height - 1); + AddCh (Curses.ACS_LLCORNER); for (b = 0; b < width - 2; b++) - Curses.addch (Curses.ACS_HLINE); - Curses.addch (Curses.ACS_LRCORNER); + AddCh (Curses.ACS_HLINE); + AddCh (Curses.ACS_LRCORNER); } public override void Init() @@ -117,7 +153,7 @@ namespace Terminal { Colors.Dialog = new ColorScheme (); Colors.Menu = new ColorScheme (); Colors.Error = new ColorScheme (); - + Clip = new Rect (0, 0, Cols, Rows); if (Curses.HasColors){ Curses.StartColor (); Curses.UseDefaultColors ();