From 3f2c6b6f97e41ad1ebe65086c8dfe7e61640e5ed Mon Sep 17 00:00:00 2001 From: Miguel de Icaza Date: Fri, 26 Jan 2018 23:01:53 -0500 Subject: [PATCH] ScrollView --- Terminal.Gui/Views/ScrollView.cs | 256 +++++++++++++++++++++++-------- demo.cs | 4 +- 2 files changed, 195 insertions(+), 65 deletions(-) diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs index 059c5574f..98ae6c0f8 100644 --- a/Terminal.Gui/Views/ScrollView.cs +++ b/Terminal.Gui/Views/ScrollView.cs @@ -1,7 +1,167 @@ -using System; +// +// ScrollView.cs: ScrollView and ScrollBarView views. +// +// Authors: +// Miguel de Icaza (miguel@gnome.org) +// +// +// TODO: +// - Mouse handling in scrollbarview +// - focus in scrollview +// - keyboard handling in scrollview to scroll +// - focus handling in scrollview to auto scroll to focused view +// - Raise events +using System; namespace Terminal.Gui { /// - /// + /// ScrollBarViews are views that display a 1-character scrollbar, either horizontal or vertical + /// + /// + /// The scrollbar is drawn to be a representation of the Size, assuming that the + /// scroll position is set at Position. + /// + public class ScrollBarView : View { + bool vertical; + int size, position; + + /// + /// The size that this scrollbar represents + /// + /// The size. + public int Size { + get => size; + set { + size = value; + SetNeedsDisplay (); + } + } + + /// + /// The position to show the scrollbar at. + /// + /// The position. + public int Position { + get => position; + set { + position = value; + SetNeedsDisplay (); + } + } + + /// + /// Initializes a new instance of the class. + /// + /// Frame for the scrollbar. + /// The size that this scrollbar represents. + /// The position within this scrollbar. + /// If set to true this is a vertical scrollbar, otherwize, the scrollbar is horizontal. + public ScrollBarView (Rect rect, int size, int position, bool isVertical) : base (rect) + { + vertical = isVertical; + this.position = position; + this.size = size; + } + + /// + /// Redraw the scrollbar + /// + /// Region to be redrawn. + public override void Redraw(Rect region) + { + Driver.SetAttribute (ColorScheme.Normal); + + if (vertical) { + if (region.Right < Bounds.Width - 1) + return; + + var col = Bounds.Width - 1; + var bh = Bounds.Height; + SpecialChar special; + + if (bh < 3) { + var by1 = position * bh / Size; + var by2 = (position + bh) * bh / Size; + + for (int y = 0; y < bh; y++) { + Move (col, y); + if (y < by1 || y > by2) + special = SpecialChar.Stipple; + else + special = SpecialChar.Diamond; + Driver.AddSpecial (special); + } + } else { + bh -= 2; + var by1 = position * bh / Size; + var by2 = (position + bh) * bh / Size; + + + Move (col, 0); + Driver.AddRune ('^'); + Move (col, Bounds.Height - 1); + Driver.AddRune ('v'); + for (int y = 0; y < bh; y++) { + Move (col, y+1); + + if (y < by1 || y > by2) + special = SpecialChar.Stipple; + else { + if (by2 - by1 == 0) + special = SpecialChar.Diamond; + else { + if (y == by1) + special = SpecialChar.TopTee; + else if (y == by2) + special = SpecialChar.BottomTee; + else + special = SpecialChar.VLine; + } + } + Driver.AddSpecial (special); + } + } + } else { + if (region.Bottom < Bounds.Height - 1) + return; + + var row = Bounds.Height - 1; + var bw = Bounds.Width; + if (bw < 3) { + } else { + bw -= 2; + var bx1 = position * bw / Size; + var bx2 = (position + bw) * bw / Size; + + Move (0, row); + Driver.AddRune ('<'); + + for (int x = 0; x < bw; x++) { + SpecialChar special; + + if (x < bx1 || x > bx2) { + special = SpecialChar.Stipple; + } else { + if (bx2 - bx1 == 0) + special = SpecialChar.Diamond; + else { + if (x == bx1) + special = SpecialChar.LeftTee; + else if (x == bx2) + special = SpecialChar.RightTee; + else + special = SpecialChar.HLine; + } + } + Driver.AddSpecial (special); + } + Driver.AddRune ('>'); + } + } + } + } + + /// + /// Scrollviews are views that present a window into a virtual space where children views are added. Similar to the iOS UIScrollView. /// /// /// @@ -15,11 +175,15 @@ namespace Terminal.Gui { /// public class ScrollView : View { View contentView; + ScrollBarView vertical, horizontal; public ScrollView (Rect frame) : base (frame) { contentView = new View (frame); + vertical = new ScrollBarView (new Rect (frame.Width - 1, 0, 1, frame.Height), frame.Height, 0, isVertical: true); + horizontal = new ScrollBarView (new Rect (0, frame.Height-1, frame.Width-1, 1), frame.Width-1, 0, isVertical: false); base.Add (contentView); + CanFocus = true; } Size contentSize; @@ -38,6 +202,8 @@ namespace Terminal.Gui { set { contentSize = value; contentView.Frame = new Rect (contentOffset, value); + vertical.Size = contentSize.Height; + horizontal.Size = contentSize.Width; } } @@ -52,6 +218,8 @@ namespace Terminal.Gui { set { contentOffset = new Point (-value.X, -value.Y); contentView.Frame = new Rect (contentOffset, contentSize); + vertical.Position = Math.Max (0, -contentOffset.Y); + horizontal.Position = Math.Max (0, -contentOffset.X); } } @@ -71,8 +239,15 @@ namespace Terminal.Gui { public bool ShowHorizontalScrollIndicator { get => showHorizontalScrollIndicator; set { + if (value == showHorizontalScrollIndicator) + return; + showHorizontalScrollIndicator = value; SetNeedsDisplay (); + if (value) + base.Add (horizontal); + else + Remove (horizontal); } } @@ -84,8 +259,15 @@ namespace Terminal.Gui { public bool ShowVerticalScrollIndicator { get => showVerticalScrollIndicator; set { + if (value == showVerticalScrollIndicator) + return; + showVerticalScrollIndicator = value; SetNeedsDisplay (); + if (value) + base.Add (vertical); + else + Remove (vertical); } } @@ -98,70 +280,16 @@ namespace Terminal.Gui { { var oldClip = ClipToBounds (); base.Redraw(region); - Attribute last = ColorScheme.Normal; - Driver.SetAttribute (last); - - void SetColor (Attribute a) - { - if (a != last) - Driver.SetAttribute (a); - last = a; - } - Driver.Clip = oldClip; + Driver.SetAttribute (ColorScheme.Normal); + } - if (true || ShowVerticalScrollIndicator) { - var bh = Bounds.Height; - var by1 = -contentOffset.Y * bh/ contentSize.Height; - var by2 = (-contentOffset.Y+bh) * bh/ contentSize.Height; - - for (int y = 0; y < bh; y++) { - Move (Bounds.Width - 1, y); - SpecialChar special; - - if (y < by1 || y > by2) - special = SpecialChar.Stipple; - else { - if (by2 - by1 == 0) - special = SpecialChar.Diamond; - else { - if (y == by1) - special = SpecialChar.TopTee; - else if (y == by2) - special = SpecialChar.BottomTee; - else - special = SpecialChar.VLine; - } - } - Driver.AddSpecial (special); - } - } - if (true || ShowHorizontalScrollIndicator){ - var bw = Bounds.Width; - var bx1 = -contentOffset.X * bw / contentSize.Width; - var bx2 = (-contentOffset.X + bw) * bw / contentSize.Width; - - Move (0, Bounds.Height - 1); - for (int x = 0; x < bw; x++) { - SpecialChar special; - - if (x < bx1 || x > bx2){ - special = SpecialChar.Stipple; - } else { - if (bx2 - bx1 == 0) - special = SpecialChar.Diamond; - else { - if (x == bx1) - special = SpecialChar.LeftTee; - else if (x == bx2) - special = SpecialChar.RightTee; - else - special = SpecialChar.HLine; - } - } - Driver.AddSpecial (special); - } - } + public override void PositionCursor() + { + if (Subviews.Count == 0) + Driver.Move (0, 0); + else + base.PositionCursor (); } } } diff --git a/demo.cs b/demo.cs index 9637a4d0f..ef4f4b331 100644 --- a/demo.cs +++ b/demo.cs @@ -67,7 +67,9 @@ class Demo { { var scrollView = new ScrollView (new Rect (50, 10, 20, 8)) { ContentSize = new Size (100, 100), - ContentOffset = new Point (5, -2) + ContentOffset = new Point (5, -2), + ShowVerticalScrollIndicator = true, + ShowHorizontalScrollIndicator = true }; //scrollView.Add (new Box10x (0, 0));