diff --git a/Terminal.Gui/Views/SplitContainer.cs b/Terminal.Gui/Views/SplitContainer.cs
index c10ca03eb..04bef1e81 100644
--- a/Terminal.Gui/Views/SplitContainer.cs
+++ b/Terminal.Gui/Views/SplitContainer.cs
@@ -9,17 +9,14 @@ namespace Terminal.Gui {
private bool panel2Collapsed;
private Pos splitterDistance = Pos.Percent (50);
private Orientation orientation = Orientation.Vertical;
- private int panel1MinSize;
+ private Pos panel1MinSize = 0;
+ private Pos panel2MinSize = 0;
///
/// Creates a new instance of the SplitContainer class.
///
public SplitContainer ()
{
- // Default to a border of 0 but not null so that user can
- // more easily change size (without null references)
- Border = new Border ();
-
splitterLine = new SplitContainerLineView (this);
this.Add (Panel1);
@@ -42,7 +39,7 @@ namespace Terminal.Gui {
/// The minimum size can be when adjusting
/// .
///
- public int Panel1MinSize {
+ public Pos Panel1MinSize {
get { return panel1MinSize; }
set {
panel1MinSize = value;
@@ -77,7 +74,16 @@ namespace Terminal.Gui {
/// The minimum size can be when adjusting
/// .
///
- public int Panel2MinSize { get; set; }
+ public Pos Panel2MinSize {
+ get {
+ return panel2MinSize;
+ }
+
+ set {
+ panel2MinSize = value;
+ Setup ();
+ }
+ }
///
/// This determines if is collapsed.
@@ -129,7 +135,6 @@ namespace Terminal.Gui {
Clear ();
base.Redraw (bounds);
}
-
private void Setup ()
{
@@ -157,6 +162,8 @@ namespace Terminal.Gui {
this.Add (Panel2);
}
+ splitterDistance = BoundByMinimumSizes (splitterDistance);
+
switch (Orientation) {
case Orientation.Horizontal:
splitterLine.X = 0;
@@ -202,16 +209,58 @@ namespace Terminal.Gui {
this.LayoutSubviews ();
}
+ ///
+ /// Considers as a candidate for
+ /// then either returns (if valid) or returns adjusted if invalid with respect to
+ /// or .
+ ///
+ ///
+ ///
+ private Pos BoundByMinimumSizes (Pos pos)
+ {
+ // if we are not yet initialized then we don't know
+ // how big we are and therefore cannot sensibly calculate
+ // how big the panels will be with a given SplitterDistance
+ if (!IsInitialized) {
+ return pos;
+ }
+
+ var availableSpace = Orientation == Orientation.Horizontal ? this.Bounds.Height : this.Bounds.Width;
+
+ var idealPosition = pos.Anchor (availableSpace);
+ var panel1MinSizeAbs = panel1MinSize.Anchor (availableSpace);
+ var panel2MinSizeAbs = panel2MinSize.Anchor (availableSpace);
+
+ // bad position because not enough space for panel1
+ if (idealPosition < panel1MinSizeAbs) {
+
+ // TODO: we should preserve Absolute/Percent status here not just force it to absolute
+ return (Pos)Math.Min (panel1MinSizeAbs, availableSpace);
+ }
+
+ // bad position because not enough space for panel2
+ if(availableSpace - idealPosition <= panel2MinSizeAbs) {
+
+ // TODO: we should preserve Absolute/Percent status here not just force it to absolute
+
+ // +1 is to allow space for the splitter
+ return (Pos)Math.Max (availableSpace - (panel2MinSizeAbs+1), 0);
+ }
+
+ // this splitter position is fine, there is enough space for everyone
+ return pos;
+ }
+
private void SetupForCollapsedPanel ()
{
View toRemove = panel1Collapsed ? Panel1 : Panel2;
View toFullSize = panel1Collapsed ? Panel2 : Panel1;
if (this.Subviews.Contains (splitterLine)) {
- this.Subviews.Remove (splitterLine);
+ this.Remove(splitterLine);
}
if (this.Subviews.Contains (toRemove)) {
- this.Subviews.Remove (toRemove);
+ this.Remove (toRemove);
}
if (!this.Subviews.Contains (toFullSize)) {
this.Add (toFullSize);
@@ -239,7 +288,7 @@ namespace Terminal.Gui {
this.parent = parent;
base.AddCommand (Command.Right, () => {
- return MoveSplitter (1,0);
+ return MoveSplitter (1, 0);
});
base.AddCommand (Command.Left, () => {
@@ -247,7 +296,7 @@ namespace Terminal.Gui {
});
base.AddCommand (Command.LineUp, () => {
- return MoveSplitter (0,-1);
+ return MoveSplitter (0, -1);
});
base.AddCommand (Command.LineDown, () => {
@@ -267,7 +316,7 @@ namespace Terminal.Gui {
if (!CanFocus || !HasFocus) {
return base.ProcessKey (kb);
}
-
+
var result = InvokeKeybindings (kb);
if (result != null)
return (bool)result;
@@ -296,7 +345,7 @@ namespace Terminal.Gui {
var location = moveRuneRenderLocation ??
new Point (Bounds.Width / 2, Bounds.Height / 2);
- AddRune (location.X,location.Y, Driver.Diamond);
+ AddRune (location.X, location.Y, Driver.Diamond);
}
}
@@ -309,7 +358,7 @@ namespace Terminal.Gui {
}
if (!dragPosition.HasValue && (mouseEvent.Flags == MouseFlags.Button1Pressed)) {
-
+
// Start a Drag
SetFocus ();
Application.EnsuresTopOnFront ();
@@ -410,8 +459,7 @@ namespace Terminal.Gui {
} else {
parent.SplitterDistance = ConvertToPosFactor (newValue, parent.Bounds.Width);
}
- }
- else {
+ } else {
parent.SplitterDistance = newValue;
}
}
diff --git a/UICatalog/Scenarios/SplitContainerExample.cs b/UICatalog/Scenarios/SplitContainerExample.cs
index 7ef6beda1..474adef44 100644
--- a/UICatalog/Scenarios/SplitContainerExample.cs
+++ b/UICatalog/Scenarios/SplitContainerExample.cs
@@ -11,6 +11,9 @@ namespace UICatalog.Scenarios {
private MenuItem miVertical;
+ private MenuItem miShowBoth;
+ private MenuItem miShowPanel1;
+ private MenuItem miShowPanel2;
///
/// Setup the scenario.
@@ -21,10 +24,16 @@ namespace UICatalog.Scenarios {
Win.Title = this.GetName ();
Win.Y = 1;
+ Win.Add (new Label ("This is a SplitContainer with a minimum panel size of 2. Drag the splitter to resize:"));
+ Win.Add (new LineView (Orientation.Horizontal) { Y = 1 });
+
splitContainer = new SplitContainer {
+ Y = 2,
Width = Dim.Fill (),
Height = Dim.Fill (),
SplitterDistance = Pos.Percent (50), // TODO: get this to work with drag resizing and percents
+ Panel1MinSize = 2,
+ Panel2MinSize = 2,
};
@@ -52,12 +61,45 @@ namespace UICatalog.Scenarios {
miVertical = new MenuItem ("_Vertical", "", () => ToggleOrientation())
{
Checked = splitContainer.Orientation == Orientation.Vertical,
- CheckType = MenuItemCheckStyle.Checked
- }})
- });
+ CheckType = MenuItemCheckStyle.Checked
+ },
+ new MenuBarItem ("_Show", new MenuItem [] {
+ miShowBoth = new MenuItem ("Both", "",()=>{
+ splitContainer.Panel1Collapsed = false;
+ splitContainer.Panel2Collapsed = false;
+ UpdateShowMenuCheckedStates();
+ }),
+ miShowPanel1 = new MenuItem ("Panel1", "", () => {
+
+ splitContainer.Panel2Collapsed = true;
+ UpdateShowMenuCheckedStates();
+ }),
+ miShowPanel2 = new MenuItem ("Panel2", "", () => {
+ splitContainer.Panel1Collapsed = true;
+ UpdateShowMenuCheckedStates();
+ }),
+ })
+ }),
+
+ }) ;
+
+ UpdateShowMenuCheckedStates ();
+
Application.Top.Add (menu);
}
+ private void UpdateShowMenuCheckedStates ()
+ {
+ miShowBoth.Checked = (!splitContainer.Panel1Collapsed) && (!splitContainer.Panel2Collapsed);
+ miShowBoth.CheckType = MenuItemCheckStyle.Checked;
+
+ miShowPanel1.Checked = splitContainer.Panel2Collapsed;
+ miShowPanel1.CheckType = MenuItemCheckStyle.Checked;
+
+ miShowPanel2.Checked = splitContainer.Panel1Collapsed;
+ miShowPanel2.CheckType = MenuItemCheckStyle.Checked;
+ }
+
public void ToggleOrientation()
{
diff --git a/UnitTests/SplitContainerTests.cs b/UnitTests/SplitContainerTests.cs
index 81c7c4ce9..0d84dbf41 100644
--- a/UnitTests/SplitContainerTests.cs
+++ b/UnitTests/SplitContainerTests.cs
@@ -95,6 +95,53 @@ namespace UnitTests {
TestHelpers.AssertDriverContentsAre (looksLike, output);
}
+
+ [Fact, AutoInitShutdown]
+ public void TestSplitContainer_Vertical_Panel1MinSize_Absolute ()
+ {
+ var splitContainer = Get11By3SplitContainer ();
+
+ splitContainer.EnsureFocus ();
+ splitContainer.FocusFirst ();
+ splitContainer.Panel1MinSize = 6;
+
+ // distance is too small (below 6)
+ splitContainer.SplitterDistance = 2;
+
+ // Should bound the value to the minimum distance
+ Assert.Equal(6,splitContainer.SplitterDistance);
+
+ splitContainer.Redraw (splitContainer.Bounds);
+
+ // so should ignore the 2 distance and stick to 6
+ string looksLike =
+@"
+111111│2222
+ ◊
+ │ ";
+ TestHelpers.AssertDriverContentsAre (looksLike, output);
+
+ // Keyboard movement on splitter should have no effect because it
+ // would take us below the minimum splitter size
+ splitContainer.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ()));
+ splitContainer.SetNeedsDisplay ();
+ splitContainer.Redraw (splitContainer.Bounds);
+ TestHelpers.AssertDriverContentsAre (looksLike, output);
+
+ // but we can continue to move the splitter right if we want
+ splitContainer.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ()));
+ splitContainer.SetNeedsDisplay ();
+ splitContainer.Redraw (splitContainer.Bounds);
+
+ looksLike =
+@"
+1111111│222
+ ◊
+ │ ";
+
+ TestHelpers.AssertDriverContentsAre (looksLike, output);
+
+ }
[Fact, AutoInitShutdown]
public void TestSplitContainer_Horizontal_Focused ()
{
@@ -132,10 +179,53 @@ namespace UnitTests {
─────◊─────
22222222222";
TestHelpers.AssertDriverContentsAre (looksLike, output);
-
}
+ [Fact, AutoInitShutdown]
+ public void TestSplitContainer_Horizontal_Panel1MinSize_Absolute ()
+ {
+ var splitContainer = Get11By3SplitContainer ();
+
+ splitContainer.Orientation = Terminal.Gui.Graphs.Orientation.Horizontal;
+ splitContainer.EnsureFocus ();
+ splitContainer.FocusFirst ();
+ splitContainer.Panel1MinSize = 1;
+
+ // 0 should not be allowed because it brings us below minimum size of Panel1
+ splitContainer.SplitterDistance = 0;
+ Assert.Equal((Pos)1,splitContainer.SplitterDistance);
+
+ splitContainer.Redraw (splitContainer.Bounds);
+
+ string looksLike =
+@"
+11111111111
+─────◊─────
+22222222222";
+ TestHelpers.AssertDriverContentsAre (looksLike, output);
+
+ // Now move splitter line down (allowed
+ splitContainer.ProcessKey (new KeyEvent (Key.CursorDown, new KeyModifiers ()));
+ splitContainer.Redraw (splitContainer.Bounds);
+ looksLike =
+@"
+11111111111
+
+─────◊─────";
+ TestHelpers.AssertDriverContentsAre (looksLike, output);
+
+ // And up 2 (only 1 is allowed because of minimum size of 1 on panel1)
+ splitContainer.ProcessKey (new KeyEvent (Key.CursorUp, new KeyModifiers ()));
+ splitContainer.ProcessKey (new KeyEvent (Key.CursorUp, new KeyModifiers ()));
+ splitContainer.Redraw (splitContainer.Bounds);
+ looksLike =
+@"
+11111111111
+─────◊─────
+22222222222";
+ TestHelpers.AssertDriverContentsAre (looksLike, output);
+ }
private SplitContainer Get11By3SplitContainer ()
{
var container = new SplitContainer () {