diff --git a/UICatalog/Program.cs b/UICatalog/Program.cs index a571f99c2..86ad5405d 100644 --- a/UICatalog/Program.cs +++ b/UICatalog/Program.cs @@ -12,7 +12,7 @@ namespace UICatalog { /// Main program for the Terminal.gui UI Catalog app. This app provides a chooser that allows /// for a calalog of UI demos, examples, and tests. /// - class Program { + internal class Program { private static Toplevel _top; private static MenuBar _menu; private static int _nameColumnWidth; @@ -24,34 +24,33 @@ namespace UICatalog { private static ListView _scenarioListView; private static StatusBar _statusBar; - private static Scenario _runningScenario = null; + private static Scenario _selectedScenario = null; static void Main (string [] args) { if (Debugger.IsAttached) CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US"); - _scenarios = Scenario.GetDerivedClassesCollection ().ToList (); + _scenarios = Scenario.GetDerivedClassesCollection ().OrderBy (t => Scenario.ScenarioMetadata.GetName (t)).ToList(); if (args.Length > 0) { var item = _scenarios.FindIndex (t => Scenario.ScenarioMetadata.GetName (t).Equals (args [0], StringComparison.OrdinalIgnoreCase)); - _runningScenario = (Scenario)Activator.CreateInstance (_scenarios [item]); - Application.Init (); - _runningScenario.Init (Application.Top); - _runningScenario.Setup (); - _runningScenario.Run (); - _runningScenario = null; + _selectedScenario = (Scenario)Activator.CreateInstance (_scenarios [item]); + _selectedScenario.Init (Application.Top); + _selectedScenario.Setup (); + _selectedScenario.Run (); + _selectedScenario = null; return; } Scenario scenario = GetScenarioToRun (); while (scenario != null) { - Application.Init (); scenario.Init (Application.Top); scenario.Setup (); scenario.Run (); scenario = GetScenarioToRun (); } + } /// @@ -75,7 +74,7 @@ namespace UICatalog { }; - _categories = Scenario.GetAllCategories (); + _categories = Scenario.GetAllCategories ().OrderBy(c => c).ToList(); _categoryListView = new ListView (_categories) { X = 1, Y = 0, @@ -125,12 +124,12 @@ namespace UICatalog { _statusBar = new StatusBar (new StatusItem [] { //new StatusItem(Key.F1, "~F1~ Help", () => Help()), new StatusItem(Key.ControlQ, "~CTRL-Q~ Quit", () => { - if (_runningScenario is null){ + if (_selectedScenario is null){ // This causes GetScenarioToRun to return null - _runningScenario = null; + _selectedScenario = null; Application.RequestStop(); } else { - _runningScenario.RequestStop(); + _selectedScenario.RequestStop(); } }), }); @@ -163,15 +162,16 @@ namespace UICatalog { Application.Iteration += Application_Iteration; #else _top.Ready += (o, a) => { - if (_runningScenario != null) { + if (_selectedScenario != null) { _top.SetFocus (_rightPane); - _runningScenario = null; + _selectedScenario = null; } }; #endif Application.Run (_top); - return _runningScenario; + Application.Shutdown (); + return _selectedScenario; } #if false @@ -183,9 +183,9 @@ namespace UICatalog { #endif private static void _scenarioListView_OpenSelectedItem (object sender, EventArgs e) { - if (_runningScenario is null) { + if (_selectedScenario is null) { var source = _scenarioListView.Source as ScenarioListDataSource; - _runningScenario = (Scenario)Activator.CreateInstance (source.Scenarios [_scenarioListView.SelectedItem]); + _selectedScenario = (Scenario)Activator.CreateInstance (source.Scenarios [_scenarioListView.SelectedItem]); Application.RequestStop (); } } @@ -246,7 +246,7 @@ namespace UICatalog { /// private static void KeyUpHandler (object sender, View.KeyEventEventArgs a) { - if (_runningScenario != null) { + if (_selectedScenario != null) { //switch (ke.Key) { //case Key.Esc: // //_runningScenario.RequestStop (); diff --git a/UICatalog/Scenario.cs b/UICatalog/Scenario.cs index 9b6442b95..3ec518c45 100644 --- a/UICatalog/Scenario.cs +++ b/UICatalog/Scenario.cs @@ -6,40 +6,54 @@ using Terminal.Gui; namespace UICatalog { /// - /// Base class for each demo/scenario. To define a new scenario simply + /// Base class for each demo/scenario. To define a new simply /// - /// 1) declare a class derived from Scenario, - /// 2) Set Name and Description as appropriate using [ScenarioMetadata] attribute - /// 3) Set one or more categories with the [ScenarioCategory] attribute + /// 1) declare a class derived from , + /// 2) Set Name and Description as appropriate using [] attribute + /// 3) Set one or more categories with the [] attribute /// 4) Implement Setup. - /// 5) Optionally, implement Run. + /// 5) Optionally, implement and/or . /// - /// The Main program uses reflection to find all scenarios and adds them to the - /// ListViews. Press ENTER to run the selected scenario. Press CTRL-Q to exit it. + /// This program uses reflection to find all scenarios and adds them to the + /// ListViews. Press ENTER to run the selected . Press CTRL-Q to exit it. /// public class Scenario : IDisposable { private bool _disposedValue; /// - /// The Top level for the Scenario. This should be set to `Application.Top` in most cases. + /// The for the . This should be set to in most cases. /// public Toplevel Top { get; set; } /// - /// The Window for the Scenario. This should be set within the `Application.Top` in most cases. + /// The for the . This should be set within ` in most cases. /// public Window Win { get; set; } /// - /// Helper that provides the default Window implementation with a frame and - /// label showing the name of the Scenario and logic to exit back to - /// the Scenario picker UI. + /// Helper that provides the default implementation with a frame and + /// label showing the name of the and logic to exit back to + /// the picker UI. /// Override Init to provide any `Toplevel` behavior needed. /// /// + /// + /// + /// Thg base implementation calls , sets to the passed in , creates a for and adds it to . + /// + /// + /// Overrides that do not call the base., must call before creating any views or calling other Terminal.Gui APIs. + /// + /// public virtual void Init(Toplevel top) { + Application.Init (); + Top = top; + if (Top == null) { + Top = Application.Top; + } + Win = new Window ($"CTRL-Q to Close - Scenario: {GetName ()}") { X = 0, Y = 0, @@ -49,15 +63,18 @@ namespace UICatalog { Top.Add (Win); } + /// + /// Defines the metadata (Name and Description) for a + /// [System.AttributeUsage (System.AttributeTargets.Class)] public class ScenarioMetadata : System.Attribute { /// - /// Scenario Name + /// Name /// public string Name { get; set; } /// - /// Scenario Description + /// Description /// public string Description { get; set; } @@ -68,14 +85,14 @@ namespace UICatalog { } /// - /// Static helper function to get the Scenario Name given a Type + /// Static helper function to get the Name given a Type /// /// /// public static string GetName (Type t) => ((ScenarioMetadata)System.Attribute.GetCustomAttributes (t) [0]).Name; /// - /// Static helper function to get the Scenario Description given a Type + /// Static helper function to get the Description given a Type /// /// /// @@ -83,17 +100,20 @@ namespace UICatalog { } /// - /// Helper to get the Scenario Name + /// Helper to get the Name (defined in ) /// /// public string GetName () => ScenarioMetadata.GetName (this.GetType ()); /// - /// Helper to get the Scenario Description + /// Helper to get the Description (defined in ) /// /// public string GetDescription () => ScenarioMetadata.GetDescription (this.GetType ()); + /// + /// Defines the category names used to catagorize a + /// [System.AttributeUsage (System.AttributeTargets.Class, AllowMultiple = true)] public class ScenarioCategory : System.Attribute { /// @@ -104,17 +124,17 @@ namespace UICatalog { public ScenarioCategory (string Name) => this.Name = Name; /// - /// Static helper function to get the Scenario Name given a Type + /// Static helper function to get the Name given a Type /// /// - /// + /// Name of the catagory public static string GetName (Type t) => ((ScenarioCategory)System.Attribute.GetCustomAttributes (t) [0]).Name; /// - /// Static helper function to get the Scenario Categories given a Type + /// Static helper function to get the Categories given a Type /// /// - /// + /// list of catagory names public static List GetCategories (Type t) => System.Attribute.GetCustomAttributes (t) .ToList () .Where (a => a is ScenarioCategory) @@ -123,30 +143,40 @@ namespace UICatalog { } /// - /// Helper function to get the Categories of a Scenario + /// Helper function to get the list of categories a belongs to (defined in ) /// - /// + /// list of catagory names public List GetCategories () => ScenarioCategory.GetCategories (this.GetType ()); + /// public override string ToString () => $"{GetName (),-30}{GetDescription ()}"; /// - /// Override this to implement the Scenario setup logic (create controls, etc...). + /// Override this to implement the setup logic (create controls, etc...). /// + /// This is typically the best place to put scenario logic code. public virtual void Setup () { } /// - /// Runs the scenario. Override to start the scenario using a Top level different than `Top`. + /// Runs the . Override to start the using a different than `Top`. + /// /// + /// + /// Overrides that do not call the base., must call before returning. + /// public virtual void Run () { Application.Run (Top); + + // Every call to Application.Init must be bound by a call to Shutdown + // or Init doesn't do anything + Application.Shutdown (); } /// - /// Stops the scenario. Override to implement shutdown behavior for the Scenario. + /// Stops the scenario. Override to change shutdown behavior for the . /// public virtual void RequestStop () { @@ -154,13 +184,13 @@ namespace UICatalog { } /// - /// Returns a list of all Categories set by all of the scenarios defined in the project. + /// Returns a list of all Categories set by all of the s defined in the project. /// internal static List GetAllCategories () { List categories = new List () { "All" }; foreach (Type type in typeof (Scenario).Assembly.GetTypes () - .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (Scenario)))) { + .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (Scenario)))) { List attrs = System.Attribute.GetCustomAttributes (type).ToList (); categories = categories.Union (attrs.Where (a => a is ScenarioCategory).Select (a => ((ScenarioCategory)a).Name)).ToList (); } @@ -168,14 +198,14 @@ namespace UICatalog { } /// - /// Returns an instance of each Scenario defined in the project. + /// Returns an instance of each defined in the project. /// https://stackoverflow.com/questions/5411694/get-all-inherited-classes-of-an-abstract-class /// internal static List GetDerivedClassesCollection () { List objects = new List (); foreach (Type type in typeof (Scenario).Assembly.GetTypes () - .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (Scenario)))) { + .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf (typeof (Scenario)))) { objects.Add (type); } return objects; diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index f71f6a818..a09b630bf 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -14,7 +14,7 @@ namespace UICatalog { public override void Init (Toplevel top) { - Top = top; + base.Init (top); } public override void Setup () @@ -144,7 +144,7 @@ namespace UICatalog { public override void Run () { - Application.Run (Top); + base.Run (); } } } diff --git a/UICatalog/Scenarios/HexEditor.cs b/UICatalog/Scenarios/HexEditor.cs index 8621a90cc..2477c0ffd 100644 --- a/UICatalog/Scenarios/HexEditor.cs +++ b/UICatalog/Scenarios/HexEditor.cs @@ -12,12 +12,6 @@ namespace UICatalog { private HexView _hexView; private bool _saved = true; - - public override void Init (Toplevel top) - { - Top = top; - } - public override void Setup () { var menu = new MenuBar (new MenuBarItem [] { @@ -145,10 +139,5 @@ namespace UICatalog { sw.Write (sb.ToString ()); sw.Close (); } - - public override void Run () - { - Application.Run (Top); - } } } diff --git a/UICatalog/Scenarios/Keys.cs b/UICatalog/Scenarios/Keys.cs index ef4f8fa6b..4f49e9197 100644 --- a/UICatalog/Scenarios/Keys.cs +++ b/UICatalog/Scenarios/Keys.cs @@ -50,6 +50,7 @@ namespace UICatalog { public override void Init (Toplevel top) { + Application.Init (); Top = top; Win = new TestWindow ($"CTRL-Q to Close - Scenario: {GetName ()}") { diff --git a/UICatalog/Scenarios/ListsAndCombos.cs b/UICatalog/Scenarios/ListsAndCombos.cs index f5633e47b..62b5f38d7 100644 --- a/UICatalog/Scenarios/ListsAndCombos.cs +++ b/UICatalog/Scenarios/ListsAndCombos.cs @@ -6,7 +6,7 @@ using Terminal.Gui; using NStack; namespace UICatalog.Scenarios { - [ScenarioMetadata (Name: "Lists", Description: "Demonstrates list selections")] + [ScenarioMetadata (Name: "ListView & ComboBox", Description: "Demonstrates a ListView populating a ComboBox that acts as a filter.")] [ScenarioCategory ("Controls")] class ListsAndCombos : Scenario { diff --git a/UICatalog/Scenarios/MessageBoxes.cs b/UICatalog/Scenarios/MessageBoxes.cs index fd916d984..4e87e088c 100644 --- a/UICatalog/Scenarios/MessageBoxes.cs +++ b/UICatalog/Scenarios/MessageBoxes.cs @@ -33,10 +33,5 @@ namespace UICatalog { Top.Add (Win); } - - public override void Run () - { - Application.Run (Top); - } } } diff --git a/UICatalog/Scenarios/Progress.cs b/UICatalog/Scenarios/Progress.cs index 8c88f2fe5..223053939 100644 --- a/UICatalog/Scenarios/Progress.cs +++ b/UICatalog/Scenarios/Progress.cs @@ -6,8 +6,9 @@ namespace UICatalog { // // This would be a great scenario to show of threading (Issue #471) // - [ScenarioMetadata (Name: "Progress", Description: "Shows off ProgressBar.")] + [ScenarioMetadata (Name: "Progress", Description: "Shows off ProgressBar and Threading")] [ScenarioCategory ("Controls")] + [ScenarioCategory ("Threading")] class Progress : Scenario { private ProgressBar _activityProgressBar; @@ -19,23 +20,28 @@ namespace UICatalog { { var pulseButton = new Button ("Pulse") { X = Pos.Center (), - Y = Pos.Center () - 5, + Y = Pos.Center () - 3, Clicked = () => Pulse () }; - Win.Add (new Button ("Start Timer") { - X = Pos.Left(pulseButton) - 20, + var startButton = new Button ("Start Timer") { Y = Pos.Y(pulseButton), Clicked = () => Start () - }); + }; - Win.Add (new Button ("Stop Timer") { - X = Pos.Right (pulseButton) + 20, // BUGBUG: Right is somehow adding additional width + var stopbutton = new Button ("Stop Timer") { Y = Pos.Y (pulseButton), Clicked = () => Stop() - }); + }; + // Center three buttons with 5 spaces between them + // TODO: Use Pos.Width instead of (Right-Left) when implemented (#502) + startButton.X = Pos.Left (pulseButton) - (Pos.Right (startButton) - Pos.Left (startButton)) - 5; + stopbutton.X = Pos.Right (pulseButton) + 5; + + Win.Add (startButton); Win.Add (pulseButton); + Win.Add (stopbutton); _activityProgressBar = new ProgressBar () { X = Pos.Center (), @@ -84,9 +90,8 @@ namespace UICatalog { _pulseProgressBar.Fraction = 0F; _timer = new Timer ((o) => { - // BUGBUG: #409 - Invoke does not cause Wakeup as it should Application.MainLoop.Invoke (() => Pulse ()); - }, null, 0, 250); + }, null, 0, 20); } private void Stop () diff --git a/UICatalog/Scenarios/SystemConsole.cs b/UICatalog/Scenarios/SystemConsole.cs new file mode 100644 index 000000000..a08bea893 --- /dev/null +++ b/UICatalog/Scenarios/SystemConsole.cs @@ -0,0 +1,33 @@ +using Terminal.Gui; + +namespace UICatalog { + [ScenarioMetadata (Name: "System Console", Description: "Not working - #518 - Enables System Console and exercises things")] + [ScenarioCategory ("Bug Repro")] + [ScenarioCategory ("Console")] + class UseSystemConsole : Scenario { + public override void Init (Toplevel top) + { + Application.UseSystemConsole = true; + base.Init (top); + } + + public override void RequestStop () + { + base.RequestStop (); + } + + public override void Run () + { + base.Run (); + } + + public override void Setup () + { + Win.Add (new Button ("Press me!") { + X = Pos.Center (), + Y = Pos.Center (), + Clicked = () => MessageBox.Query (20, 7, "Hi", "Neat?", "Yes", "No") + }); + } + } +} \ No newline at end of file diff --git a/UICatalog/Scenarios/TopLevelNoWindowBug.cs b/UICatalog/Scenarios/TopLevelNoWindowBug.cs index a8040b935..3cd2553bd 100644 --- a/UICatalog/Scenarios/TopLevelNoWindowBug.cs +++ b/UICatalog/Scenarios/TopLevelNoWindowBug.cs @@ -34,7 +34,7 @@ namespace UICatalog { }; ntop.Add (win); - Application.Run (ntop); + base.Run (); } } } diff --git a/UICatalog/Scenarios/WindowExperiment.cs b/UICatalog/Scenarios/WindowExperiment.cs new file mode 100644 index 000000000..b0e964561 --- /dev/null +++ b/UICatalog/Scenarios/WindowExperiment.cs @@ -0,0 +1,142 @@ +using System.Collections.Generic; +using System.Linq; +using Terminal.Gui; + +namespace UICatalog { + [ScenarioMetadata (Name: "Windows & FrameViews", Description: "Shows Windows, sub-Windows, FrameViews, and how TAB doesn't work right (#434, #522)")] + [ScenarioCategory ("Views")] + class WindowExperiment : Scenario { + public override void Init (Toplevel top) + { + Application.Init (); + + Top = top; + if (Top == null) { + Top = Application.Top; + } + } + + public override void RequestStop () + { + base.RequestStop (); + } + + public override void Run () + { + base.Run (); + } + + public override void Setup () + { + int margin = 3; + int padding = 1; + int height = 10; + var listWin = new List (); + Win = new Window ($"{listWin.Count} - Scenario: {GetName ()}", padding) { + X = margin, + Y = margin, + Width = Dim.Fill (margin), + Height = height, + }; + Win.ColorScheme = Colors.Dialog; + Win.Add (new Button ("Press me!") { + X = Pos.Center (), + Y = 0, + ColorScheme = Colors.Error, + Clicked = () => MessageBox.ErrorQuery (30, 10, Win.Title.ToString (), "Neat?", "Yes", "No") + }); + Top.Add (Win); + listWin.Add (Win); + + for (var i = 0; i < 2; i++) { + Window win = null; + win = new Window ($"{listWin.Count} - Scenario: {GetName ()}", padding) { + X = margin, + Y = Pos.Bottom(listWin.Last()) + (margin/2), + Width = Dim.Fill (margin), + Height = height, + }; + win.ColorScheme = Colors.Dialog; + win.Add (new Button ("Press me!") { + X = Pos.Center (), + Y = 0, + ColorScheme = Colors.Error, + Clicked = () => MessageBox.ErrorQuery (30, 10, win.Title.ToString (), "Neat?", "Yes", "No") + }); + var subWin = new Window("Sub Window") { + X = Pos.Percent (0), + Y = Pos.AnchorEnd() - 5, + Width = Dim.Percent (50), + Height = 5, + ColorScheme = Colors.Base, + }; + subWin.Add (new TextField (win.Title.ToString ())); + win.Add (subWin); + var frameView = new FrameView ("This is a Sub-FrameView") { + X = Pos.Percent(50), + Y = Pos.AnchorEnd () - 5, + Width = Dim.Percent (100), + Height = 5, + ColorScheme = Colors.Base, + }; + frameView.Add (new TextField ("Edit Me")); + win.Add (frameView); + + Top.Add (win); + listWin.Add (win); + } + + + FrameView frame = null; + frame = new FrameView ($"This is a FrameView") { + X = margin, + Y = Pos.Bottom (listWin.Last ()) + (margin / 2), + Width = Dim.Fill (margin), + Height = height, + }; + frame.ColorScheme = Colors.Dialog; + frame.Add (new Button ("Press me!") { + X = Pos.Center (), + Y = 0, + ColorScheme = Colors.Error, + Clicked = () => MessageBox.ErrorQuery (30, 10, frame.Title.ToString (), "Neat?", "Yes", "No") + }); + var subWinFV = new Window ("this is a Sub-Window") { + X = Pos.Percent (0), + Y = Pos.AnchorEnd () - (height - 4), + Width = Dim.Percent (50), + Height = Dim.Fill () - 1, + ColorScheme = Colors.Base, + }; + subWinFV.Add (new TextField (frame.Title.ToString ())); + frame.Add (subWinFV); + var frameViewFV = new FrameView ("this is a Sub-FrameView") { + X = Pos.Percent (50), + Y = Pos.AnchorEnd () - (height - 4), + Width = Dim.Percent (100), + Height = Dim.Fill() - 1, + ColorScheme = Colors.Base, + }; + frameViewFV.Add (new TextField ("Edit Me")); + + frameViewFV.Add (new CheckBox (0, 1, "Check me")); + // BUGBUG: This checkbox is not shown even though frameViewFV has 3 rows in + // it's client area. #522 + frameViewFV.Add (new CheckBox (0, 2, "Or, Check me")); + + frame.Add (new CheckBox ("No, Check me!") { + X = 0, + Y = Pos.AnchorEnd() - 1, // BUGBUG: #522 If I don't do the -1 it doesn't draw, but it should! + }); + frame.Add (new CheckBox ("Really, Check me!") { + X = Pos.Percent(50), + Y = Pos.AnchorEnd () - 1, // BUGBUG: #522 If I don't do the -1 it doesn't draw, but it should! + }); + + frame.Add (frameViewFV); + + Top.Add (frame); + listWin.Add (frame); + } + } +} \ No newline at end of file