Added Main to Scenario. Updated a few scenarios to test out.

This commit is contained in:
Tig
2024-03-25 15:20:24 -06:00
parent 561d671746
commit fffae452e2
8 changed files with 191 additions and 183 deletions

View File

@@ -93,7 +93,7 @@ public static partial class Application
{
t.Running = false;
#if DEBUG_IDISPOSABLE
// Don't dispose the tolevels. It's up to caller dispose them
// Don't dispose the toplevels. It's up to caller dispose them
Debug.Assert (t.WasDisposed);
#endif
}
@@ -105,7 +105,7 @@ public static partial class Application
if (Top is { })
{
Debug.Assert (Top.WasDisposed);
// If End wasn't called _latestClosedRunStateToplevel may be null
// If End wasn't called _cachedRunStateToplevel may be null
if (_cachedRunStateToplevel is { })
{
Debug.Assert (_cachedRunStateToplevel.WasDisposed);
@@ -246,7 +246,8 @@ public static partial class Application
// multiple times. We need to do this because some settings are only
// valid after a Driver is loaded. In this cases we need just
// `Settings` so we can determine which driver to use.
Load (true);
// Don't reset, so we can inherit the theme from the previous run.
Load (false);
Apply ();
// Ignore Configuration for ForceDriver if driverName is specified
@@ -350,6 +351,7 @@ public static partial class Application
/// </remarks>
public static void Shutdown ()
{
// TODO: Throw an exception if Init hasn't been called.
ResetState ();
PrintJsonErrors ();
}

View File

@@ -147,9 +147,8 @@ public static class ConfigurationManager
{
if (_settings is null)
{
throw new InvalidOperationException (
"ConfigurationManager has not been initialized. Call ConfigurationManager.Reset() before accessing the Settings property."
);
// If Settings is null, we need to initialize it.
Reset ();
}
return _settings;

View File

@@ -88,18 +88,6 @@ public class Scenario : IDisposable
/// </summary>
public Window Win { get; set; }
public void Dispose ()
{
// BUGBUG: Top should have already been disposed. We dispose it here until we can fix the scenarios that are doing it wrong.
Top?.Dispose ();
// We created Win, so we Dispose it.
Win?.Dispose ();
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose (true);
GC.SuppressFinalize (this);
}
/// <summary>
/// Helper function to get the list of categories a <see cref="Scenario"/> belongs to (defined in
@@ -140,6 +128,15 @@ public class Scenario : IDisposable
return objects.OrderBy (s => s.GetName ()).ToList ();
}
public virtual void Main ()
{
Init ();
Setup ();
Run ();
}
/// <summary>
/// Helper that calls <see cref="Application.Init"/> and creates the default <see cref="Terminal.Gui.Window"/> implementation with a frame and label
/// showing the name of the <see cref="Scenario"/> and logic to exit back to the Scenario picker UI. Override
@@ -186,7 +183,7 @@ public class Scenario : IDisposable
/// </remarks>
public virtual void Run ()
{
// Must explicit call Application.Shutdown method to shutdown.
// Must explicitly call Application.Shutdown method to shutdown.
Application.Run (Top);
}
@@ -197,6 +194,13 @@ public class Scenario : IDisposable
/// <summary>Gets the Scenario Name + Description with the Description padded based on the longest known Scenario name.</summary>
/// <returns></returns>
public override string ToString () { return $"{GetName ().PadRight (_maxScenarioNameLen)}{GetDescription ()}"; }
public void Dispose ()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose (true);
GC.SuppressFinalize (this);
}
protected virtual void Dispose (bool disposing)
{
@@ -204,11 +208,10 @@ public class Scenario : IDisposable
{
if (disposing)
{
// TODO: dispose managed state (managed objects)
Top?.Dispose ();
Win?.Dispose ();
}
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
// TODO: set large fields to null
_disposedValue = true;
}
}

View File

@@ -354,6 +354,20 @@ public class BackgroundWorkerCollection : Scenario
Source = new ListWrapper (_log)
};
Add (_listLog);
Closing += WorkerApp_Closing;
}
private void WorkerApp_Closing (object sender, ToplevelClosingEventArgs e)
{
Toplevel top = Application.OverlappedChildren.Find (x => x.Data.ToString () == "WorkerApp");
if (Visible && top == this)
{
Visible = false;
e.Cancel = true;
Application.OverlappedMoveNext ();
}
}
public void CancelWorker ()

