This commit is contained in:
Tig
2024-06-28 22:27:50 -07:00
parent 13cbdabd10
commit f8ede1283b
10 changed files with 282 additions and 272 deletions

View File

@@ -0,0 +1,23 @@
namespace Terminal.Gui;
/// <summary>
/// Interface declaring common functionality useful for designer implementations.
/// </summary>
public interface IDesignable
{
/// <summary>
/// Causes the View to enable design-time mode. This typically means that the view will load demo data and
/// be configured to allow for design-time manipulation.
/// </summary>
/// <param name="context">Optional arbitrary, View-specific, context.</param>
/// <typeparam name="TContext">A non-null type for <paramref name="context"/>.</typeparam>
/// <returns><see langword="true"/> if the view successfully loaded demo data.</returns>
public bool EnableForDesign<TContext> (in TContext context) where TContext : notnull => EnableForDesign ();
/// <summary>
/// Causes the View to enable design-time mode. This typically means that the view will load demo data and
/// be configured to allow for design-time manipulation.
/// </summary>
/// <returns><see langword="true"/> if the view successfully loaded demo data.</returns>
public bool EnableForDesign () => false;
}

View File

@@ -27,7 +27,7 @@ namespace Terminal.Gui;
/// invoked repeatedly while the button is pressed.
/// </para>
/// </remarks>
public class Button : View
public class Button : View, IDesignable
{
private readonly Rune _leftBracket;
private readonly Rune _leftDefault;
@@ -190,4 +190,12 @@ public class Button : View
}
}
}
/// <inheritdoc />
public bool EnableForDesign ()
{
Title = "_Button";
return true;
}
}

View File

@@ -11,7 +11,7 @@ using System.ComponentModel;
namespace Terminal.Gui;
/// <summary>Provides a drop-down list of items the user can select from.</summary>
public class ComboBox : View
public class ComboBox : View, IDesignable
{
private readonly ComboListView _listview;
private readonly int _minimumHeight = 2;
@@ -243,7 +243,7 @@ public class ComboBox : View
public event EventHandler Expanded;
/// <inheritdoc/>
protected internal override bool OnMouseEvent (MouseEvent me)
protected internal override bool OnMouseEvent (MouseEvent me)
{
if (me.Position.X == Viewport.Right - 1
&& me.Position.Y == Viewport.Top
@@ -813,7 +813,7 @@ public class ComboBox : View
set => _hideDropdownListOnClick = WantContinuousButtonPressed = value;
}
protected internal override bool OnMouseEvent (MouseEvent me)
protected internal override bool OnMouseEvent (MouseEvent me)
{
var res = false;
bool isMousePositionValid = IsMousePositionValid (me);
@@ -983,4 +983,14 @@ public class ComboBox : View
AddCommand (Command.LineUp, () => _container.MoveUpList ());
}
}
/// <inheritdoc />
public bool EnableForDesign ()
{
var source = new ObservableCollection<string> (["Combo Item 1", "Combo Item two", "Combo Item Quattro", "Last Combo Item"]);
SetSource (source);
Height = Dim.Auto (DimAutoStyle.Content, minimumContentDim: source.Count + 1);
return true;
}
}

View File

@@ -101,7 +101,7 @@ public interface IListDataSource: IDisposable
/// first item that starts with what the user types will be selected.
/// </para>
/// </remarks>
public class ListView : View
public class ListView : View, IDesignable
{
private bool _allowsMarking;
private bool _allowsMultipleSelection = true;
@@ -921,6 +921,15 @@ public class ListView : View
Source.SuspendCollectionChangedEvent = false;
}
}
/// <inheritdoc />
public bool EnableForDesign ()
{
var source = new ListWrapper<string> (["List Item 1", "List Item two", "List Item Quattro", "Last List Item"]);
Source = source;
return true;
}
}
/// <summary>

View File

