diff --git a/Terminal.Gui/Views/FrameView.cs b/Terminal.Gui/Views/FrameView.cs index 09ad21884..8ae9b1412 100644 --- a/Terminal.Gui/Views/FrameView.cs +++ b/Terminal.Gui/Views/FrameView.cs @@ -12,6 +12,7 @@ using System; using System.Linq; using NStack; +using Terminal.Gui.Graphs; namespace Terminal.Gui { /// @@ -88,6 +89,7 @@ namespace Terminal.Gui { public FrameView (Rect frame, ustring title = null, View [] views = null, Border border = null) : base (frame) { //var cFrame = new Rect (1, 1, Math.Max (frame.Width - 2, 0), Math.Max (frame.Height - 2, 0)); + Initialize (frame, title, views, border); } @@ -229,14 +231,60 @@ namespace Terminal.Gui { ClearNeedsDisplay (); - Driver.SetAttribute (GetNormalColor ()); - //Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: false); - Border.DrawContent (this, false); - if (HasFocus) - Driver.SetAttribute (ColorScheme.HotNormal); - if (Border.DrawMarginFrame) - Driver.DrawWindowTitle (scrRect, Title, padding.Left, padding.Top, padding.Right, padding.Bottom); - Driver.SetAttribute (GetNormalColor ()); + if (!IgnoreBorderPropertyOnRedraw) { + Driver.SetAttribute (GetNormalColor ()); + //Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: false); + Border.DrawContent (this, false); + if (HasFocus) + Driver.SetAttribute (ColorScheme.HotNormal); + if (Border.DrawMarginFrame) + Driver.DrawWindowTitle (scrRect, Title, padding.Left, padding.Top, padding.Right, padding.Bottom); + Driver.SetAttribute (GetNormalColor ()); + } else { + var lc = new LineCanvas (); + + if (Border?.BorderStyle != BorderStyle.None) { + + lc.AddLine (new Point (0, 0), bounds.Width - 1, Orientation.Horizontal, Border.BorderStyle); + lc.AddLine (new Point (0, 0), bounds.Height - 1, Orientation.Vertical, Border.BorderStyle); + + lc.AddLine (new Point (bounds.Width - 1, bounds.Height - 1), -bounds.Width + 1, Orientation.Horizontal, Border.BorderStyle); + lc.AddLine (new Point (bounds.Width - 1, bounds.Height - 1), -bounds.Height + 1, Orientation.Vertical, Border.BorderStyle); + } + + foreach (var subview in contentView.Subviews) { + lc.AddLine (new Point (subview.Frame.X + 1, subview.Frame.Y + 1), subview.Frame.Width - 1, Orientation.Horizontal, subview.Border.BorderStyle); + lc.AddLine (new Point (subview.Frame.X + 1, subview.Frame.Y + 1), subview.Frame.Height - 1, Orientation.Vertical, subview.Border.BorderStyle); + + lc.AddLine (new Point (subview.Frame.X + subview.Frame.Width, subview.Frame.Y + subview.Frame.Height), -subview.Frame.Width + 1, Orientation.Horizontal, subview.Border.BorderStyle); + lc.AddLine (new Point (subview.Frame.X + subview.Frame.Width, subview.Frame.Y + subview.Frame.Height), -subview.Frame.Height + 1, Orientation.Vertical, subview.Border.BorderStyle); + + } + + Driver.SetAttribute (ColorScheme.Normal); + foreach(var p in lc.GenerateImage (bounds)) { + AddRune (p.Key.X, p.Key.Y, p.Value); + } + + + + // Redraw the lines so that focus/drag symbol renders + foreach (var subview in contentView.Subviews) { + // line.DrawSplitterSymbol (); + } + + // Draw Titles over Border + foreach (var subview in contentView.Subviews) { + // TODO: Use reflection to see if subview has a Title property + if (subview is FrameView viewWithTite) { + var rect = viewWithTite.Frame; + rect.X = rect.X + 1; + rect.Y = rect.Y + 2; + // TODO: Do focus color correctly + Driver.DrawWindowTitle (rect, viewWithTite.Title, padding.Left, padding.Top, padding.Right, padding.Bottom); + } + } + } } /// diff --git a/Terminal.Gui/Views/TileView.cs b/Terminal.Gui/Views/TileView.cs index 3da730448..093d105fb 100644 --- a/Terminal.Gui/Views/TileView.cs +++ b/Terminal.Gui/Views/TileView.cs @@ -1,4 +1,5 @@ -using System; +using NStack; +using System; using System.Collections.Generic; using System.Linq; using Terminal.Gui.Graphs; @@ -10,29 +11,26 @@ namespace Terminal.Gui { /// the display area into resizeable . /// public class TileView : View { - TileView parentTileView; - /// - /// Use this field instead of Border to create an integrated - /// Border in which lines connect with subviews and splitters - /// seamlessly + /// The keyboard key that the user can press to toggle resizing + /// of splitter lines. Mouse drag splitting is always enabled. /// - public BorderStyle IntegratedBorder { get; set; } + public Key ToggleResizable { get; set; } = Key.CtrlMask | Key.F10; /// - /// A single presented in a . To create + /// A single presented in a . To create /// new instances use /// or . /// public class Tile { /// - /// The that is showing in this . - /// You should add new child views to this member if you want multiple - /// within the . + /// The that is contained in this . + /// Add new child views to this member for multiple + /// s within the . /// - public View View { get; internal set; } + public View ContentView { get; internal set; } /// /// Gets or Sets the minimum size you to allow when splitter resizing along @@ -41,21 +39,103 @@ namespace Terminal.Gui { public int MinSize { get; set; } /// - /// The text that should be displayed above the . This + /// The text that should be displayed above the . This /// will appear over the splitter line or border (above the view client area). /// /// - /// Title are not rendered for root level tiles if there is no - /// to render into. + /// Title are not rendered for root level tiles + /// is . /// - public string Title { get; set; } + public string Title { + get => _title; + set { + if (!OnTitleChanging (_title, value)) { + var old = _title; + _title = value; + OnTitleChanged (old, _title); + return; + } + _title = value; + } + } + + private string _title = string.Empty; + + /// + /// An which allows passing a cancelable new value event. + /// + public class TitleEventArgs : EventArgs { + /// + /// The new Window Title. + /// + public ustring NewTitle { get; set; } + + /// + /// The old Window Title. + /// + public ustring OldTitle { get; set; } + + /// + /// Flag which allows cancelling the Title change. + /// + public bool Cancel { get; set; } + + /// + /// Initializes a new instance of + /// + /// The that is/has been replaced. + /// The new to be replaced. + public TitleEventArgs (ustring oldTitle, ustring newTitle) + { + OldTitle = oldTitle; + NewTitle = newTitle; + } + } + + /// + /// Called before the changes. Invokes the event, which can be cancelled. + /// + /// The that is/has been replaced. + /// The new to be replaced. + /// true if an event handler cancelled the Title change. + public virtual bool OnTitleChanging (ustring oldTitle, ustring newTitle) + { + var args = new TitleEventArgs (oldTitle, newTitle); + TitleChanging?.Invoke (args); + return args.Cancel; + } + + /// + /// Event fired when the is changing. Set to + /// true to cancel the Title change. + /// + public event Action TitleChanging; + + /// + /// Called when the has been changed. Invokes the event. + /// + /// The that is/has been replaced. + /// The new to be replaced. + public virtual void OnTitleChanged (ustring oldTitle, ustring newTitle) + { + var args = new TitleEventArgs (oldTitle, newTitle); + TitleChanged?.Invoke (args); + } + + /// + /// Event fired after the has been changed. + /// + public event Action TitleChanged; /// /// Creates a new instance of the class. /// - internal Tile () + public Tile () { - View = new View () { Width = Dim.Fill (), Height = Dim.Fill () }; + ContentView = new View () { Width = Dim.Fill (), Height = Dim.Fill () }; +#if DEBUG_IDISPOSABLE + ContentView.Data = "Tile.ContentView"; +#endif Title = string.Empty; MinSize = 0; } @@ -71,7 +151,7 @@ namespace Terminal.Gui { public IReadOnlyCollection Tiles => tiles.AsReadOnly (); /// - /// The splitter locations. Note that there will be N-1 splitters where + /// The splitter locations. Note that there will be N-1 splitters where /// N is the number of . /// public IReadOnlyCollection SplitterDistances => splitterDistances.AsReadOnly (); @@ -93,8 +173,11 @@ namespace Terminal.Gui { /// public TileView (int tiles) { - CanFocus = true; RebuildForTileCount (tiles); + IgnoreBorderPropertyOnRedraw = true; + Border = new Border () { + BorderStyle = BorderStyle.None + }; } /// @@ -111,7 +194,7 @@ namespace Terminal.Gui { } /// - /// Scraps all and creates new tiles + /// Scraps all and creates new tiles /// in orientation /// /// @@ -119,9 +202,18 @@ namespace Terminal.Gui { { tiles = new List (); splitterDistances = new List (); + if (splitterLines != null) { + foreach (var sl in splitterLines) { + sl.Dispose (); + } + } splitterLines = new List (); RemoveAll (); + foreach (var tile in tiles) { + tile.ContentView.Dispose (); + tile.ContentView = null; + } tiles.Clear (); splitterDistances.Clear (); @@ -130,8 +222,6 @@ namespace Terminal.Gui { } for (int i = 0; i < count; i++) { - - if (i > 0) { var currentPos = Pos.Percent ((100 / count) * i); splitterDistances.Add (currentPos); @@ -142,7 +232,8 @@ namespace Terminal.Gui { var tile = new Tile (); tiles.Add (tile); - Add (tile.View); + Add (tile.ContentView); + tile.TitleChanged += (e) => SetNeedsDisplay (); } LayoutSubviews (); @@ -153,7 +244,6 @@ namespace Terminal.Gui { /// This will also add another splitter line /// /// - /// public Tile InsertTile (int idx) { var oldTiles = Tiles.ToArray (); @@ -167,11 +257,13 @@ namespace Terminal.Gui { var oldTile = oldTiles [i > idx ? i - 1 : i]; // remove the new empty View - Remove (tiles [i].View); + Remove (tiles [i].ContentView); + tiles [i].ContentView.Dispose (); + tiles [i].ContentView = null; // restore old Tile and View tiles [i] = oldTile; - Add (tiles [i].View); + Add (tiles [i].ContentView); } else { toReturn = tiles [i]; } @@ -184,7 +276,7 @@ namespace Terminal.Gui { /// /// Removes a at the provided from - /// the view. Returns the removed tile or null if already empty. + /// the view. Returns the removed tile or null if already empty. /// /// /// @@ -206,11 +298,13 @@ namespace Terminal.Gui { var oldTile = oldTiles [oldIdx]; // remove the new empty View - Remove (tiles [i].View); + Remove (tiles [i].ContentView); + tiles [i].ContentView.Dispose (); + tiles [i].ContentView = null; // restore old Tile and View tiles [i] = oldTile; - Add (tiles [i].View); + Add (tiles [i].ContentView); } SetNeedsDisplay (); @@ -226,7 +320,7 @@ namespace Terminal.Gui { public int IndexOf (View toFind, bool recursive = false) { for (int i = 0; i < tiles.Count; i++) { - var v = tiles [i].View; + var v = tiles [i].ContentView; if (v == toFind) { return i; @@ -285,14 +379,12 @@ namespace Terminal.Gui { } Setup (contentArea); - - base.LayoutSubviews (); } /// /// Attempts to update the of line at - /// to the new . Returns false if the new position is not allowed because of + /// to the new . Returns false if the new position is not allowed because of /// , location of other splitters etc. /// /// Only absolute values (e.g. 10) and percent values (i.e. ) @@ -301,7 +393,7 @@ namespace Terminal.Gui { public bool SetSplitterPos (int idx, Pos value) { if (!(value is Pos.PosAbsolute) && !(value is Pos.PosFactor)) { - throw new ArgumentException ($"Only Percent and Absolute values are supported. Passed value was {value.GetType ().Name}"); + throw new ArgumentException ($"Only Percent and Absolute values are supported. Passed value was {value.GetType ().Name}"); } var fullSpace = orientation == Orientation.Vertical ? Bounds.Width : Bounds.Height; @@ -316,18 +408,13 @@ namespace Terminal.Gui { return true; } - /// - public override bool OnEnter (View view) - { - Driver.SetCursorVisibility (CursorVisibility.Invisible); - return base.OnEnter (view); - } /// public override void Redraw (Rect bounds) { Driver.SetAttribute (ColorScheme.Normal); Clear (); + base.Redraw (bounds); var lc = new LineCanvas (); @@ -338,11 +425,11 @@ namespace Terminal.Gui { if (IsRootTileView ()) { if (HasBorder ()) { - lc.AddLine (new Point (0, 0), bounds.Width - 1, Orientation.Horizontal, IntegratedBorder); - lc.AddLine (new Point (0, 0), bounds.Height - 1, Orientation.Vertical, IntegratedBorder); + lc.AddLine (new Point (0, 0), bounds.Width - 1, Orientation.Horizontal, Border.BorderStyle); + lc.AddLine (new Point (0, 0), bounds.Height - 1, Orientation.Vertical, Border.BorderStyle); - lc.AddLine (new Point (bounds.Width - 1, bounds.Height - 1), -bounds.Width + 1, Orientation.Horizontal, IntegratedBorder); - lc.AddLine (new Point (bounds.Width - 1, bounds.Height - 1), -bounds.Height + 1, Orientation.Vertical, IntegratedBorder); + lc.AddLine (new Point (bounds.Width - 1, bounds.Height - 1), -bounds.Width + 1, Orientation.Horizontal, Border.BorderStyle); + lc.AddLine (new Point (bounds.Width - 1, bounds.Height - 1), -bounds.Height + 1, Orientation.Vertical, Border.BorderStyle); } foreach (var line in allLines) { @@ -363,12 +450,14 @@ namespace Terminal.Gui { length += 2; } - lc.AddLine (origin, length, line.Orientation, IntegratedBorder); + lc.AddLine (origin, length, line.Orientation, Border.BorderStyle); } } Driver.SetAttribute (ColorScheme.Normal); - lc.Draw (this, bounds); + foreach (var p in lc.GenerateImage (bounds)) { + AddRune (p.Key.X, p.Key.Y, p.Value); + } // Redraw the lines so that focus/drag symbol renders foreach (var line in allLines) { @@ -388,7 +477,7 @@ namespace Terminal.Gui { // TODO: Render with focus color if focused - var title = titleToRender.GetTrimmedTitle(); + var title = titleToRender.GetTrimmedTitle (); for (int i = 0; i < title.Length; i++) { AddRune (renderAt.X + i, renderAt.Y, title [i]); @@ -408,7 +497,7 @@ namespace Terminal.Gui { /// The number of panels that the should be split into /// The new nested . /// if a was converted to a new nested - /// . if it was already a nested + /// . if it was already a nested /// public bool TrySplitTile (int idx, int numberOfPanels, out TileView result) { @@ -417,7 +506,7 @@ namespace Terminal.Gui { var tile = tiles [idx]; var title = tile.Title; - View toMove = tile.View; + View toMove = tile.ContentView; if (toMove is TileView existing) { result = existing; @@ -436,11 +525,14 @@ namespace Terminal.Gui { // Remove the view itself and replace it with the new TileView Remove (toMove); + toMove.Dispose (); + toMove = null; + Add (newContainer); - tile.View = newContainer; + tile.ContentView = newContainer; - var newTileView1 = newContainer.tiles [0].View; + var newTileView1 = newContainer.tiles [0].ContentView; // Add the original content into the first view of the new container foreach (var childView in childViews) { newTileView1.Add (childView); @@ -454,6 +546,30 @@ namespace Terminal.Gui { return true; } + /// + public override bool ProcessHotKey (KeyEvent keyEvent) + { + bool focusMoved = false; + + if(keyEvent.Key == ToggleResizable) { + foreach(var l in splitterLines) { + + var iniBefore = l.IsInitialized; + l.IsInitialized = false; + l.CanFocus = !l.CanFocus; + l.IsInitialized = iniBefore; + + if (l.CanFocus && !focusMoved) { + l.SetFocus (); + focusMoved = true; + } + } + return true; + } + + return base.ProcessHotKey (keyEvent); + } + private bool IsValidNewSplitterPos (int idx, Pos value, int fullSpace) { int newSize = value.Anchor (fullSpace); @@ -554,18 +670,18 @@ namespace Terminal.Gui { foreach (var sub in v.Tiles) { // Don't render titles for invisible stuff! - if (!sub.View.Visible) { + if (!sub.ContentView.Visible) { continue; } - if (sub.View is TileView subTileView) { + if (sub.ContentView is TileView subTileView) { // Panels with sub split tiles in them can never // have their Titles rendered. Instead we dive in // and pull up their children as titles titles.AddRange (GetAllTitlesToRenderRecursively (subTileView, depth + 1)); } else { if (sub.Title.Length > 0) { - titles.Add (new TileTitleToRender (v,sub, depth)); + titles.Add (new TileTitleToRender (v, sub, depth)); } } } @@ -576,11 +692,11 @@ namespace Terminal.Gui { /// /// /// if is nested within a parent - /// e.g. via the . if it is a root level . + /// e.g. via the . if it is a root level . /// /// /// Note that manually adding one to another will not result in a parent/child - /// relationship and both will still be considered 'root' containers. Always use + /// relationship and both will still be considered 'root' containers. Always use /// if you want to subdivide a . /// public bool IsRootTileView () @@ -589,8 +705,8 @@ namespace Terminal.Gui { } /// - /// Returns the immediate parent of this. Note that in case - /// of deep nesting this might not be the root . Returns null + /// Returns the immediate parent of this. Note that in case + /// of deep nesting this might not be the root . Returns null /// if this instance is not a nested child (created with /// ) /// @@ -641,22 +757,22 @@ namespace Terminal.Gui { HideSplittersBasedOnTileVisibility (); - var visibleTiles = tiles.Where (t => t.View.Visible).ToArray (); + var visibleTiles = tiles.Where (t => t.ContentView.Visible).ToArray (); var visibleSplitterLines = splitterLines.Where (l => l.Visible).ToArray (); for (int i = 0; i < visibleTiles.Length; i++) { var tile = visibleTiles [i]; if (Orientation == Orientation.Vertical) { - tile.View.X = i == 0 ? bounds.X : Pos.Right (visibleSplitterLines [i - 1]); - tile.View.Y = bounds.Y; - tile.View.Height = bounds.Height; - tile.View.Width = GetTileWidthOrHeight (i, Bounds.Width, visibleTiles, visibleSplitterLines); + tile.ContentView.X = i == 0 ? bounds.X : Pos.Right (visibleSplitterLines [i - 1]); + tile.ContentView.Y = bounds.Y; + tile.ContentView.Height = bounds.Height; + tile.ContentView.Width = GetTileWidthOrHeight (i, Bounds.Width, visibleTiles, visibleSplitterLines); } else { - tile.View.X = bounds.X; - tile.View.Y = i == 0 ? 0 : Pos.Bottom (visibleSplitterLines [i - 1]); - tile.View.Width = bounds.Width; - tile.View.Height = GetTileWidthOrHeight (i, Bounds.Height, visibleTiles, visibleSplitterLines); + tile.ContentView.X = bounds.X; + tile.ContentView.Y = i == 0 ? bounds.Y : Pos.Bottom (visibleSplitterLines [i - 1]); + tile.ContentView.Width = bounds.Width; + tile.ContentView.Height = GetTileWidthOrHeight (i, Bounds.Height, visibleTiles, visibleSplitterLines); } } } @@ -672,7 +788,7 @@ namespace Terminal.Gui { } for (int i = 0; i < tiles.Count; i++) { - if (!tiles [i].View.Visible) { + if (!tiles [i].ContentView.Visible) { // when a tile is not visible, prefer hiding // the splitter on it's left @@ -737,20 +853,20 @@ namespace Terminal.Gui { /// public Point GetLocalCoordinateForTitle (TileView intoCoordinateSpace) { - Tile.View.ViewToScreen (0, 0, out var screenCol, out var screenRow); + Tile.ContentView.ViewToScreen (0, 0, out var screenCol, out var screenRow); screenRow--; return intoCoordinateSpace.ScreenToView (screenCol, screenRow); } internal string GetTrimmedTitle () { - Dim spaceDim = Tile.View.Width; + Dim spaceDim = Tile.ContentView.Width; var spaceAbs = spaceDim.Anchor (Parent.Bounds.Width); - var title = Tile.Title; + var title = $" {Tile.Title} "; - if(title.Length > spaceAbs) { + if (title.Length > spaceAbs) { return title.Substring (0, spaceAbs); } @@ -768,7 +884,7 @@ namespace Terminal.Gui { public TileViewLineView (TileView parent, int idx) { - CanFocus = true; + CanFocus = false; TabStop = true; this.Parent = parent; @@ -833,7 +949,7 @@ namespace Terminal.Gui { public void DrawSplitterSymbol () { - if (CanFocus && HasFocus) { + if (dragPosition != null || CanFocus) { var location = moveRuneRenderLocation ?? new Point (Bounds.Width / 2, Bounds.Height / 2); @@ -843,10 +959,6 @@ namespace Terminal.Gui { public override bool MouseEvent (MouseEvent mouseEvent) { - if (!CanFocus) { - return true; - } - if (!dragPosition.HasValue && (mouseEvent.Flags == MouseFlags.Button1Pressed)) { // Start a Drag @@ -980,12 +1092,23 @@ namespace Terminal.Gui { private bool HasBorder () { - return IntegratedBorder != BorderStyle.None; + return Border?.BorderStyle != BorderStyle.None; } + + /// + protected override void Dispose (bool disposing) + { + foreach (var tile in Tiles) { + Remove (tile.ContentView); + tile.ContentView.Dispose (); + } + base.Dispose (disposing); + } + } /// - /// Provides data for events. + /// Provides data for events. /// public class SplitterEventArgs : EventArgs { @@ -1020,7 +1143,7 @@ namespace Terminal.Gui { } /// - /// Represents a method that will handle splitter events. + /// Represents a method that will handle splitter events. /// public delegate void SplitterEventHandler (object sender, SplitterEventArgs e); } diff --git a/UICatalog/Properties/launchSettings.json b/UICatalog/Properties/launchSettings.json index b4df9cb03..80329d78d 100644 --- a/UICatalog/Properties/launchSettings.json +++ b/UICatalog/Properties/launchSettings.json @@ -54,6 +54,10 @@ "executablePath": "wsl", "commandLineArgs": "dotnet UICatalog.dll", "distributionName": "" + }, + "Tile View Experiments": { + "commandName": "Project", + "commandLineArgs": "\"Tile View Experiments\"" } } } \ No newline at end of file diff --git a/UICatalog/Scenarios/Notepad.cs b/UICatalog/Scenarios/Notepad.cs index 8d2667e15..009f0b92b 100644 --- a/UICatalog/Scenarios/Notepad.cs +++ b/UICatalog/Scenarios/Notepad.cs @@ -48,8 +48,8 @@ namespace UICatalog.Scenarios { Width = Dim.Fill (), Height = Dim.Fill (1), }; - split.Tiles.ElementAt(0).View.Add (tabView); - split.IntegratedBorder = BorderStyle.None; + split.Tiles.ElementAt(0).ContentView.Add (tabView); + split.Border.BorderStyle = BorderStyle.None; Application.Top.Add (split); @@ -156,7 +156,7 @@ namespace UICatalog.Scenarios { var newTile = split.InsertTile(tileIndex + offset); var newTabView = CreateNewTabView (); tab.CloneTo (newTabView); - newTile.View.Add(newTabView); + newTile.ContentView.Add(newTabView); newTabView.EnsureFocus(); newTabView.FocusFirst(); diff --git a/UICatalog/Scenarios/TileViewExperiment.cs b/UICatalog/Scenarios/TileViewExperiment.cs new file mode 100644 index 000000000..eeca74535 --- /dev/null +++ b/UICatalog/Scenarios/TileViewExperiment.cs @@ -0,0 +1,109 @@ +using System; +using Terminal.Gui; +using Terminal.Gui.Graphs; +using System.Linq; + +namespace UICatalog.Scenarios { + [ScenarioMetadata (Name: "Tile View Experiments", Description: "Experiments with Tile View")] + [ScenarioCategory ("Controls")] + [ScenarioCategory ("LineView")] + public class TileViewExperiment : Scenario { + + + public override void Init (ColorScheme colorScheme) + { + Application.Init (); + } + + /// + /// Setup the scenario. + /// + public override void Setup () + { + var menu = new MenuBar (new MenuBarItem [] { + new MenuBarItem ("_File", new MenuItem [] { + new MenuItem ("_Quit", "", () => Application.RequestStop()), + }) }); + + Application.Top.Add (menu); + + var frame = new FrameView () { + X = 0, + Y = 1, + Width = Dim.Fill (), + Height = Dim.Fill (), + IgnoreBorderPropertyOnRedraw = true + }; + frame.Border.BorderStyle = BorderStyle.Double; + + Application.Top.Add (frame); + + var view1 = new FrameView () { + Title = "View 1", + Text = "View1 30%/50% Single", + X = -1, + Y = -1, + Width = Dim.Percent (30), + Height = Dim.Percent (50), + ColorScheme = Colors.ColorSchemes ["Dialog"], + Border = new Border () { BorderStyle = BorderStyle.Single } + }; + + frame.Add (view1); + + //var view12splitter = new SplitterEventArgs + + var view2 = new FrameView () { + Title = "View 2", + Text = "View2 right of view1, 30%/70% Single.", + X = Pos.Right (view1) - 1, + Y = -1, + Width = Dim.Percent (30), + Height = Dim.Percent (70), + ColorScheme = Colors.ColorSchemes ["Error"], + Border = new Border () { BorderStyle = BorderStyle.Single } + }; + + frame.Add (view2); + + var view3 = new FrameView () { + Title = "View 3", + Text = "View3 right of View2 Fill/Fill Single", + X = Pos.Right (view2) - 1, + Y = -1, + Width = Dim.Fill (-1), + Height = Dim.Fill (-1), + ColorScheme = Colors.ColorSchemes ["Menu"], + Border = new Border () { BorderStyle = BorderStyle.Single } + }; + + frame.Add (view3); + + var view4 = new FrameView () { + Title = "View 4", + Text = "View4 below View2 view2.Width/5 Single", + X = Pos.Left (view2), + Y = Pos.Bottom (view2) - 1, + Width = view2.Width, + Height = 5, + ColorScheme = Colors.ColorSchemes ["TopLevel"], + Border = new Border () { BorderStyle = BorderStyle.Single } + }; + + frame.Add (view4); + + var view5 = new FrameView () { + Title = "View 5", + Text = "View5 below View4 view4.Width/5 Double", + X = Pos.Left (view2), + Y = Pos.Bottom (view4) - 1, + Width = view4.Width, + Height = 5, + ColorScheme = Colors.ColorSchemes ["TopLevel"], + Border = new Border () { BorderStyle = BorderStyle.Double } + }; + + frame.Add (view5); + } + } +} \ No newline at end of file diff --git a/UICatalog/Scenarios/TileViewNesting.cs b/UICatalog/Scenarios/TileViewNesting.cs index ba21b5d30..a8adfe9cf 100644 --- a/UICatalog/Scenarios/TileViewNesting.cs +++ b/UICatalog/Scenarios/TileViewNesting.cs @@ -104,19 +104,19 @@ namespace UICatalog.Scenarios { Terminal.Gui.Graphs.Orientation.Horizontal : Terminal.Gui.Graphs.Orientation.Vertical); - root.Tiles.ElementAt(0).View.Add (CreateContentControl (1)); + root.Tiles.ElementAt(0).ContentView.Add (CreateContentControl (1)); root.Tiles.ElementAt(0).Title = cbTitles.Checked ? $"View 1" : string.Empty; - root.Tiles.ElementAt (1).View.Add (CreateContentControl (2)); + root.Tiles.ElementAt (1).ContentView.Add (CreateContentControl (2)); root.Tiles.ElementAt(1).Title = cbTitles.Checked ? $"View 2" : string.Empty; - root.IntegratedBorder = border ? BorderStyle.Rounded : BorderStyle.None; + root.Border.BorderStyle = border ? BorderStyle.Rounded : BorderStyle.None; workArea.Add (root); if (numberOfViews == 1) { - root.Tiles.ElementAt (1).View.Visible = false; + root.Tiles.ElementAt (1).ContentView.Visible = false; } if (numberOfViews > 2) { @@ -164,18 +164,18 @@ namespace UICatalog.Scenarios { if (viewsCreated == viewsToCreate) { return; } - if (!(to.Tiles.ElementAt(0).View is TileView)) { + if (!(to.Tiles.ElementAt(0).ContentView is TileView)) { Split(to,true); } - if (!(to.Tiles.ElementAt (1).View is TileView)) { + if (!(to.Tiles.ElementAt (1).ContentView is TileView)) { Split(to,false); } - if (to.Tiles.ElementAt (0).View is TileView && to.Tiles.ElementAt (1).View is TileView) { + if (to.Tiles.ElementAt (0).ContentView is TileView && to.Tiles.ElementAt (1).ContentView is TileView) { - AddMoreViews ((TileView)to.Tiles.ElementAt (0).View); - AddMoreViews ((TileView)to.Tiles.ElementAt (1).View); + AddMoreViews ((TileView)to.Tiles.ElementAt (0).ContentView); + AddMoreViews ((TileView)to.Tiles.ElementAt (1).ContentView); } } @@ -207,7 +207,7 @@ namespace UICatalog.Scenarios { Orientation.Horizontal : Orientation.Vertical; - newView.Tiles.ElementAt (1).View.Add (CreateContentControl(viewsCreated)); + newView.Tiles.ElementAt (1).ContentView.Add (CreateContentControl(viewsCreated)); } private TileView CreateTileView (int titleNumber, Orientation orientation) diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index 4d20116c4..7659ad4f2 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -216,8 +216,8 @@ namespace UICatalog { Height = Dim.Fill (1), CanFocus = true, Shortcut = Key.CtrlMask | Key.C, - IntegratedBorder = BorderStyle.Rounded, }; + ContentPane.Border.BorderStyle = BorderStyle.Single; ContentPane.SetSplitterPos (0, 25); ContentPane.ShortcutAction = () => ContentPane.SetFocus (); @@ -236,7 +236,7 @@ namespace UICatalog { ContentPane.Tiles.ElementAt(0).Title = "Categories"; ContentPane.Tiles.ElementAt (0).MinSize = 2; - ContentPane.Tiles.ElementAt (0).View.Add (CategoryListView); + ContentPane.Tiles.ElementAt (0).ContentView.Add (CategoryListView); ScenarioListView = new ListView () { X = 0, @@ -250,7 +250,7 @@ namespace UICatalog { ScenarioListView.OpenSelectedItem += ScenarioListView_OpenSelectedItem; ContentPane.Tiles.ElementAt (1).Title = "Scenarios"; - ContentPane.Tiles.ElementAt (1).View.Add (ScenarioListView); + ContentPane.Tiles.ElementAt (1).ContentView.Add (ScenarioListView); ContentPane.Tiles.ElementAt (1).MinSize = 2; KeyDown += KeyDownHandler; diff --git a/UnitTests/TileViewTests.cs b/UnitTests/Views/TileViewTests.cs similarity index 79% rename from UnitTests/TileViewTests.cs rename to UnitTests/Views/TileViewTests.cs index 547bb59a1..d3e97acec 100644 --- a/UnitTests/TileViewTests.cs +++ b/UnitTests/Views/TileViewTests.cs @@ -1,11 +1,11 @@ using System; +using System.ComponentModel; using System.Linq; -using Terminal.Gui; using Terminal.Gui.Graphs; using Xunit; using Xunit.Abstractions; -namespace UnitTests { +namespace Terminal.Gui.ViewTests { public class TileViewTests { readonly ITestOutputHelper output; @@ -60,7 +60,7 @@ namespace UnitTests { public void TestTileView_Vertical_Focused () { var tileView = Get11By3TileView (out var line); - SetInputFocusLine (tileView); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); tileView.Redraw (tileView.Bounds); @@ -100,7 +100,7 @@ namespace UnitTests { public void TestTileView_Vertical_Focused_WithBorder () { var tileView = Get11By3TileView (out var line, true); - SetInputFocusLine (tileView); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); tileView.Redraw (tileView.Bounds); @@ -141,9 +141,10 @@ namespace UnitTests { public void TestTileView_Vertical_Focused_50PercentSplit () { var tileView = Get11By3TileView (out var line); - SetInputFocusLine (tileView); tileView.SetSplitterPos (0, Pos.Percent (50)); Assert.IsType (tileView.SplitterDistances.ElementAt (0)); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); + tileView.Redraw (tileView.Bounds); string looksLike = @@ -209,7 +210,7 @@ namespace UnitTests { public void TestTileView_Vertical_View1MinSize_Absolute () { var tileView = Get11By3TileView (out var line); - SetInputFocusLine (tileView); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); tileView.Tiles.ElementAt (0).MinSize = 6; // distance is too small (below 6) @@ -254,7 +255,7 @@ namespace UnitTests { public void TestTileView_Vertical_View1MinSize_Absolute_WithBorder () { var tileView = Get11By3TileView (out var line, true); - SetInputFocusLine (tileView); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); tileView.Tiles.ElementAt (0).MinSize = 5; // distance is too small (below 5) @@ -298,7 +299,7 @@ namespace UnitTests { public void TestTileView_Vertical_View2MinSize_Absolute () { var tileView = Get11By3TileView (out var line); - SetInputFocusLine (tileView); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); tileView.Tiles.ElementAt (1).MinSize = 6; // distance leaves too little space for view2 (less than 6 would remain) @@ -342,7 +343,7 @@ namespace UnitTests { public void TestTileView_Vertical_View2MinSize_Absolute_WithBorder () { var tileView = Get11By3TileView (out var line, true); - SetInputFocusLine (tileView); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); tileView.Tiles.ElementAt (1).MinSize = 5; // distance leaves too little space for view2 (less than 5 would remain) @@ -386,8 +387,6 @@ namespace UnitTests { public void TestTileView_InsertPanelAtStart () { var tileView = Get11By3TileView (out var line, true); - SetInputFocusLine (tileView); - tileView.InsertTile (0); tileView.Redraw (tileView.Bounds); @@ -405,8 +404,6 @@ namespace UnitTests { public void TestTileView_InsertPanelMiddle () { var tileView = Get11By3TileView (out var line, true); - SetInputFocusLine (tileView); - tileView.InsertTile (1); tileView.Redraw (tileView.Bounds); @@ -424,8 +421,6 @@ namespace UnitTests { public void TestTileView_InsertPanelAtEnd () { var tileView = Get11By3TileView (out var line, true); - SetInputFocusLine (tileView); - tileView.InsertTile (2); tileView.Redraw (tileView.Bounds); @@ -445,7 +440,9 @@ namespace UnitTests { var tileView = Get11By3TileView (out var line); tileView.Orientation = Terminal.Gui.Graphs.Orientation.Horizontal; - SetInputFocusLine (tileView); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); + + Assert.True (line.HasFocus); tileView.Redraw (tileView.Bounds); @@ -485,9 +482,9 @@ namespace UnitTests { public void TestTileView_Horizontal_View1MinSize_Absolute () { var tileView = Get11By3TileView (out var line); + tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ())); tileView.Orientation = Terminal.Gui.Graphs.Orientation.Horizontal; - SetInputFocusLine (tileView); tileView.Tiles.ElementAt (0).MinSize = 1; // 0 should not be allowed because it brings us below minimum size of View1 @@ -534,15 +531,15 @@ namespace UnitTests { var tileView = Get11By3TileView (); var ex = Assert.Throws (() => tileView.SetSplitterPos (0, Pos.Right (tileView))); - Assert.Equal ("Only Percent and Absolute values are supported. Passed value was PosCombine", ex.Message); + Assert.Equal ("Only Percent and Absolute values are supported. Passed value was PosCombine", ex.Message); ex = Assert.Throws (() => tileView.SetSplitterPos (0, Pos.Function (() => 1))); - Assert.Equal ("Only Percent and Absolute values are supported. Passed value was PosFunc", ex.Message); + Assert.Equal ("Only Percent and Absolute values are supported. Passed value was PosFunc", ex.Message); // Also not allowed because this results in a PosCombine ex = Assert.Throws (() => tileView.SetSplitterPos (0, Pos.Percent (50) - 1)); - Assert.Equal ("Only Percent and Absolute values are supported. Passed value was PosCombine", ex.Message); + Assert.Equal ("Only Percent and Absolute values are supported. Passed value was PosCombine", ex.Message); } [Fact, AutoInitShutdown] @@ -551,22 +548,22 @@ namespace UnitTests { var tileView = GetNestedContainer2Left1Right (false); Assert.Equal (20, tileView.Frame.Width); - Assert.Equal (10, tileView.Tiles.ElementAt (0).View.Frame.Width); - Assert.Equal (9, tileView.Tiles.ElementAt (1).View.Frame.Width); + Assert.Equal (10, tileView.Tiles.ElementAt (0).ContentView.Frame.Width); + Assert.Equal (9, tileView.Tiles.ElementAt (1).ContentView.Frame.Width); - Assert.IsType (tileView.Tiles.ElementAt (0).View); - var left = (TileView)tileView.Tiles.ElementAt (0).View; + Assert.IsType (tileView.Tiles.ElementAt (0).ContentView); + var left = (TileView)tileView.Tiles.ElementAt (0).ContentView; Assert.Same (left.SuperView, tileView); - Assert.Equal (2, left.Tiles.ElementAt (0).View.Subviews.Count); - Assert.IsType