View File

@@ -6,21 +6,18 @@ namespace UICatalog.Scenarios;
[ScenarioCategory ("Unicode")]
public class ChineseUI : Scenario
{
public override void Init ()
public override void Main ()
{
Application.Init ();
Toplevel top = new ();
var win = new Window
{
Title = "Test",
Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
X = 0,
Y = 0,
Width = Dim.Fill (),
Height = Dim.Fill ()
};
top.Add (win);
var buttonPanel = new FrameView
{
@@ -35,20 +32,20 @@ public class ChineseUI : Scenario
var btn = new Button { X = 1, Y = 1, Text = "你" }; // v1: A
btn.Accept += (s, e) =>
{
int result = MessageBox.Query (
"Confirm",
"Are you sure you want to quit ui?",
0,
"Yes",
"No"
);
{
int result = MessageBox.Query (
"Confirm",
"Are you sure you want to quit ui?",
0,
"Yes",
"No"
);
if (result == 0)
{
Application.RequestStop ();
}
};
if (result == 0)
{
Application.RequestStop ();
}
};
buttonPanel.Add (
btn,
@@ -56,10 +53,10 @@ public class ChineseUI : Scenario
new Button { X = 22, Y = 1, Text = "呀" } // v1: C
);
Application.Run (top);
Application.Run (win);
top.Dispose ();
win.Dispose ();
Application.Shutdown ();
}
public override void Run () { }
}

View File

@@ -4,37 +4,28 @@ namespace UICatalog.Scenarios;
[ScenarioMetadata ("Generic", "Generic sample - A template for creating new Scenarios")]
[ScenarioCategory ("Controls")]
public class MyScenario : Scenario
public sealed class MyScenario : Scenario
{
public override void Init ()
public override void Main ()
{
// The base `Scenario.Init` implementation:
// - Calls `Application.Init ()`
// - Adds a full-screen Window to Application.Top with a title
// that reads "Press <hotkey> to Quit". Access this Window with `this.Win`.
// - Sets the Theme & the ColorScheme property of `this.Win` to `colorScheme`.
// To override this, implement an override of `Init`.
//base.Init ();
// A common, alternate, implementation where `this.Win` is not used is below. This code
// leverages ConfigurationManager to borrow the color scheme settings from UICatalog:
// Init
Application.Init ();
ConfigurationManager.Themes.Theme = Theme;
ConfigurationManager.Apply ();
Top = new Toplevel ();
Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
}
public override void Setup ()
{
// Put scenario code here (in a real app, this would be the code
// that would setup the app before `Application.Run` is called`).
// With a Scenario, after UI Catalog calls `Scenario.Setup` it calls
// `Scenario.Run` which calls `Application.Run`. Example:
// Setup - Create a top-level application window and configure it.
Window appWindow = new ()
{
Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
};
var button = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "Press me!" };
Top.Add (button);
button.Accept += (s, e) => MessageBox.ErrorQuery ("Error", "You pressed the button!", "Ok");
appWindow.Add (button);
// Run - Start the application.
Application.Run (appWindow);
appWindow.Dispose ();
// Shutdown - Calling Application.Shutdown is required.
Application.Shutdown ();
}
}

View File