@@ -34,7 +34,7 @@ namespace Terminal.Gui;
/// duplicates a shortcut (e.g. _File and Alt-F), the hot key wins.
/// </para>
/// </remarks>
public class MenuBar : View
public class MenuBar : View, IDesignable
{
// Spaces before the Title
private static readonly int _leftPadding = 1;
@@ -1591,4 +1591,177 @@ public class MenuBar : View
}
#endregion Mouse Handling
/// <inheritdoc />
public bool EnableForDesign<TContext> (in TContext context) where TContext : notnull
{
if (context is not Func<string, bool> actionFn)
{
actionFn = (s) => true;
}
Menus =
[
new MenuBarItem (
"_File",
new MenuItem []
{
new (
"_New",
"",
() => actionFn ("New"),
null,
null,
KeyCode.CtrlMask | KeyCode.N
),
new (
"_Open",
"",
() => actionFn ("Open"),
null,
null,
KeyCode.CtrlMask | KeyCode.O
),
new (
"_Save",
"",
() => actionFn ("Save"),
null,
null,
KeyCode.CtrlMask | KeyCode.S
),
null,
// Don't use Application.Quit so we can disambiguate between quitting and closing the toplevel
new (
"_Quit",
"",
() => actionFn ("Quit"),
null,
null,
KeyCode.CtrlMask | KeyCode.Q
)
}
),
new MenuBarItem (
"_Edit",
new MenuItem []
{
new (
"_Copy",
"",
() => actionFn ("Copy"),
null,
null,
KeyCode.CtrlMask | KeyCode.C
),
new (
"C_ut",
"",
() => actionFn ("Cut"),
null,
null,
KeyCode.CtrlMask | KeyCode.X
),
new (
"_Paste",
"",
() => actionFn ("Paste"),
null,
null,
KeyCode.CtrlMask | KeyCode.V
),
new MenuBarItem (
"_Find and Replace",
new MenuItem []
{
new (
"F_ind",
"",
() => actionFn ("Find"),
null,
null,
KeyCode.CtrlMask | KeyCode.F
),
new (
"_Replace",
"",
() => actionFn ("Replace"),
null,
null,
KeyCode.CtrlMask | KeyCode.H
),
new MenuBarItem (
"_3rd Level",
new MenuItem []
{
new (
"_1st",
"",
() => actionFn (
"1"
),
null,
null,
KeyCode.F1
),
new (
"_2nd",
"",
() => actionFn (
"2"
),
null,
null,
KeyCode.F2
)
}
),
new MenuBarItem (
"_4th Level",
new MenuItem []
{
new (
"_5th",
"",
() => actionFn (
"5"
),
null,
null,
KeyCode.CtrlMask
| KeyCode.D5
),
new (
"_6th",
"",
() => actionFn (
"6"
),
null,
null,
KeyCode.CtrlMask
| KeyCode.D6
)
}
)
}
),
new (
"_Select All",
"",
() => actionFn ("Select All"),
null,
null,
KeyCode.CtrlMask
| KeyCode.ShiftMask
| KeyCode.S
)
}
),
new MenuBarItem ("_About", "Top-Level", () => actionFn ("About"))
];
return true;
}
}

View File

@@ -35,7 +35,7 @@ public enum ProgressBarFormat
/// <see cref="Pulse"/> method is called. Call <see cref="Pulse"/> repeatedly as progress is made.
/// </para>
/// </remarks>
public class ProgressBar : View
public class ProgressBar : View, IDesignable
{
private int [] _activityPos;
private bool _bidirectionalMarquee = true;
@@ -277,4 +277,13 @@ public class ProgressBar : View
_fraction = 0;
Initialized += ProgressBar_Initialized;
}
/// <inheritdoc />
public bool EnableForDesign ()
{
Width = Dim.Fill ();
Height = Dim.Auto (DimAutoStyle.Text, minimumContentDim: 1);
Fraction = 0.75f;
return true;
}
}

View File

