Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #505. Finishes merge of TileView (previously SplitContainer) #2258

Merged
merged 21 commits into from Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Terminal.Gui/Views/FrameView.cs
Expand Up @@ -310,7 +310,7 @@ public override void Redraw (Rect bounds)
foreach (var p in lc.GenerateImage (bounds)) {
this.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 ();
Expand Down
50 changes: 33 additions & 17 deletions Terminal.Gui/Views/TileView.cs
Expand Up @@ -13,6 +13,12 @@ namespace Terminal.Gui {
public class TileView : View {
TileView parentTileView;

/// <summary>
/// The keyboard key that the user can press to toggle resizing
/// of splitter lines. Mouse drag splitting is always enabled.
/// </summary>
public Key ToggleResizable { get; set; } = Key.CtrlMask | Key.F10;

/// <summary>
/// A single <see cref="ContentView"/> presented in a <see cref="TileView"/>. To create
/// new instances use <see cref="TileView.RebuildForTileCount(int)"/>
Expand Down Expand Up @@ -167,7 +173,6 @@ public TileView () : this (2)
/// <param name="tiles"></param>
public TileView (int tiles)
{
CanFocus = true;
RebuildForTileCount (tiles);
IgnoreBorderPropertyOnRedraw = true;
Border = new Border () {
Expand Down Expand Up @@ -403,15 +408,6 @@ public bool SetSplitterPos (int idx, Pos value)
return true;
}

/// <inheritdoc/>
public override bool OnEnter (View view)
{
Driver.SetCursorVisibility (CursorVisibility.Invisible);
if (!Tiles.Where (t => t.ContentView.HasFocus).Any ()) {
Tiles.FirstOrDefault ()?.ContentView.SetFocus ();
}
return base.OnEnter (view);
}

/// <inheritdoc/>
public override void Redraw (Rect bounds)
Expand Down Expand Up @@ -550,6 +546,30 @@ public bool TrySplitTile (int idx, int numberOfPanels, out TileView result)
return true;
}

/// <inheritdoc/>
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);
Expand Down Expand Up @@ -750,7 +770,7 @@ private void Setup (Rect bounds)
tile.ContentView.Width = GetTileWidthOrHeight (i, Bounds.Width, visibleTiles, visibleSplitterLines);
} else {
tile.ContentView.X = bounds.X;
tile.ContentView.Y = i == 0 ? 0 : Pos.Bottom (visibleSplitterLines [i - 1]);
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);
}
Expand Down Expand Up @@ -864,7 +884,7 @@ private class TileViewLineView : LineView {

public TileViewLineView (TileView parent, int idx)
{
CanFocus = true;
CanFocus = false;
TabStop = true;

this.Parent = parent;
Expand Down Expand Up @@ -929,7 +949,7 @@ public override void Redraw (Rect bounds)

public void DrawSplitterSymbol ()
{
if (CanFocus && HasFocus) {
if (dragPosition != null || CanFocus) {
var location = moveRuneRenderLocation ??
new Point (Bounds.Width / 2, Bounds.Height / 2);

Expand All @@ -939,10 +959,6 @@ public void DrawSplitterSymbol ()

public override bool MouseEvent (MouseEvent mouseEvent)
{
if (!CanFocus) {
return true;
}

if (!dragPosition.HasValue && (mouseEvent.Flags == MouseFlags.Button1Pressed)) {

// Start a Drag
Expand Down
8 changes: 4 additions & 4 deletions UICatalog/Scenarios/TileViewExperiment.cs
Expand Up @@ -32,7 +32,7 @@ public override void Setup ()
Y = 1,
Width = 15, //Dim.Fill (),
Height = 15, //Dim.Fill (),
//IgnoreBorderPropertyOnRedraw = true
//IgnoreBorderPropertyOnRedraw = true

};
frame1.Border.BorderStyle = BorderStyle.Double;
Expand Down Expand Up @@ -60,11 +60,11 @@ public override void Setup ()
Width = 14, //Dim.Percent (30) - 5,
Height = 14, //Dim.Percent (50) - 5,
ColorScheme = Colors.ColorSchemes ["Dialog"],
Border = new Border () {
BorderStyle = BorderStyle.Single,
Border = new Border () {
BorderStyle = BorderStyle.Single,
//BorderThickness = new Thickness (1),
DrawMarginFrame = true,
Padding = new Thickness(1),
Padding = new Thickness (1),
BorderBrush = Color.BrightMagenta,
Title = "Border Title"
}
Expand Down
3 changes: 3 additions & 0 deletions UICatalog/Scenarios/TileViewNesting.cs
Expand Up @@ -94,6 +94,9 @@ private void SetupTileView ()
bool? border = cbBorder.Checked;
bool? startHorizontal = cbHorizontal.Checked;

foreach(var sub in workArea.Subviews) {
sub.Dispose ();
}
workArea.RemoveAll ();

if (numberOfViews <= 0) {
Expand Down
101 changes: 61 additions & 40 deletions UnitTests/Views/TileViewTests.cs
@@ -1,6 +1,6 @@
using System;
using System.ComponentModel;
using System.Linq;
using Terminal.Gui;
using Terminal.Gui.Graphs;
using Xunit;
using Xunit.Abstractions;
Expand Down Expand Up @@ -60,7 +60,7 @@ public void TestTileView_Vertical_WithBorder ()
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);

Expand Down Expand Up @@ -100,7 +100,7 @@ public void TestTileView_Vertical_Focused ()
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);

Expand Down Expand Up @@ -141,9 +141,10 @@ public void TestTileView_Vertical_Focused_WithBorder ()
public void TestTileView_Vertical_Focused_50PercentSplit ()
{
var tileView = Get11By3TileView (out var line);
SetInputFocusLine (tileView);
tileView.SetSplitterPos (0, Pos.Percent (50));
Assert.IsType<Pos.PosFactor> (tileView.SplitterDistances.ElementAt (0));
tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));

tileView.Redraw (tileView.Bounds);

string looksLike =
Expand Down Expand Up @@ -209,7 +210,7 @@ public void TestTileView_Horizontal ()
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)
Expand Down Expand Up @@ -254,7 +255,7 @@ public void TestTileView_Vertical_View1MinSize_Absolute ()
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)
Expand Down Expand Up @@ -298,7 +299,7 @@ public void TestTileView_Vertical_View1MinSize_Absolute_WithBorder ()
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)
Expand Down Expand Up @@ -342,7 +343,7 @@ public void TestTileView_Vertical_View2MinSize_Absolute ()
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)
Expand Down Expand Up @@ -386,8 +387,6 @@ public void TestTileView_Vertical_View2MinSize_Absolute_WithBorder ()
public void TestTileView_InsertPanelAtStart ()
{
var tileView = Get11By3TileView (out var line, true);
SetInputFocusLine (tileView);

tileView.InsertTile (0);

tileView.Redraw (tileView.Bounds);
Expand All @@ -405,8 +404,6 @@ public void TestTileView_InsertPanelAtStart ()
public void TestTileView_InsertPanelMiddle ()
{
var tileView = Get11By3TileView (out var line, true);
SetInputFocusLine (tileView);

tileView.InsertTile (1);

tileView.Redraw (tileView.Bounds);
Expand All @@ -424,8 +421,6 @@ public void TestTileView_InsertPanelMiddle ()
public void TestTileView_InsertPanelAtEnd ()
{
var tileView = Get11By3TileView (out var line, true);
SetInputFocusLine (tileView);

tileView.InsertTile (2);

tileView.Redraw (tileView.Bounds);
Expand All @@ -445,7 +440,9 @@ public void TestTileView_Horizontal_Focused ()
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);

Expand Down Expand Up @@ -485,9 +482,9 @@ public void TestTileView_Horizontal_Focused ()
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
Expand Down Expand Up @@ -2045,6 +2042,45 @@ public void TestNestedContainer3RightAnd1Down_TileVisibility_WithoutBorder ()

}

[Fact, AutoInitShutdown]
public void Test_SplitTop_WholeBottom()
{
var tileView = new TileView (2) {
Width = 20,
Height = 10,
Orientation = Orientation.Horizontal,
};
tileView.Border.BorderStyle = BorderStyle.Single;

Assert.True (tileView.TrySplitTile (0,2,out TileView top));

top.Tiles.ElementAt (0).ContentView.Add (new Label ("bleh"));
top.Tiles.ElementAt (1).ContentView.Add (new Label ("blah"));

tileView.Tiles.ElementAt (1).ContentView.Add (new Label ("Hello"));
tileView.ColorScheme = new ColorScheme ();
top.ColorScheme = new ColorScheme ();
tileView.LayoutSubviews ();

tileView.Redraw (tileView.Bounds);

string looksLike =
@"
┌─────────┬────────┐
│bleh │blah │
│ │ │
│ │ │
│ │ │
├─────────┴────────┤
│Hello │
│ │
│ │
└──────────────────┘";

TestHelpers.AssertDriverContentsAre (looksLike, output);

}

[Fact, AutoInitShutdown]
public void TestNestedContainer3RightAnd1Down_TitleDoesNotOverspill()
{
Expand Down Expand Up @@ -2094,12 +2130,10 @@ public void TestNestedContainer3RightAnd1Down_TitleTriesToOverspill ()

TestHelpers.AssertDriverContentsAre (looksLike, output);
}


[Fact,AutoInitShutdown]
[Fact, AutoInitShutdown]
public void TestDisposal_NoEarlyDisposalsOfUsersViews_DuringRebuildForTileCount ()
{
var tv = GetTileView (20,10);
var tv = GetTileView (20, 10);

var myReusableView = new DisposeCounter ();

Expand All @@ -2113,10 +2147,10 @@ public void TestDisposal_NoEarlyDisposalsOfUsersViews_DuringRebuildForTileCount
// but I still want my view in the first tile
tv.Tiles.ElementAt (0).ContentView.Add (myReusableView);
Assert.Multiple (
()=>Assert.Equal (0, myReusableView.DisposalCount)
,()=> {
() => Assert.Equal (0, myReusableView.DisposalCount)
, () => {
tv.Dispose ();
Assert.Equal (1, myReusableView.DisposalCount);
Assert.Equal (1, myReusableView.DisposalCount);
});
}
[Fact, AutoInitShutdown]
Expand All @@ -2140,15 +2174,13 @@ public void TestDisposal_NoEarlyDisposalsOfUsersViews_DuringInsertTile ()
() => Assert.Equal (0, myReusableView.DisposalCount)
, () => {
tv.Dispose ();

// TODO seems to be double disposed ?!
Assert.True (myReusableView.DisposalCount >= 1);
Assert.True (myReusableView.DisposalCount>=1);
});
}
[Theory, AutoInitShutdown]
[InlineData(0)]
[InlineData (0)]
[InlineData (1)]
public void TestDisposal_NoEarlyDisposalsOfUsersViews_DuringRemoveTile(int idx)
public void TestDisposal_NoEarlyDisposalsOfUsersViews_DuringRemoveTile (int idx)
{
var tv = GetTileView (20, 10);

Expand All @@ -2166,14 +2198,10 @@ public void TestDisposal_NoEarlyDisposalsOfUsersViews_DuringRemoveTile(int idx)
() => Assert.Equal (0, myReusableView.DisposalCount)
, () => {
tv.Dispose ();

// TODO seems to be double disposed ?!
Assert.True (myReusableView.DisposalCount >= 1);
});
}

private class DisposeCounter : View
{
private class DisposeCounter : View {
public int DisposalCount;
protected override void Dispose (bool disposing)
{
Expand Down Expand Up @@ -2255,13 +2283,6 @@ private LineView GetLine (TileView tileView)
return tileView.Subviews.OfType<LineView> ().Single ();
}

private void SetInputFocusLine (TileView tileView)
{
var line = GetLine (tileView);
line.SetFocus ();
Assert.True (line.HasFocus);
}


private TileView Get5x1TilesView (bool border = true)
{
Expand Down