@@ -12,15 +12,92 @@ public class Notepad : Scenario
{
private TabView _focusedTabView;
private StatusItem _lenStatusItem;
private int _numbeOfNewTabs = 1;
private int _numNewTabs = 1;
private TabView _tabView;
// Don't create a Window, just return the top-level view
public override void Init ()
public override void Main ()
{
Application.Init ();
Top = new ();
Top.ColorScheme = Colors.ColorSchemes ["Base"];
Toplevel top = new ();
var menu = new MenuBar
{
Menus =
[
new (
"_File",
new MenuItem []
{
new (
"_New",
"",
() => New (),
null,
null,
KeyCode.N
| KeyCode.CtrlMask
| KeyCode.AltMask
),
new ("_Open", "", () => Open ()),
new ("_Save", "", () => Save ()),
new ("Save _As", "", () => SaveAs ()),
new ("_Close", "", () => Close ()),
new ("_Quit", "", () => Quit ())
}
),
new (
"_About",
"",
() => MessageBox.Query ("Notepad", "About Notepad...", "Ok")
)
]
};
top.Add (menu);
_tabView = CreateNewTabView ();
_tabView.Style.ShowBorder = true;
_tabView.ApplyStyleChanges ();
// Start with only a single view but support splitting to show side by side
var split = new TileView (1) { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1) };
split.Tiles.ElementAt (0).ContentView.Add (_tabView);
split.LineStyle = LineStyle.None;
top.Add (split);
_lenStatusItem = new (KeyCode.CharMask, "Len: ", null);
var statusBar = new StatusBar (
new []
{
new (
Application.QuitKey,
$"{Application.QuitKey} to Quit",
() => Quit ()
),
// These shortcut keys don't seem to work correctly in linux
//new StatusItem(Key.CtrlMask | Key.N, "~^O~ Open", () => Open()),
//new StatusItem(Key.CtrlMask | Key.N, "~^N~ New", () => New()),
new (KeyCode.CtrlMask | KeyCode.S, "~^S~ Save", () => Save ()),
new (KeyCode.CtrlMask | KeyCode.W, "~^W~ Close", () => Close ()),
_lenStatusItem
}
);
_focusedTabView = _tabView;
_tabView.SelectedTabChanged += TabView_SelectedTabChanged;
_tabView.Enter += (s, e) => _focusedTabView = _tabView;
top.Add (statusBar);
top.Ready += (s, e) => New ();
Application.Run (top);
top.Dispose ();
Application.Shutdown ();
}
public void Save () { Save (_focusedTabView, _focusedTabView.SelectedTab); }
@@ -58,99 +135,26 @@ public class Notepad : Scenario
if (string.IsNullOrWhiteSpace (fd.Path))
{
fd.Dispose ();
return false;
}
if (fd.Canceled)
{
fd.Dispose ();
return false;
}
tab.File = new FileInfo (fd.Path);
tab.File = new (fd.Path);
tab.Text = fd.FileName;
tab.Save ();
fd.Dispose ();
return true;
}
public override void Setup ()
{
var menu = new MenuBar
{
Menus =
[
new MenuBarItem (
"_File",
new MenuItem []
{
new (
"_New",
"",
() => New (),
null,
null,
KeyCode.N
| KeyCode.CtrlMask
| KeyCode.AltMask
),
new ("_Open", "", () => Open ()),
new ("_Save", "", () => Save ()),
new ("Save _As", "", () => SaveAs ()),
new ("_Close", "", () => Close ()),
new ("_Quit", "", () => Quit ())
}
),
new MenuBarItem (
"_About",
"",
() => MessageBox.Query ("Notepad", "About Notepad...", "Ok")
)
]
};
Top.Add (menu);
_tabView = CreateNewTabView ();
_tabView.Style.ShowBorder = true;
_tabView.ApplyStyleChanges ();
// Start with only a single view but support splitting to show side by side
var split = new TileView (1) { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1) };
split.Tiles.ElementAt (0).ContentView.Add (_tabView);
split.LineStyle = LineStyle.None;
Top.Add (split);
_lenStatusItem = new StatusItem (KeyCode.CharMask, "Len: ", null);
var statusBar = new StatusBar (
new []
{
new (
Application.QuitKey,
$"{Application.QuitKey} to Quit",
() => Quit ()
),
// These shortcut keys don't seem to work correctly in linux
//new StatusItem(Key.CtrlMask | Key.N, "~^O~ Open", () => Open()),
//new StatusItem(Key.CtrlMask | Key.N, "~^N~ New", () => New()),
new (KeyCode.CtrlMask | KeyCode.S, "~^S~ Save", () => Save ()),
new (KeyCode.CtrlMask | KeyCode.W, "~^W~ Close", () => Close ()),
_lenStatusItem
}
);
_focusedTabView = _tabView;
_tabView.SelectedTabChanged += TabView_SelectedTabChanged;
_tabView.Enter += (s, e) => _focusedTabView = _tabView;
Top.Add (statusBar);
Top.Ready += (s, e) => New ();
}
private void Close () { Close (_focusedTabView, _focusedTabView.SelectedTab); }
private void Close (TabView tv, Tab tabToClose)
@@ -244,7 +248,7 @@ public class Notepad : Scenario
return tv;
}
private void New () { Open (null, $"new {_numbeOfNewTabs++}"); }
private void New () { Open (null, $"new {_numNewTabs++}"); }
private void Open ()
{
@@ -264,7 +268,7 @@ public class Notepad : Scenario
}
// TODO should open in focused TabView
Open (new FileInfo (path), Path.GetFileName (path));
Open (new (path), Path.GetFileName (path));
}
}
@@ -335,27 +339,27 @@ public class Notepad : Scenario
if (e.Tab == null)
{
items = new MenuBarItem (
new MenuItem [] { new ("Open", "", () => Open ()) }
);
items = new (
new MenuItem [] { new ("Open", "", () => Open ()) }
);
}
else
{
var tv = (TabView)sender;
var t = (OpenedFile)e.Tab;
items = new MenuBarItem (
new MenuItem []
{
new ("Save", "", () => Save (_focusedTabView, e.Tab)),
new ("Close", "", () => Close (tv, e.Tab)),
null,
new ("Split Up", "", () => SplitUp (tv, t)),
new ("Split Down", "", () => SplitDown (tv, t)),
new ("Split Right", "", () => SplitRight (tv, t)),
new ("Split Left", "", () => SplitLeft (tv, t))
}
);
items = new (
new MenuItem []
{
new ("Save", "", () => Save (_focusedTabView, e.Tab)),
new ("Close", "", () => Close (tv, e.Tab)),
null,
new ("Split Up", "", () => SplitUp (tv, t)),
new ("Split Down", "", () => SplitDown (tv, t)),
new ("Split Right", "", () => SplitRight (tv, t)),
new ("Split Left", "", () => SplitLeft (tv, t))
}
);
}
Rectangle screen = ((View)sender).BoundsToScreen (new (e.MouseEvent.X, e.MouseEvent.Y, 0, 0));
@@ -368,14 +372,6 @@ public class Notepad : Scenario
private class OpenedFile : Tab
{
public FileInfo File { get; set; }
/// <summary>The text of the tab the last time it was saved</summary>
/// <value></value>
public string SavedText { get; set; }
public bool UnsavedChanges => !string.Equals (SavedText, View.Text);
public OpenedFile CloneTo (TabView other)
{
var newTab = new OpenedFile { DisplayText = base.Text, File = File };
@@ -407,6 +403,8 @@ public class Notepad : Scenario
};
}
public FileInfo File { get; set; }
public void RegisterTextViewEvents (TabView parent)
{
var textView = (TextView)View;
@@ -436,6 +434,12 @@ public class Notepad : Scenario
};
}
/// <summary>The text of the tab the last time it was saved</summary>
/// <value></value>
public string SavedText { get; set; }
public bool UnsavedChanges => !string.Equals (SavedText, View.Text);
internal void Save ()
{
string newText = View.Text;

View File

@@ -290,11 +290,10 @@ internal class UICatalogApp
Application.Init (driverName: _forceDriver);
_selectedScenario.Theme = _cachedTheme;
_selectedScenario.TopLevelColorScheme = _topLevelColorScheme;
_selectedScenario.Init ();
_selectedScenario.Setup ();
_selectedScenario.Run ();
_selectedScenario.Main ();
_selectedScenario.Dispose ();
_selectedScenario = null;
// TODO: Throw if shutdown was not called already
Application.Shutdown ();
VerifyObjectsWereDisposed ();
@@ -322,13 +321,12 @@ internal class UICatalogApp
Apply ();
scenario.Theme = _cachedTheme;
scenario.TopLevelColorScheme = _topLevelColorScheme;
scenario.Init ();
scenario.Setup ();
scenario.Run ();
scenario.Main ();
scenario.Dispose ();
// This call to Application.Shutdown brackets the Application.Init call
// made by Scenario.Init() above
// TODO: Throw if shutdown was not called already
Application.Shutdown ();
VerifyObjectsWereDisposed ();