@@ -1,7 +1,7 @@
namespace Terminal.Gui;
/// <summary>Displays a group of labels each with a selected indicator. Only one of those can be selected at a given time.</summary>
public class RadioGroup : View
public class RadioGroup : View, IDesignable
{
private int _cursor;
private List<(int pos, int length)> _horizontal;
@@ -229,32 +229,6 @@ public class RadioGroup : View
}
}
/// <inheritdoc/>
public override string Text
{
get
{
if (_radioLabels.Count == 0)
{
return string.Empty;
}
// Return labels as a CSV string
return string.Join (",", _radioLabels);
}
set
{
if (string.IsNullOrEmpty (value))
{
RadioLabels = [];
}
else
{
RadioLabels = value.Split (',').Select (x => x.Trim ()).ToArray ();
}
}
}
/// <summary>The currently selected item from the list of radio labels</summary>
/// <value>The selected.</value>
public int SelectedItem
@@ -487,4 +461,11 @@ public class RadioGroup : View
break;
}
}
/// <inheritdoc />
public bool EnableForDesign ()
{
RadioLabels = new [] { "Option _1", "Option _2", "Option _3" };
return true;
}
}

View File

@@ -348,49 +348,17 @@ public class AllViewsTester : Scenario
view.ColorScheme = Colors.ColorSchemes ["Base"];
}
// If the view supports a Text property, set it so we have something to look at
if (view.GetType ().GetProperty ("Text") != null)
if (view is IDesignable designable)
{
try
{
view.GetType ()
.GetProperty ("Text")
?.GetSetMethod ()
?.Invoke (view, new [] { _demoText });
}
catch (TargetInvocationException e)
{
MessageBox.ErrorQuery ("Exception", e.InnerException.Message, "Ok");
view = null;
}
}
// If the view supports a Title property, set it so we have something to look at
if (view != null && view.GetType ().GetProperty ("Title") != null)
{
if (view.GetType ().GetProperty ("Title")!.PropertyType == typeof (string))
{
view?.GetType ()
.GetProperty ("Title")
?.GetSetMethod ()
?.Invoke (view, new [] { "Test Title" });
}
else
{
view?.GetType ()
.GetProperty ("Title")
?.GetSetMethod ()
?.Invoke (view, new [] { "Test Title" });
}
}
// If the view supports a Source property, set it so we have something to look at
if (view != null && view.GetType ().GetProperty ("Source") != null && view.GetType ().GetProperty ("Source").PropertyType == typeof (IListDataSource))
{
var source = new ListWrapper<string> (["Test Text #1", "Test Text #2", "Test Text #3"]);
view?.GetType ().GetProperty ("Source")?.GetSetMethod ()?.Invoke (view, [source]);
designable.EnableForDesign (_demoText);
}
else
{
view.Text = _demoText;
view.Title = "_Test Title";
}
// TODO: Add IOrientation so this doesn't require reflection
// If the view supports a Title property, set it so we have something to look at
if (view?.GetType ().GetProperty ("Orientation") is { } prop)
{

View File

@@ -3,7 +3,7 @@ using Terminal.Gui;
namespace UICatalog.Scenarios;
[ScenarioMetadata ("MenuBar", "Demonstrates the MenuBar using the same menu used in unit tests.")]
[ScenarioMetadata ("MenuBar", "Demonstrates the MenuBar using the demo menu.")]
[ScenarioCategory ("Controls")]
[ScenarioCategory ("Menus")]
public class MenuBarScenario : Scenario
@@ -14,186 +14,6 @@ public class MenuBarScenario : Scenario
private Label _lastAction;
private Label _lastKey;
/// <summary>
/// This method creates at test menu bar. It is called by the MenuBar unit tests, so it's possible to do both unit
/// testing and user-experience testing with the same setup.
/// </summary>
/// <param name="actionFn"></param>
/// <returns></returns>
public static MenuBar CreateTestMenu (Func<string, bool> actionFn)
{
// TODO: add a disabled menu item to this
var mb = new MenuBar
{
Menus =
[
new MenuBarItem (
"_File",
new MenuItem []
{
new (
"_New",
"",
() => actionFn ("New"),
null,
null,
KeyCode.CtrlMask | KeyCode.N
),
new (
"_Open",
"",
() => actionFn ("Open"),
null,
null,
KeyCode.CtrlMask | KeyCode.O
),
new (
"_Save",
"",
() => actionFn ("Save"),
null,
null,
KeyCode.CtrlMask | KeyCode.S
),
null,
// Don't use Application.Quit so we can disambiguate between quitting and closing the toplevel
new (
"_Quit",
"",
() => actionFn ("Quit"),
null,
null,
KeyCode.CtrlMask | KeyCode.Q
)
}
),
new MenuBarItem (
"_Edit",
new MenuItem []
{
new (
"_Copy",
"",
() => actionFn ("Copy"),
null,
null,
KeyCode.CtrlMask | KeyCode.C
),
new (
"C_ut",
"",
() => actionFn ("Cut"),
null,
null,
KeyCode.CtrlMask | KeyCode.X
),
new (
"_Paste",
"",
() => actionFn ("Paste"),
null,
null,
KeyCode.CtrlMask | KeyCode.V
),
new MenuBarItem (
"_Find and Replace",
new MenuItem []
{
new (
"F_ind",
"",
() => actionFn ("Find"),
null,
null,
KeyCode.CtrlMask | KeyCode.F
),
new (
"_Replace",
"",
() => actionFn ("Replace"),
null,
null,
KeyCode.CtrlMask | KeyCode.H
),
new MenuBarItem (
"_3rd Level",
new MenuItem []
{
new (
"_1st",
"",
() => actionFn (
"1"
),
null,
null,
KeyCode.F1
),
new (
"_2nd",
"",
() => actionFn (
"2"
),
null,
null,
KeyCode.F2
)
}
),
new MenuBarItem (
"_4th Level",
new MenuItem []
{
new (
"_5th",
"",
() => actionFn (
"5"
),
null,
null,
KeyCode.CtrlMask
| KeyCode.D5
),
new (
"_6th",
"",
() => actionFn (
"6"
),
null,
null,
KeyCode.CtrlMask
| KeyCode.D6
)
}
)
}
),
new (
"_Select All",
"",
() => actionFn ("Select All"),
null,
null,
KeyCode.CtrlMask
| KeyCode.ShiftMask
| KeyCode.S
)
}
),
new MenuBarItem ("_About", "Top-Level", () => actionFn ("About"))
]
};
mb.UseKeysUpDownAsKeysLeftRight = true;
mb.Key = KeyCode.F9;
mb.Title = "TestMenuBar";
return mb;
}
public override void Main ()
{
// Init
@@ -239,14 +59,19 @@ public class MenuBarScenario : Scenario
_focusedView = new Label { X = Pos.Right (label), Y = Pos.Top (label), Text = "" };
appWindow.Add (_focusedView);
MenuBar menuBar = CreateTestMenu (
s =>
{
_lastAction.Text = s;
MenuBar menuBar = new MenuBar ();
menuBar.UseKeysUpDownAsKeysLeftRight = true;
menuBar.Key = KeyCode.F9;
menuBar.Title = "TestMenuBar";
return true;
}
);
bool fnAction (string s)
{
_lastAction.Text = s;
return true;
}
menuBar.EnableForDesign ((Func<string, bool>)fnAction);
menuBar.MenuOpening += (s, e) =>
{

View File

@@ -1253,14 +1253,16 @@ wo
MenuItem mbiCurrent = null;
MenuItem miCurrent = null;
MenuBar menu = MenuBarScenario.CreateTestMenu (
s =>
MenuBar menu = new MenuBar ();
menu.EnableForDesign (
new Func<object, bool> (s =>
{
miAction = s;
miAction = s as string;
return true;
}
);
})
);
menu.Key = KeyCode.F9;
menu.MenuOpening += (s, e) => mbiCurrent = e.CurrentMenu;
menu.MenuOpened += (s, e) => { miCurrent = e.MenuItem; };
@@ -1301,14 +1303,16 @@ wo
MenuItem mbiCurrent = null;
MenuItem miCurrent = null;
MenuBar menu = MenuBarScenario.CreateTestMenu (
s =>
MenuBar menu = new MenuBar ();
menu.EnableForDesign (
new Func<object, bool> (s =>
{
miAction = s;
miAction = s as string;
return true;
}
);
})
);
menu.Key = KeyCode.F9;
menu.MenuOpening += (s, e) => mbiCurrent = e.CurrentMenu;
menu.MenuOpened += (s, e) => { miCurrent = e.MenuItem; };