mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-30 09:47:58 +01:00
Merge pull request #146 from tig/tznind-splitcontainer
Fixes & enhancments to SplitContainer
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
using System;
|
||||
using NStack;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Terminal.Gui.Graphs;
|
||||
|
||||
namespace Terminal.Gui {
|
||||
@@ -7,15 +11,12 @@ namespace Terminal.Gui {
|
||||
/// A <see cref="View"/> consisting of a moveable bar that divides
|
||||
/// the display area into 2 resizeable panels.
|
||||
/// </summary>
|
||||
public class SplitContainer : View {
|
||||
public class SplitContainer : FrameView {
|
||||
|
||||
private LineView splitterLine;
|
||||
private bool panel1Collapsed;
|
||||
private bool panel2Collapsed;
|
||||
private SplitContainerLineView splitterLine;
|
||||
private Pos splitterDistance = Pos.Percent (50);
|
||||
private Orientation orientation = Orientation.Vertical;
|
||||
private Pos panel1MinSize = 0;
|
||||
private Pos panel2MinSize = 0;
|
||||
private SplitterPanel [] splitterPanels = { new SplitterPanel(), new SplitterPanel() };
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the SplitContainer class.
|
||||
@@ -24,34 +25,29 @@ namespace Terminal.Gui {
|
||||
{
|
||||
splitterLine = new SplitContainerLineView (this);
|
||||
|
||||
this.Add (Panel1);
|
||||
this.Add (splitterPanels [0]);
|
||||
this.Add (splitterLine);
|
||||
this.Add (Panel2);
|
||||
this.Add (splitterPanels [1]);
|
||||
|
||||
Setup ();
|
||||
LayoutStarted += (e) => Setup ();
|
||||
|
||||
CanFocus = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The left or top panel of the <see cref="SplitContainer"/>
|
||||
/// (depending on <see cref="Orientation"/>). Add panel contents
|
||||
/// to this <see cref="View"/> using <see cref="View.Add(View)"/>.
|
||||
/// Gets the list of panels. Currently only supports 2 panels.
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The first item in the list is either the leftmost or topmost panel;
|
||||
/// the second item is either the rightmost or bottom panel
|
||||
/// (depending on <see cref="Orientation"/>)
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Add panel contents to the <see cref="SplitterPanel"/>s using <see cref="View.Add(View)"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
public View Panel1 { get; } = new View ();
|
||||
|
||||
/// <summary>
|
||||
/// The minimum size <see cref="Panel1"/> can be when adjusting
|
||||
/// <see cref="SplitterDistance"/>.
|
||||
/// </summary>
|
||||
public Pos Panel1MinSize {
|
||||
get { return panel1MinSize; }
|
||||
set {
|
||||
panel1MinSize = value;
|
||||
Setup ();
|
||||
}
|
||||
}
|
||||
|
||||
public List<SplitterPanel> Panels { get { return splitterPanels.ToList(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the <see cref="SplitterDistance"/> is changed
|
||||
@@ -66,59 +62,6 @@ namespace Terminal.Gui {
|
||||
SplitterMoved?.Invoke (this, new SplitterEventArgs (this, splitterDistance));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This determines if <see cref="Panel1"/> is collapsed.
|
||||
/// </summary>
|
||||
public bool Panel1Collapsed {
|
||||
get { return panel1Collapsed; }
|
||||
set {
|
||||
panel1Collapsed = value;
|
||||
if (value && panel2Collapsed) {
|
||||
panel2Collapsed = false;
|
||||
}
|
||||
|
||||
Setup ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The right or bottom panel of the <see cref="SplitContainer"/>
|
||||
/// (depending on <see cref="Orientation"/>). Add panel contents
|
||||
/// to this <see cref="View"/> using <see cref="View.Add(View)"/>
|
||||
/// </summary>
|
||||
public View Panel2 { get; } = new View ();
|
||||
|
||||
/// <summary>
|
||||
/// The minimum size <see cref="Panel2"/> can be when adjusting
|
||||
/// <see cref="SplitterDistance"/>.
|
||||
/// </summary>
|
||||
public Pos Panel2MinSize {
|
||||
get {
|
||||
return panel2MinSize;
|
||||
}
|
||||
|
||||
set {
|
||||
panel2MinSize = value;
|
||||
Setup ();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This determines if <see cref="Panel2"/> is collapsed.
|
||||
/// </summary>
|
||||
public bool Panel2Collapsed {
|
||||
get { return panel2Collapsed; }
|
||||
set {
|
||||
panel2Collapsed = value;
|
||||
if (value && panel1Collapsed) {
|
||||
panel1Collapsed = false;
|
||||
}
|
||||
Setup ();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Orientation of the dividing line (Horizontal or Vertical).
|
||||
/// </summary>
|
||||
@@ -130,7 +73,6 @@ namespace Terminal.Gui {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>Distance Horizontally or Vertically to the splitter line when
|
||||
/// neither panel is collapsed.
|
||||
@@ -164,85 +106,103 @@ namespace Terminal.Gui {
|
||||
Driver.SetAttribute (ColorScheme.Normal);
|
||||
Clear ();
|
||||
base.Redraw (bounds);
|
||||
|
||||
// Draw Splitter over Border (to get Ts)
|
||||
if (splitterLine.Visible) {
|
||||
splitterLine.Redraw (bounds);
|
||||
}
|
||||
|
||||
// Draw Titles over Border
|
||||
var screen = ViewToScreen (bounds);
|
||||
if (splitterPanels[0].Visible && splitterPanels[0].Title.Length > 0) {
|
||||
Driver.SetAttribute (splitterPanels[0].HasFocus ? ColorScheme.HotNormal : ColorScheme.Normal);
|
||||
Driver.DrawWindowTitle (new Rect (screen.X, screen.Y, splitterPanels[0].Frame.Width, 1), splitterPanels[0].Title, 0, 0, 0, 0);
|
||||
}
|
||||
if (splitterLine.Visible) {
|
||||
screen = ViewToScreen (splitterLine.Frame);
|
||||
} else {
|
||||
screen.X--;
|
||||
screen.Y--;
|
||||
}
|
||||
if (Orientation == Orientation.Horizontal) {
|
||||
if (splitterPanels[1].Visible && splitterPanels[1].Title.Length > 0) {
|
||||
|
||||
Driver.SetAttribute (splitterPanels[1].HasFocus ? ColorScheme.HotNormal : ColorScheme.Normal);
|
||||
Driver.DrawWindowTitle (new Rect (screen.X + 1, screen.Y + 1, splitterPanels[1].Bounds.Width, 1), splitterPanels[1].Title, 0, 0, 0, 0);
|
||||
}
|
||||
} else {
|
||||
if (splitterPanels[1].Visible && splitterPanels[1].Title.Length > 0) {
|
||||
Driver.SetAttribute (splitterPanels[1].HasFocus ? ColorScheme.HotNormal : ColorScheme.Normal);
|
||||
Driver.DrawWindowTitle (new Rect (screen.X + 1, screen.Y + 1, splitterPanels[1].Bounds.Width, 1), splitterPanels[1].Title, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Setup ()
|
||||
{
|
||||
splitterLine.Orientation = Orientation;
|
||||
splitterLine.Text = splitterPanels[1].Title;
|
||||
|
||||
if (panel1Collapsed || panel2Collapsed) {
|
||||
SetupForCollapsedPanel ();
|
||||
if (!splitterPanels[0].Visible || !splitterPanels[1].Visible) {
|
||||
View toFullSize = !splitterPanels[0].Visible ? splitterPanels[1] : splitterPanels[0];
|
||||
|
||||
splitterLine.Visible = false;
|
||||
|
||||
toFullSize.X = 0;
|
||||
toFullSize.Y = 0;
|
||||
toFullSize.Width = Dim.Fill ();
|
||||
toFullSize.Height = Dim.Fill ();
|
||||
} else {
|
||||
SetupForNormal ();
|
||||
splitterLine.Visible = true;
|
||||
|
||||
splitterDistance = BoundByMinimumSizes (splitterDistance);
|
||||
|
||||
splitterPanels[0].X = 0;
|
||||
splitterPanels[0].Y = 0;
|
||||
|
||||
splitterPanels[1].Width = Dim.Fill ();
|
||||
splitterPanels[1].Height = Dim.Fill ();
|
||||
|
||||
switch (Orientation) {
|
||||
case Orientation.Horizontal:
|
||||
splitterLine.X = -1;
|
||||
splitterLine.Y = splitterDistance;
|
||||
splitterLine.Width = Dim.Fill () + 1;
|
||||
splitterLine.Height = 1;
|
||||
splitterLine.LineRune = Driver.HLine;
|
||||
|
||||
splitterPanels[0].Width = Dim.Fill ();
|
||||
splitterPanels[0].Height = new Dim.DimFunc (() =>
|
||||
splitterDistance.Anchor (Bounds.Height)) - 1;
|
||||
|
||||
splitterPanels[1].Y = Pos.Bottom (splitterLine);
|
||||
splitterPanels[1].X = 0;
|
||||
break;
|
||||
|
||||
case Orientation.Vertical:
|
||||
splitterLine.X = splitterDistance;
|
||||
splitterLine.Y = -1;
|
||||
splitterLine.Width = 1;
|
||||
splitterLine.Height = Dim.Fill () + 1;
|
||||
splitterLine.LineRune = Driver.VLine;
|
||||
|
||||
splitterPanels[0].Height = Dim.Fill ();
|
||||
splitterPanels[0].Width = new Dim.DimFunc (() =>
|
||||
splitterDistance.Anchor (Bounds.Width)) - 1;
|
||||
|
||||
splitterPanels[1].X = Pos.Right (splitterLine);
|
||||
splitterPanels[1].Y = 0;
|
||||
break;
|
||||
|
||||
default: throw new ArgumentOutOfRangeException (nameof (orientation));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupForNormal ()
|
||||
{
|
||||
// Ensure all our component views are here
|
||||
// (e.g. if we are transitioning from a collapsed state)
|
||||
|
||||
if (!this.Subviews.Contains (splitterLine)) {
|
||||
this.Add (splitterLine);
|
||||
}
|
||||
if (!this.Subviews.Contains (Panel1)) {
|
||||
this.Add (Panel1);
|
||||
}
|
||||
if (!this.Subviews.Contains (Panel2)) {
|
||||
this.Add (Panel2);
|
||||
}
|
||||
|
||||
splitterDistance = BoundByMinimumSizes (splitterDistance);
|
||||
|
||||
switch (Orientation) {
|
||||
case Orientation.Horizontal:
|
||||
splitterLine.X = 0;
|
||||
splitterLine.Y = splitterDistance;
|
||||
splitterLine.Width = Dim.Fill ();
|
||||
splitterLine.Height = 1;
|
||||
splitterLine.LineRune = Driver.HLine;
|
||||
|
||||
this.Panel1.X = 0;
|
||||
this.Panel1.Y = 0;
|
||||
this.Panel1.Width = Dim.Fill ();
|
||||
this.Panel1.Height = new Dim.DimFunc (() =>
|
||||
splitterDistance.Anchor (Bounds.Height));
|
||||
|
||||
this.Panel2.Y = Pos.Bottom (splitterLine);
|
||||
this.Panel2.X = 0;
|
||||
this.Panel2.Width = Dim.Fill ();
|
||||
this.Panel2.Height = Dim.Fill ();
|
||||
break;
|
||||
|
||||
case Orientation.Vertical:
|
||||
splitterLine.X = splitterDistance;
|
||||
splitterLine.Y = 0;
|
||||
splitterLine.Width = 1;
|
||||
splitterLine.Height = Dim.Fill ();
|
||||
splitterLine.LineRune = Driver.VLine;
|
||||
|
||||
this.Panel1.X = 0;
|
||||
this.Panel1.Y = 0;
|
||||
this.Panel1.Height = Dim.Fill ();
|
||||
this.Panel1.Width = new Dim.DimFunc (() =>
|
||||
splitterDistance.Anchor (Bounds.Width));
|
||||
|
||||
this.Panel2.X = Pos.Right (splitterLine);
|
||||
this.Panel2.Y = 0;
|
||||
this.Panel2.Width = Dim.Fill ();
|
||||
this.Panel2.Height = Dim.Fill ();
|
||||
break;
|
||||
|
||||
default: throw new ArgumentOutOfRangeException (nameof (orientation));
|
||||
};
|
||||
|
||||
this.LayoutSubviews ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Considers <paramref name="pos"/> as a candidate for <see cref="splitterDistance"/>
|
||||
/// then either returns (if valid) or returns adjusted if invalid with respect to
|
||||
/// <see cref="Panel1MinSize"/> or <see cref="Panel2MinSize"/>.
|
||||
/// then either returns (if valid) or returns adjusted if invalid with respect to the
|
||||
/// <see cref="SplitterPanel.MinSize"/> of the panels.
|
||||
/// </summary>
|
||||
/// <param name="pos"></param>
|
||||
/// <returns></returns>
|
||||
@@ -258,48 +218,70 @@ namespace Terminal.Gui {
|
||||
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) {
|
||||
// bad position because not enough space for splitterPanels[0]
|
||||
if (idealPosition < splitterPanels [0].MinSize.Anchor (availableSpace)) {
|
||||
|
||||
// TODO: we should preserve Absolute/Percent status here not just force it to absolute
|
||||
return (Pos)Math.Min (panel1MinSizeAbs, availableSpace);
|
||||
return (Pos)Math.Min (splitterPanels [0].MinSize.Anchor (availableSpace), availableSpace);
|
||||
}
|
||||
|
||||
// bad position because not enough space for panel2
|
||||
if (availableSpace - idealPosition <= panel2MinSizeAbs) {
|
||||
// bad position because not enough space for splitterPanels[1]
|
||||
if (availableSpace - idealPosition <= splitterPanels [1].MinSize.Anchor (availableSpace)) {
|
||||
|
||||
// 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);
|
||||
return (Pos)Math.Max (availableSpace - (splitterPanels [1].MinSize.Anchor (availableSpace) + 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;
|
||||
/// <summary>
|
||||
/// A panel within a <see cref="SplitterPanel"/>.
|
||||
/// </summary>
|
||||
public class SplitterPanel : View {
|
||||
Pos minSize = 2;
|
||||
|
||||
if (this.Subviews.Contains (splitterLine)) {
|
||||
this.Remove (splitterLine);
|
||||
}
|
||||
if (this.Subviews.Contains (toRemove)) {
|
||||
this.Remove (toRemove);
|
||||
}
|
||||
if (!this.Subviews.Contains (toFullSize)) {
|
||||
this.Add (toFullSize);
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum size for the panel.
|
||||
/// </summary>
|
||||
public Pos MinSize { get => minSize;
|
||||
set {
|
||||
minSize = value;
|
||||
SuperView?.SetNeedsLayout ();
|
||||
}
|
||||
}
|
||||
|
||||
toFullSize.X = 0;
|
||||
toFullSize.Y = 0;
|
||||
toFullSize.Width = Dim.Fill ();
|
||||
toFullSize.Height = Dim.Fill ();
|
||||
ustring title = ustring.Empty;
|
||||
/// <summary>
|
||||
/// The title to be displayed for this <see cref="SplitterPanel"/>. The title will be rendered
|
||||
/// on the top border aligned to the left of the panel.
|
||||
/// </summary>
|
||||
/// <value>The title.</value>
|
||||
public ustring Title {
|
||||
get => title;
|
||||
set {
|
||||
title = value;
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Redraw (Rect bounds)
|
||||
{
|
||||
Driver.SetAttribute (ColorScheme.Normal);
|
||||
base.Redraw (bounds);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnVisibleChanged ()
|
||||
{
|
||||
base.OnVisibleChanged ();
|
||||
SuperView?.SetNeedsLayout ();
|
||||
}
|
||||
}
|
||||
|
||||
private class SplitContainerLineView : LineView {
|
||||
@@ -309,7 +291,6 @@ namespace Terminal.Gui {
|
||||
Pos dragOrignalPos;
|
||||
Point? moveRuneRenderLocation;
|
||||
|
||||
// TODO: Make focusable and allow moving with keyboard
|
||||
public SplitContainerLineView (SplitContainer parent)
|
||||
{
|
||||
CanFocus = true;
|
||||
@@ -337,10 +318,19 @@ namespace Terminal.Gui {
|
||||
AddKeyBinding (Key.CursorLeft, Command.Left);
|
||||
AddKeyBinding (Key.CursorUp, Command.LineUp);
|
||||
AddKeyBinding (Key.CursorDown, Command.LineDown);
|
||||
|
||||
LayoutStarted += (e) => {
|
||||
moveRuneRenderLocation = null;
|
||||
if (Orientation == Orientation.Horizontal) {
|
||||
StartingAnchor = Driver.LeftTee;
|
||||
EndingAnchor = Driver.RightTee;
|
||||
} else {
|
||||
StartingAnchor = Driver.TopTee;
|
||||
EndingAnchor = Driver.BottomTee;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
///<inheritdoc/>
|
||||
public override bool ProcessKey (KeyEvent kb)
|
||||
{
|
||||
if (!CanFocus || !HasFocus) {
|
||||
@@ -353,10 +343,13 @@ namespace Terminal.Gui {
|
||||
|
||||
return base.ProcessKey (kb);
|
||||
}
|
||||
|
||||
public override void PositionCursor ()
|
||||
{
|
||||
base.PositionCursor ();
|
||||
Move (this.Bounds.Width / 2, this.Bounds.Height / 2);
|
||||
var location = moveRuneRenderLocation ??
|
||||
new Point (Bounds.Width / 2, Bounds.Height / 2);
|
||||
Move (location.X, location.Y);
|
||||
}
|
||||
|
||||
public override bool OnEnter (View view)
|
||||
@@ -366,21 +359,19 @@ namespace Terminal.Gui {
|
||||
|
||||
return base.OnEnter (view);
|
||||
}
|
||||
|
||||
public override void Redraw (Rect bounds)
|
||||
{
|
||||
base.Redraw (bounds);
|
||||
|
||||
if (CanFocus && HasFocus) {
|
||||
|
||||
var location = moveRuneRenderLocation ??
|
||||
new Point (Bounds.Width / 2, Bounds.Height / 2);
|
||||
|
||||
AddRune (location.X, location.Y, Driver.Diamond);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///<inheritdoc/>
|
||||
public override bool MouseEvent (MouseEvent mouseEvent)
|
||||
{
|
||||
if (!CanFocus) {
|
||||
@@ -397,6 +388,12 @@ namespace Terminal.Gui {
|
||||
dragPosition = new Point (mouseEvent.X, mouseEvent.Y);
|
||||
dragOrignalPos = Orientation == Orientation.Horizontal ? Y : X;
|
||||
Application.GrabMouse (this);
|
||||
|
||||
if (Orientation == Orientation.Horizontal) {
|
||||
|
||||
} else {
|
||||
moveRuneRenderLocation = new Point (0, Math.Max (1, Math.Min (Bounds.Height - 2, mouseEvent.Y)));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -414,7 +411,7 @@ namespace Terminal.Gui {
|
||||
} else {
|
||||
int dx = mouseEvent.X - dragPosition.Value.X;
|
||||
parent.SplitterDistance = Offset (X, dx);
|
||||
moveRuneRenderLocation = new Point (0, mouseEvent.Y);
|
||||
moveRuneRenderLocation = new Point (0, Math.Max (1, Math.Min (Bounds.Height - 2, mouseEvent.Y)));
|
||||
}
|
||||
|
||||
parent.SetNeedsDisplay ();
|
||||
@@ -431,11 +428,12 @@ namespace Terminal.Gui {
|
||||
dragOrignalPos,
|
||||
Orientation == Orientation.Horizontal ? Y : X);
|
||||
dragPosition = null;
|
||||
moveRuneRenderLocation = null;
|
||||
//moveRuneRenderLocation = null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool MoveSplitter (int distanceX, int distanceY)
|
||||
{
|
||||
if (Orientation == Orientation.Vertical) {
|
||||
@@ -461,7 +459,6 @@ namespace Terminal.Gui {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Pos Offset (Pos pos, int delta)
|
||||
{
|
||||
var posAbsolute = pos.Anchor (Orientation == Orientation.Horizontal ?
|
||||
|
||||
@@ -5,54 +5,51 @@ using Terminal.Gui.Graphs;
|
||||
namespace UICatalog.Scenarios {
|
||||
[ScenarioMetadata (Name: "Split Container", Description: "Demonstrates the SplitContainer functionality")]
|
||||
[ScenarioCategory ("Controls")]
|
||||
[ScenarioCategory ("LineView")]
|
||||
public class SplitContainerExample : Scenario {
|
||||
|
||||
private SplitContainer splitContainer;
|
||||
|
||||
|
||||
private MenuItem miVertical;
|
||||
private MenuItem miShowBoth;
|
||||
private MenuItem miShowPanel1;
|
||||
private MenuItem miShowPanel2;
|
||||
private MenuItem miShowNeither;
|
||||
|
||||
/// <summary>
|
||||
/// Setup the scenario.
|
||||
/// </summary>
|
||||
public override void Setup ()
|
||||
{
|
||||
// Scenario Window's.
|
||||
// Scenario Windows.
|
||||
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 });
|
||||
Win.Add (new Label ("This is a SplitContainer with a minimum panel size of 4. Drag the splitter to resize:"));
|
||||
|
||||
splitContainer = new SplitContainer {
|
||||
Y = 2,
|
||||
Width = Dim.Fill (),
|
||||
Height = Dim.Fill (),
|
||||
X = 2,
|
||||
Width = Dim.Fill () - 2,
|
||||
Height = Dim.Fill () - 1,
|
||||
SplitterDistance = Pos.Percent (50), // TODO: get this to work with drag resizing and percents
|
||||
Panel1MinSize = 2,
|
||||
Panel2MinSize = 2,
|
||||
};
|
||||
|
||||
splitContainer.Panels [0].MinSize = 4;
|
||||
splitContainer.Panels [1].MinSize = 4;
|
||||
|
||||
Label lbl1;
|
||||
splitContainer.Panel1.Add (new Label ("Hello"));
|
||||
splitContainer.Panel1.Add (lbl1 = new Label ("Type Something:"){Y=2});
|
||||
splitContainer.Panel1.Add (new TextField (){Width = Dim.Fill(),Y=2,X=Pos.Right(lbl1)+1});
|
||||
|
||||
Label lbl2;
|
||||
splitContainer.Panel2.Add (new Label ("World"));
|
||||
splitContainer.Panel2.Add (lbl2 = new Label ("Type Here Too:"){Y=2});
|
||||
splitContainer.Panel2.Add (new TextField (){Width = Dim.Fill(),Y=2,X=Pos.Right(lbl2)+1});
|
||||
splitContainer.Panel2.Add (new Label ("Here is a Text box:") { Y = 4 });
|
||||
splitContainer.Panel2.Add (new TextView () { Y = 5, Width = Dim.Fill(), Height = Dim.Fill(), AllowsTab = false});
|
||||
splitContainer.Panels [0].Title = "Hello";
|
||||
splitContainer.Panels [0].Add (lbl1 = new Label ("Type Something:") { Y = 1 });
|
||||
splitContainer.Panels [0].Add (new TextField () { Width = Dim.Fill (), Y = 1, X = Pos.Right (lbl1) + 1 });
|
||||
|
||||
Label lbl2;
|
||||
splitContainer.Panels [1].Title = "World";
|
||||
splitContainer.Panels [1].Add (lbl2 = new Label ("Type Here Too:") { Y = 1 });
|
||||
splitContainer.Panels [1].Add (new TextField () { Width = Dim.Fill (), Y = 1, X = Pos.Right (lbl2) + 1 });
|
||||
splitContainer.Panels [1].Add (new Label ("Here is a Text box:") { Y = 3 });
|
||||
splitContainer.Panels [1].Add (new TextView () { Y = 4, Width = Dim.Fill (), Height = Dim.Fill (), AllowsTab = false });
|
||||
|
||||
Win.Add (splitContainer);
|
||||
|
||||
|
||||
var menu = new MenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("_File", new MenuItem [] {
|
||||
new MenuItem ("_Quit", "", () => Quit()),
|
||||
@@ -65,46 +62,53 @@ namespace UICatalog.Scenarios {
|
||||
},
|
||||
new MenuBarItem ("_Show", new MenuItem [] {
|
||||
miShowBoth = new MenuItem ("Both", "",()=>{
|
||||
splitContainer.Panel1Collapsed = false;
|
||||
splitContainer.Panel2Collapsed = false;
|
||||
splitContainer.Panels [0].Visible = true;
|
||||
splitContainer.Panels [1].Visible = true;
|
||||
UpdateShowMenuCheckedStates();
|
||||
}),
|
||||
miShowPanel1 = new MenuItem ("Panel1", "", () => {
|
||||
|
||||
splitContainer.Panel2Collapsed = true;
|
||||
miShowPanel1 = new MenuItem ("Panel 1", "", () => {
|
||||
splitContainer.Panels [0].Visible = true;
|
||||
splitContainer.Panels [1].Visible = false;
|
||||
UpdateShowMenuCheckedStates();
|
||||
}),
|
||||
miShowPanel2 = new MenuItem ("Panel2", "", () => {
|
||||
splitContainer.Panel1Collapsed = true;
|
||||
miShowPanel2 = new MenuItem ("Panel 2", "", () => {
|
||||
splitContainer.Panels [0].Visible = false;
|
||||
splitContainer.Panels [1].Visible = true;
|
||||
UpdateShowMenuCheckedStates();
|
||||
}),
|
||||
}),
|
||||
miShowNeither = new MenuItem ("Neither", "",()=>{
|
||||
splitContainer.Panels [0].Visible = false;
|
||||
splitContainer.Panels [1].Visible = false;
|
||||
UpdateShowMenuCheckedStates();
|
||||
}),
|
||||
})
|
||||
}),
|
||||
|
||||
}) ;
|
||||
});
|
||||
|
||||
UpdateShowMenuCheckedStates ();
|
||||
|
||||
|
||||
Application.Top.Add (menu);
|
||||
}
|
||||
|
||||
private void UpdateShowMenuCheckedStates ()
|
||||
{
|
||||
miShowBoth.Checked = (!splitContainer.Panel1Collapsed) && (!splitContainer.Panel2Collapsed);
|
||||
miShowBoth.Checked = (splitContainer.Panels [0].Visible) && (splitContainer.Panels [1].Visible);
|
||||
miShowBoth.CheckType = MenuItemCheckStyle.Checked;
|
||||
|
||||
miShowPanel1.Checked = splitContainer.Panel2Collapsed;
|
||||
miShowPanel1.Checked = splitContainer.Panels [0].Visible && !splitContainer.Panels [1].Visible;
|
||||
miShowPanel1.CheckType = MenuItemCheckStyle.Checked;
|
||||
|
||||
miShowPanel2.Checked = splitContainer.Panel1Collapsed;
|
||||
miShowPanel2.Checked = !splitContainer.Panels [0].Visible && splitContainer.Panels [1].Visible;
|
||||
miShowPanel2.CheckType = MenuItemCheckStyle.Checked;
|
||||
|
||||
miShowNeither.Checked = (!splitContainer.Panels [0].Visible) && (!splitContainer.Panels [1].Visible);
|
||||
miShowNeither.CheckType = MenuItemCheckStyle.Checked;
|
||||
}
|
||||
|
||||
public void ToggleOrientation()
|
||||
public void ToggleOrientation ()
|
||||
{
|
||||
|
||||
miVertical.Checked = !miVertical.Checked;
|
||||
splitContainer.Orientation = miVertical.Checked ? Orientation.Vertical : Orientation.Horizontal;
|
||||
splitContainer.Orientation = miVertical.Checked ? Orientation.Vertical : Orientation.Horizontal;
|
||||
}
|
||||
|
||||
private void Quit ()
|
||||
|
||||
@@ -151,8 +151,7 @@ namespace UICatalog {
|
||||
public MenuItem miIsMouseDisabled;
|
||||
public MenuItem miHeightAsBuffer;
|
||||
|
||||
public FrameView ContentPane;
|
||||
public SplitContainer SplitContainer;
|
||||
public SplitContainer ContentPane;
|
||||
public ListView CategoryListView;
|
||||
public ListView ScenarioListView;
|
||||
|
||||
@@ -208,23 +207,17 @@ namespace UICatalog {
|
||||
DriverName,
|
||||
};
|
||||
|
||||
ContentPane = new FrameView ("Categories") {
|
||||
ContentPane = new SplitContainer () {
|
||||
X = 0,
|
||||
Y = 1, // for menu
|
||||
Width = Dim.Fill (),
|
||||
Height = Dim.Fill (1),
|
||||
CanFocus = true,
|
||||
Shortcut = Key.CtrlMask | Key.C
|
||||
};
|
||||
|
||||
ContentPane.ShortcutAction = () => ContentPane.SetFocus ();
|
||||
|
||||
SplitContainer = new SplitContainer {
|
||||
Width = Dim.Fill (0),
|
||||
Height = Dim.Fill (0),
|
||||
Shortcut = Key.CtrlMask | Key.C,
|
||||
SplitterDistance = 25
|
||||
};
|
||||
|
||||
ContentPane.ShortcutAction = () => ContentPane.SetFocus ();
|
||||
|
||||
CategoryListView = new ListView (_categories) {
|
||||
X = 0,
|
||||
Y = 0,
|
||||
@@ -237,16 +230,9 @@ namespace UICatalog {
|
||||
ScenarioListView.SetFocus ();
|
||||
};
|
||||
CategoryListView.SelectedItemChanged += CategoryListView_SelectedChanged;
|
||||
ContentPane.Add (SplitContainer);
|
||||
|
||||
SplitContainer.Panel1.Add (CategoryListView);
|
||||
|
||||
SetTitlePaddingToAlignScenariosHeader ();
|
||||
|
||||
SplitContainer.SplitterMoved += (s, e) => {
|
||||
SetTitlePaddingToAlignScenariosHeader ();
|
||||
};
|
||||
|
||||
ContentPane.Panels [0].Title = "Categories";
|
||||
ContentPane.Panels [0].Add (CategoryListView);
|
||||
|
||||
ScenarioListView = new ListView () {
|
||||
X = 0,
|
||||
@@ -258,11 +244,14 @@ namespace UICatalog {
|
||||
};
|
||||
|
||||
ScenarioListView.OpenSelectedItem += ScenarioListView_OpenSelectedItem;
|
||||
SplitContainer.Panel2.Add (ScenarioListView);
|
||||
|
||||
ContentPane.Panels [1].Title = "Scenarios";
|
||||
ContentPane.Panels [1].Add (ScenarioListView);
|
||||
|
||||
KeyDown += KeyDownHandler;
|
||||
Add (MenuBar);
|
||||
Add (ContentPane);
|
||||
|
||||
Add (StatusBar);
|
||||
|
||||
Loaded += LoadedHandler;
|
||||
@@ -271,22 +260,7 @@ namespace UICatalog {
|
||||
CategoryListView.SelectedItem = _cachedCategoryIndex;
|
||||
ScenarioListView.SelectedItem = _cachedScenarioIndex;
|
||||
}
|
||||
|
||||
private void SetTitlePaddingToAlignScenariosHeader ()
|
||||
{
|
||||
// Pos.Anchor is internal so we have to use reflection to access it
|
||||
var anchor = typeof (Pos).GetMethod ("Anchor", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
var splitterWidth = (int)anchor.Invoke (
|
||||
SplitContainer.SplitterDistance, new object [] { SplitContainer.Bounds.Width}
|
||||
);
|
||||
|
||||
var newTitle = $"Categories ({ContentPane.ShortcutTag})";
|
||||
newTitle = newTitle.PadRight (splitterWidth + 1, (char)Driver.HLine);
|
||||
newTitle += "Scenarios";
|
||||
|
||||
ContentPane.Title = newTitle;
|
||||
}
|
||||
|
||||
|
||||
void LoadedHandler ()
|
||||
{
|
||||
Application.HeightAsBuffer = _heightAsBuffer;
|
||||
|
||||
Reference in New Issue
Block a user