diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index feb12c7b..6c22810b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,12 @@ jobs: package: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '10.0.x' - name: Build run: cd src && dotnet build diff --git a/.gitignore b/.gitignore index dfcfd56f..a96e7a22 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,8 @@ # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs +.claude + # Mono auto generated files mono_crash.* diff --git a/README.md b/README.md index b3d660dc..08e2dff9 100644 --- a/README.md +++ b/README.md @@ -198,12 +198,12 @@ italics are experimental and require passing the `-e` flag when starting applica - [x] GraphView - [x] HexView - [x] Label - - [x] LineView + - [x] Line - [x] ListView - [x] MenuBar - [ ] Copy/Paste preserve menu entries - [x] ProgressBar - - [x] RadioGroup + - [x] OptionSelector - [ ] [SplitContainer](https://github.com/gui-cs/Terminal.Gui/pull/2258) (Unreleased) - [ ] Copy/Paste preserve split content panels - [x] StatusBar diff --git a/Showcase/Menus.Designer.cs b/Showcase/Menus.Designer.cs index 33652cf4..bca97270 100644 --- a/Showcase/Menus.Designer.cs +++ b/Showcase/Menus.Designer.cs @@ -8,7 +8,8 @@ // the code is regenerated. // // ----------------------------------------------------------------------------- -namespace Showcase { +namespace Showcase +{ using System; using Terminal.Gui; using Terminal.Gui.App; @@ -20,211 +21,61 @@ namespace Showcase { using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; - - - public partial class Menus : Terminal.Gui.ViewBase.View { - + + + public partial class Menus : Terminal.Gui.Views.Dialog + { + private Terminal.Gui.Views.MenuBar menuBar; - - private Terminal.Gui.Views.MenuBarItem fileF9Menu; - - private Terminal.Gui.Views.MenuBarItem newMenu; - - private Terminal.Gui.Views.MenuItem projectMenuItem; - - private Terminal.Gui.Views.MenuItem repositoryMenuItem; - - private Terminal.Gui.Views.MenuItem fileMenuItem; - - private Terminal.Gui.Views.MenuItem projectFromExistingCodeMenuItem; - - private Terminal.Gui.Views.MenuItem spellCheckerConfigurationForSelectedItemMenuItem; - - private Terminal.Gui.Views.MenuBarItem openMenu; - - private Terminal.Gui.Views.MenuItem projectSolutionMenuItem; - - private Terminal.Gui.Views.MenuItem folderMenuItem; - - private Terminal.Gui.Views.MenuItem workspaceMenuItem; - - private Terminal.Gui.Views.MenuItem cMakeMenuItem; - - private Terminal.Gui.Views.MenuItem fileMenuItem2; - - private Terminal.Gui.Views.MenuBarItem editMenu; - - private Terminal.Gui.Views.MenuBarItem goToMenu; - - private Terminal.Gui.Views.MenuItem goToLineMenuItem; - - private Terminal.Gui.Views.MenuItem goToTextMenuItem; - - private Terminal.Gui.Views.MenuItem goToAllMenuItem; - - private Terminal.Gui.Views.MenuItem findAndReplaceMenuItem; - - private Terminal.Gui.Views.MenuItem goToBaseMenuItem; - - private Terminal.Gui.Views.MenuItem undoMenuItem; - - private Terminal.Gui.Views.MenuItem redoMenuItem; - - private Terminal.Gui.Views.MenuItem undoLastGlobalActionMenuItem; - - private Terminal.Gui.Views.MenuItem redoLastGlobalActionMenuItem; - - private Terminal.Gui.Views.MenuItem cutMenuItem; - - private Terminal.Gui.Views.MenuItem copyMenuItem; - - private Terminal.Gui.Views.MenuItem pasteMenuItem; - - private Terminal.Gui.Views.MenuItem showClipboardHistoryMenuItem; - - private void InitializeComponent() { + + private Terminal.Gui.Views.MenuBarItem fileMenu; + + private Terminal.Gui.Views.MenuItem newMenuItem; + + private Terminal.Gui.Views.MenuItem carMenuItem; + + private Terminal.Gui.Views.MenuItem exitMenuItem; + + private void InitializeComponent() + { this.menuBar = new Terminal.Gui.Views.MenuBar(); - this.Width = Dim.Fill(0); - this.Height = Dim.Fill(0); - this.X = 0; - this.Y = 0; + this.Width = Dim.Percent(90); + this.Height = Dim.Percent(80); + this.X = Pos.Center(); + this.Y = Pos.Center(); this.Visible = true; - this.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; - this.CanFocus = false; - this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Overlapped; + this.CanFocus = true; + this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Transparent; this.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; this.menuBar.Width = Dim.Fill(0); - this.menuBar.Height = 1; + this.menuBar.Height = Dim.Auto(); this.menuBar.X = 0; this.menuBar.Y = 0; this.menuBar.Visible = true; this.menuBar.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.menuBar.CanFocus = false; - this.menuBar.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.menuBar.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Transparent; this.menuBar.Data = "menuBar"; this.menuBar.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; - this.fileF9Menu = new Terminal.Gui.Views.MenuBarItem(); - this.fileF9Menu.Title = "_File (F9)"; - this.newMenu = new Terminal.Gui.Views.MenuBarItem(); - this.newMenu.Title = "New"; - this.projectMenuItem = new Terminal.Gui.Views.MenuItem(); - this.projectMenuItem.Title = "Project..."; - this.projectMenuItem.Data = "projectMenuItem"; - this.projectMenuItem.ShortcutKey = ((Terminal.Gui.Drivers.KeyCode)(1073741902u)); - this.repositoryMenuItem = new Terminal.Gui.Views.MenuItem(); - this.repositoryMenuItem.Title = "Repository..."; - this.repositoryMenuItem.Data = "repositoryMenuItem"; - this.fileMenuItem = new Terminal.Gui.Views.MenuItem(); - this.fileMenuItem.Title = "File..."; - this.fileMenuItem.Data = "fileMenuItem"; - this.projectFromExistingCodeMenuItem = new Terminal.Gui.Views.MenuItem(); - this.projectFromExistingCodeMenuItem.Title = "Project From Existing Code..."; - this.projectFromExistingCodeMenuItem.Data = "projectFromExistingCodeMenuItem"; - this.spellCheckerConfigurationForSelectedItemMenuItem = new Terminal.Gui.Views.MenuItem(); - this.spellCheckerConfigurationForSelectedItemMenuItem.Title = "Spell Checker Configuration for Selected Item"; - this.spellCheckerConfigurationForSelectedItemMenuItem.Data = "spellCheckerConfigurationForSelectedItemMenuItem"; - this.newMenu.Children = new Terminal.Gui.Views.MenuItem[] { - this.projectMenuItem, - this.repositoryMenuItem, - this.fileMenuItem, - this.projectFromExistingCodeMenuItem, - null, - this.spellCheckerConfigurationForSelectedItemMenuItem}; - this.openMenu = new Terminal.Gui.Views.MenuBarItem(); - this.openMenu.Title = "Open"; - this.projectSolutionMenuItem = new Terminal.Gui.Views.MenuItem(); - this.projectSolutionMenuItem.Title = "Project/Solution"; - this.projectSolutionMenuItem.Data = "projectSolutionMenuItem"; - this.projectSolutionMenuItem.ShortcutKey = ((Terminal.Gui.Drivers.KeyCode)(1342177359u)); - this.folderMenuItem = new Terminal.Gui.Views.MenuItem(); - this.folderMenuItem.Title = "Folder..."; - this.folderMenuItem.Data = "folderMenuItem"; - this.workspaceMenuItem = new Terminal.Gui.Views.MenuItem(); - this.workspaceMenuItem.Title = "Workspace..."; - this.workspaceMenuItem.Data = "workspaceMenuItem"; - this.cMakeMenuItem = new Terminal.Gui.Views.MenuItem(); - this.cMakeMenuItem.Title = "CMake..."; - this.cMakeMenuItem.Data = "cMakeMenuItem"; - this.fileMenuItem2 = new Terminal.Gui.Views.MenuItem(); - this.fileMenuItem2.Title = "File..."; - this.fileMenuItem2.Data = "fileMenuItem2"; - this.fileMenuItem2.ShortcutKey = ((Terminal.Gui.Drivers.KeyCode)(1073741903u)); - this.openMenu.Children = new Terminal.Gui.Views.MenuItem[] { - this.projectSolutionMenuItem, - this.folderMenuItem, - this.workspaceMenuItem, - this.cMakeMenuItem, - null, - this.fileMenuItem2}; - this.fileF9Menu.Children = new Terminal.Gui.Views.MenuItem[] { - this.newMenu, - this.openMenu}; - this.editMenu = new Terminal.Gui.Views.MenuBarItem(); - this.editMenu.Title = "Edit"; - this.goToMenu = new Terminal.Gui.Views.MenuBarItem(); - this.goToMenu.Title = "Go To"; - this.goToLineMenuItem = new Terminal.Gui.Views.MenuItem(); - this.goToLineMenuItem.Title = "Go To Line..."; - this.goToLineMenuItem.Data = "goToLineMenuItem"; - this.goToLineMenuItem.ShortcutKey = ((Terminal.Gui.Drivers.KeyCode)(1073741895u)); - this.goToTextMenuItem = new Terminal.Gui.Views.MenuItem(); - this.goToTextMenuItem.Title = "Go To Text..."; - this.goToTextMenuItem.Data = "goToTextMenuItem"; - this.goToTextMenuItem.ShortcutKey = ((Terminal.Gui.Drivers.KeyCode)(2415919174u)); - this.goToAllMenuItem = new Terminal.Gui.Views.MenuItem(); - this.goToAllMenuItem.Title = "Go To All..."; - this.goToAllMenuItem.Data = "goToAllMenuItem"; - this.goToMenu.Children = new Terminal.Gui.Views.MenuItem[] { - this.goToLineMenuItem, - this.goToTextMenuItem, - this.goToAllMenuItem}; - this.findAndReplaceMenuItem = new Terminal.Gui.Views.MenuItem(); - this.findAndReplaceMenuItem.Title = "Find and Replace"; - this.findAndReplaceMenuItem.Data = "findAndReplaceMenuItem"; - this.goToBaseMenuItem = new Terminal.Gui.Views.MenuItem(); - this.goToBaseMenuItem.Title = "Go To Base"; - this.goToBaseMenuItem.Data = "goToBaseMenuItem"; - this.undoMenuItem = new Terminal.Gui.Views.MenuItem(); - this.undoMenuItem.Title = "Undo"; - this.undoMenuItem.Data = "undoMenuItem"; - this.redoMenuItem = new Terminal.Gui.Views.MenuItem(); - this.redoMenuItem.Title = "Redo"; - this.redoMenuItem.Data = "redoMenuItem"; - this.undoLastGlobalActionMenuItem = new Terminal.Gui.Views.MenuItem(); - this.undoLastGlobalActionMenuItem.Title = "Undo Last Global Action"; - this.undoLastGlobalActionMenuItem.Data = "undoLastGlobalActionMenuItem"; - this.redoLastGlobalActionMenuItem = new Terminal.Gui.Views.MenuItem(); - this.redoLastGlobalActionMenuItem.Title = "Redo Last Global Action"; - this.redoLastGlobalActionMenuItem.Data = "redoLastGlobalActionMenuItem"; - this.cutMenuItem = new Terminal.Gui.Views.MenuItem(); - this.cutMenuItem.Title = "Cut"; - this.cutMenuItem.Data = "cutMenuItem"; - this.copyMenuItem = new Terminal.Gui.Views.MenuItem(); - this.copyMenuItem.Title = "Copy"; - this.copyMenuItem.Data = "copyMenuItem"; - this.pasteMenuItem = new Terminal.Gui.Views.MenuItem(); - this.pasteMenuItem.Title = "Paste"; - this.pasteMenuItem.Data = "pasteMenuItem"; - this.showClipboardHistoryMenuItem = new Terminal.Gui.Views.MenuItem(); - this.showClipboardHistoryMenuItem.Title = "Show Clipboard History"; - this.showClipboardHistoryMenuItem.Data = "showClipboardHistoryMenuItem"; - this.editMenu.Children = new Terminal.Gui.Views.MenuItem[] { - this.goToMenu, - this.findAndReplaceMenuItem, - this.goToBaseMenuItem, - null, - this.undoMenuItem, - this.redoMenuItem, - this.undoLastGlobalActionMenuItem, - this.redoLastGlobalActionMenuItem, - null, - this.cutMenuItem, - this.copyMenuItem, - this.pasteMenuItem, - this.showClipboardHistoryMenuItem}; - this.menuBar.Menus = new Terminal.Gui.Views.MenuBarItem[] { - this.fileF9Menu, - this.editMenu}; + this.fileMenu = new Terminal.Gui.Views.MenuBarItem(); + this.fileMenu.Title = "_File"; + this.fileMenu.Key = Key.F9; + this.newMenuItem = new Terminal.Gui.Views.MenuItem(); + this.newMenuItem.Title = "New"; + this.newMenuItem.Data = "newMenuItem"; + + + this.carMenuItem = new Terminal.Gui.Views.MenuItem(); + this.carMenuItem.Title = "Car"; + this.carMenuItem.Data = "carMenuItem"; + this.newMenuItem.SubMenu = new Menu([carMenuItem]); + + this.exitMenuItem = new Terminal.Gui.Views.MenuItem(); + this.exitMenuItem.Title = "Exit"; + this.exitMenuItem.Data = "exitMenuItem"; + this.fileMenu.PopoverMenu = new PopoverMenu([newMenuItem, exitMenuItem]); + this.menuBar.Add(this.fileMenu); this.Add(this.menuBar); } } diff --git a/Showcase/Menus.cs b/Showcase/Menus.cs index 70a221e0..a49c9009 100644 --- a/Showcase/Menus.cs +++ b/Showcase/Menus.cs @@ -7,7 +7,7 @@ // You can make changes to this file and they will not be overwritten when saving. // // ----------------------------------------------------------------------------- -namespace Showcase { +namespace Showcase{ using Terminal.Gui; diff --git a/Showcase/Program.cs b/Showcase/Program.cs index c21b179a..40bd3311 100644 --- a/Showcase/Program.cs +++ b/Showcase/Program.cs @@ -1,51 +1,15 @@ -using System.Collections.ObjectModel; -using System.Runtime.InteropServices.ComTypes; -using Terminal.Gui; -using Terminal.Gui.App; -using Terminal.Gui.Drivers; -using Terminal.Gui.ViewBase; -using Terminal.Gui.Views; +using Terminal.Gui.App; namespace Showcase { internal class Program { - private static Type[] views = new[] - { - typeof(Menus), - typeof(Tabs) - - }; static void Main(string[] args) { - Application.Init(); - - var w = new Window() - { - Title = "Showcase" - }; - - var lv = new ListView() - { - Width = Dim.Fill(), - Height = Dim.Fill(), - }; - w.Add(lv); - lv.SetSource(new ObservableCollection(views)); - - - lv.KeyDown += (_, e) => + using(var app = Application.Create()) { - if (e.KeyCode == KeyCode.Enter) - { - var v = (Toplevel)Activator.CreateInstance(views[lv.SelectedItem]); - e.Handled = true; - Application.Run(v); - } - }; - - Application.Run(w); - Application.Shutdown(); + app.Run(); + } } } } diff --git a/Showcase/Showcase.csproj b/Showcase/Showcase.csproj index 624b4c1f..6c8c78be 100644 --- a/Showcase/Showcase.csproj +++ b/Showcase/Showcase.csproj @@ -2,14 +2,14 @@ Exe - net8.0 + net10.0 enable enable false - + diff --git a/Showcase/Tabs.Designer.cs b/Showcase/Tabs.Designer.cs deleted file mode 100644 index d898e59a..00000000 --- a/Showcase/Tabs.Designer.cs +++ /dev/null @@ -1,942 +0,0 @@ - -//------------------------------------------------------------------------------ - -// -// This code was generated by: -// TerminalGuiDesigner v2.0.0.0 -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ----------------------------------------------------------------------------- -namespace Showcase { - using System; - using Terminal.Gui; - using Terminal.Gui.App; - using Terminal.Gui.Drawing; - using Terminal.Gui.Input; - using Terminal.Gui.ViewBase; - using Terminal.Gui.Views; - using System.Collections; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Drawing; - - - public partial class Tabs : Terminal.Gui.Views.Dialog { - - private Terminal.Gui.Views.TabView tabView2; - - private Terminal.Gui.Views.TextView tvEditor; - - private Terminal.Gui.Views.TextView tvMouseManager; - - private Terminal.Gui.Views.TabView tabView; - - private Terminal.Gui.Views.GraphView graphView; - - private Terminal.Gui.Views.Label label; - - private Terminal.Gui.Views.TabView tabView3; - - private Terminal.Gui.Views.StatusBar statusBar; - - private Terminal.Gui.Views.Shortcut f1EditMe; - - private void InitializeComponent() { - this.statusBar = new Terminal.Gui.Views.StatusBar(); - this.tabView3 = new Terminal.Gui.Views.TabView(); - this.label = new Terminal.Gui.Views.Label(); - this.graphView = new Terminal.Gui.Views.GraphView(); - this.tabView = new Terminal.Gui.Views.TabView(); - this.tvMouseManager = new Terminal.Gui.Views.TextView(); - this.tvEditor = new Terminal.Gui.Views.TextView(); - this.tabView2 = new Terminal.Gui.Views.TabView(); - this.Width = Dim.Percent(90); - this.Height = Dim.Percent(80); - this.X = Pos.Center(); - this.Y = Pos.Center(); - this.Visible = true; - this.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Movable; - this.CanFocus = true; - this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; - this.Modal = true; - this.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; - this.Title = "Tab Views Demo"; - this.tabView2.Width = Dim.Percent(75); - this.tabView2.Height = Dim.Percent(75); - this.tabView2.X = 0; - this.tabView2.Y = 0; - this.tabView2.Visible = true; - this.tabView2.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; - this.tabView2.CanFocus = true; - this.tabView2.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; - this.tabView2.Data = "tabView2"; - this.tabView2.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; - this.tabView2.MaxTabTextWidth = 30u; - this.tabView2.Style.ShowBorder = true; - this.tabView2.Style.ShowTopLine = true; - this.tabView2.Style.TabsOnBottom = true; - Terminal.Gui.Views.Tab tabView2editorcs; - tabView2editorcs = new Terminal.Gui.Views.Tab(); - tabView2editorcs.DisplayText = "Editor.cs"; - tabView2editorcs.View = new View(); - tabView2editorcs.View.Width = Dim.Fill(); - tabView2editorcs.View.Height = Dim.Fill(); - tabView2editorcs.View.CanFocus = true; - this.tvEditor.Width = Dim.Fill(0); - this.tvEditor.Height = Dim.Fill(0); - this.tvEditor.X = 0; - this.tvEditor.Y = 0; - this.tvEditor.Visible = true; - this.tvEditor.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; - this.tvEditor.CanFocus = true; - this.tvEditor.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; - this.tvEditor.AllowsTab = true; - this.tvEditor.AllowsReturn = true; - this.tvEditor.WordWrap = false; - this.tvEditor.Data = "tvEditor"; - this.tvEditor.Text = "using System.Collections.ObjectModel;\r\nusing System.Reflection;\r\nusing System.Tex" + - "t;\r\nusing System.Text.Json;\r\nusing Microsoft.Extensions.Configuration;\r\nusing Mi" + - "crosoft.Extensions.Logging;\r\nusing Serilog;\r\nusing Terminal.Gui;\r\nusing Terminal" + - ".Gui.App;\r\nusing Terminal.Gui.Drawing;\r\nusing Terminal.Gui.Drivers;\r\nusing Termi" + - "nal.Gui.Input;\r\nusing Terminal.Gui.ViewBase;\r\nusing Terminal.Gui.Views;\r\nusing T" + - "erminalGuiDesigner.FromCode;\r\nusing TerminalGuiDesigner.Operations;\r\nusing Termi" + - "nalGuiDesigner.ToCode;\r\nusing TerminalGuiDesigner.UI.Windows;\r\nusing Attribute =" + - " Terminal.Gui.Drawing.Attribute;\r\nusing ILogger = Microsoft.Extensions.Logging.I" + - "Logger;\r\n\r\nnamespace TerminalGuiDesigner.UI;\r\n\r\n/// \r\n/// Root that is visible on loading the\r\n/// applicatio" + - "n. Hooks key and mouse events and mounts as a sub-view whatever file\r\n/// the u" + - "ser opens.\r\n/// \r\npublic class Editor : Toplevel\r\n{\r\n private KeyMa" + - "p keyMap;\r\n private readonly KeyboardManager keyboardManager;\r\n private re" + - "adonly MouseManager mouseManager;\r\n\r\n private Design? viewBeingEdited;\r\n p" + - "rivate bool enableDrag = true;\r\n private bool enableShowFocused = true;\r\n " + - "private bool editing = false;\r\n\r\n private ListView? rootCommandsListView;\r\n " + - " private bool menuOpen;\r\n\r\n /// \r\n /// Set this to have a short " + - "duration message appear in lower right\r\n /// (see ).\r\n /// \r\n private string? flashMessage = null;\r\n\r\n " + - " /// \r\n /// The of the las" + - "t undertaken\r\n /// operation at the time of the last save or null if no save " + - "or last save\r\n /// was before applying any operations.\r\n /// \r\n " + - " internal Guid? LastSavedOperation;\r\n\r\n private static string _keymapPath =" + - " string.Empty;\r\n private static string _logDirectory = string.Empty;\r\n\r\n /" + - "// \r\n /// True to disable logging (must be set before constructing <" + - "see cref=\"Editor\"/>).\r\n /// \r\n public static bool Quiet = false;" + - "\r\n\r\n /// \r\n /// Initializes a new instance of the class.\r\n /// \r\n public Editor()\r\n {\r\n // Bug: Th" + - "is will have strange inheritance behavior if Editor is inherited from.\r\n " + - "this.CanFocus = true;\r\n\r\n if (!Quiet)\r\n {\r\n Logging.Log" + - "ger = CreateLogger();\r\n }\r\n\r\n LoadKeyMap();\r\n\r\n this.keyboa" + - "rdManager = new KeyboardManager(this.keyMap);\r\n this.mouseManager = new M" + - "ouseManager();\r\n this.Closing += this.Editor_Closing;\r\n\r\n this.Bui" + - "ldRootMenu();\r\n }\r\n\r\n private void LoadKeyMap()\r\n {\r\n _keymapPat" + - "h = Path.Combine(\r\n Environment.GetFolderPath(Environment.SpecialFold" + - "er.ApplicationData),\r\n \"TerminalGuiDesigner\", \"keymap.json\");\r\n\r\n " + - " try\r\n {\r\n if (File.Exists(_keymapPath))\r\n {\r\n " + - " var json = File.ReadAllText(_keymapPath);\r\n this.ke" + - "yMap = JsonSerializer.Deserialize(json) ?? new KeyMap();\r\n }\r" + - "\n else\r\n {\r\n this.keyMap = new KeyMap();\r\n " + - " }\r\n\r\n SelectionManager.Instance.SelectedScheme = this.keyM" + - "ap.SelectionColor.Scheme;\r\n }\r\n catch (Exception ex)\r\n {\r\n " + - " // if there is bad yaml use the defaults\r\n ExceptionViewer" + - ".ShowException(\"Failed to read keybindings from configuration file\", ex);\r\n " + - " this.keyMap = new KeyMap();\r\n }\r\n }\r\n\r\n\r\n private void SaveK" + - "eyMap()\r\n {\r\n try\r\n {\r\n var json = JsonSerializer.Se" + - "rialize(this.keyMap);\r\n File.WriteAllText(_keymapPath, json);\r\n " + - " SelectionManager.Instance.SelectedScheme = this.keyMap.SelectionColor.Sche" + - "me;\r\n }\r\n catch (Exception ex)\r\n {\r\n ExceptionVi" + - "ewer.ShowException(\"Failed to save keybindings from configuration file\", ex);\r\n " + - " }\r\n }\r\n\r\n static ILogger CreateLogger()\r\n {\r\n _logDirecto" + - "ry = Path.Combine(\r\n Environment.GetFolderPath(Environment.SpecialFol" + - "der.ApplicationData),\r\n \"TerminalGuiDesigner\", \"logs\");\r\n\r\n //" + - " Configure Serilog to write logs to a file\r\n Log.Logger = new LoggerConfi" + - "guration()\r\n .MinimumLevel.Verbose() // Verbose includes Trace and De" + - "bug\r\n .WriteTo.File(Path.Combine(_logDirectory,\"logfile.txt\"), rollin" + - "gInterval: RollingInterval.Day)\r\n .CreateLogger();\r\n\r\n // Crea" + - "te a logger factory compatible with Microsoft.Extensions.Logging\r\n using " + - "var loggerFactory = LoggerFactory.Create(builder =>\r\n {\r\n buil" + - "der\r\n .AddSerilog(dispose: true) // Integrate Serilog with ILogge" + - "r\r\n .SetMinimumLevel(LogLevel.Trace); // Set minimum log level\r\n " + - " });\r\n\r\n // Get an ILogger instance\r\n return loggerFactory.C" + - "reateLogger(\"Global Logger\");\r\n }\r\n\r\n /// \r\n /// Gets or Sets " + - "a value indicating whether that do not have borders\r\n /// " + - " should have a dotted line rendered around them so users don\'t lose track of whe" + - "re\r\n /// they are on a same-colored background.\r\n /// \r\n // B" + - "UG: Thread-safety\r\n public static bool ShowBorders { get; set; } = true;\r\n\r\n " + - " /// \r\n /// Gets a value indicating whether to enable experimental" + - " features.\r\n /// \r\n // BUG: Thread-safety\r\n public static boo" + - "l Experimental { get; internal set; }\r\n\r\n /// \r\n /// Runs the .\r\n /// \r\n /// Command li" + - "ne options provided by user (or default)\r\n /// indicating what file to open i" + - "f any and other settings e.g. .\r\n public vo" + - "id Run(Options options)\r\n {\r\n if (!string.IsNullOrWhiteSpace(options.P" + - "ath))\r\n {\r\n try\r\n {\r\n var toLoadOrCr" + - "eate = new FileInfo(options.Path);\r\n\r\n if (toLoadOrCreate.Exists)" + - "\r\n {\r\n this.Open(toLoadOrCreate);\r\n " + - " }\r\n else\r\n {\r\n Type toCrea" + - "te = typeof(Window);\r\n\r\n if (!string.IsNullOrWhiteSpace(optio" + - "ns.ViewType))\r\n {\r\n // TODO: We should" + - " probably use something like IsAssignableTo instead\r\n toC" + - "reate = GetSupportedRootViews().FirstOrDefault(v => v.Name.Equals(options.ViewTy" + - "pe)) ?? toCreate;\r\n }\r\n\r\n this.New(toLoadO" + - "rCreate, toCreate, options.Namespace);\r\n }\r\n }\r\n " + - " catch (Exception ex)\r\n {\r\n MessageBox.ErrorQuery" + - "(\"Error Loading Designer\", ex.Message, \"Ok\");\r\n Application.Shutd" + - "own();\r\n return;\r\n }\r\n }\r\n\r\n Application" + - ".KeyDown += (_, k) =>\r\n {\r\n if (this.editing || this.viewBeing" + - "Edited == null)\r\n {\r\n return;\r\n }\r\n\r\n " + - " try\r\n {\r\n if (this.HandleKey(k))\r\n " + - " {\r\n k.Handled = true;\r\n }\r\n }\r\n " + - " catch (Exception ex)\r\n {\r\n ExceptionViewer.S" + - "howException(\"Error processing keystroke\", ex);\r\n }\r\n };\r\n\r\n " + - " Application.MouseEvent += (s, m) =>\r\n {\r\n // if another " + - "window is showing don\'t respond to mouse\r\n if (!this.IsCurrentTop)\r\n " + - " {\r\n return;\r\n }\r\n\r\n // If disabl" + - "ing drag we suppress all but right click (button 3)\r\n if (!m.Flags.Ha" + - "sFlag(MouseFlags.Button3Clicked) && !this.enableDrag)\r\n {\r\n " + - " return;\r\n }\r\n\r\n if (this.editing || this.viewBeingEd" + - "ited == null)\r\n {\r\n return;\r\n }\r\n\r\n " + - " try\r\n {\r\n this.mouseManager.HandleMouse(m, this.vi" + - "ewBeingEdited);\r\n\r\n // right click\r\n if (m.Flags.H" + - "asFlag(this.keyMap.RightClick))\r\n {\r\n var hit " + - "= this.viewBeingEdited.View.HitTest(m, out _, out _);\r\n\r\n if " + - "(hit != null)\r\n {\r\n var d = hit.GetNea" + - "restDesign() ?? this.viewBeingEdited;\r\n if (d != null)\r\n " + - " {\r\n this.CreateAndShowContextM" + - "enu(m, d);\r\n }\r\n }\r\n }\r" + - "\n }\r\n catch (Exception ex)\r\n {\r\n " + - " ExceptionViewer.ShowException(\"Error processing mouse\", ex);\r\n }\r\n " + - " };\r\n\r\n Application.Run(this, this.ErrorHandler);\r\n Applicati" + - "on.Shutdown();\r\n }\r\n\r\n /// \r\n /// Tailors redrawing to add ove" + - "rlays (e.g. showing what is selected etc.).\r\n /// Only runs when a view is op" + - "en and .\r\n /// \r\n protected overrid" + - "e void OnDrawComplete(DrawContext? context)\r\n {\r\n base.OnDrawComplete(" + - "context);\r\n\r\n // if we are not editing a view\r\n if (this.viewBeing" + - "Edited == null)\r\n {\r\n return;\r\n }\r\n\r\n var bounds" + - " = Viewport;\r\n\r\n if (this.enableShowFocused)\r\n {\r\n stri" + - "ng? toDisplay = this.GetLowerRightTextIfAny();\r\n\r\n // and have a desi" + - "gnable view focused\r\n if (toDisplay != null)\r\n {\r\n " + - " // write its name in the lower right\r\n int y = this.GetCo" + - "ntentSize().Height - 1;\r\n int right = bounds.Width - 1;\r\n " + - " var runes = toDisplay.EnumerateRunes().ToList();\r\n var le" + - "n = runes.Count();\r\n\r\n for (int i = 0; i < len; i++)\r\n " + - " {\r\n this.AddRune(right - len + i, y, runes[i]);\r\n " + - " }\r\n }\r\n }\r\n\r\n if (this.mouseManager.SelectionB" + - "ox != null)\r\n {\r\n var box = this.mouseManager.SelectionBox.Val" + - "ue;\r\n for (int x = 0; x < box.Width; x++)\r\n {\r\n " + - " for (int y = 0; y < box.Height; y++)\r\n {\r\n " + - " if (y == 0 || y == box.Height - 1 || x == 0 || x == box.Width - 1)\r\n " + - " {\r\n this.AddRune(box.X + x, box.Y + y, new Rune" + - "(\'.\'));\r\n }\r\n }\r\n }\r\n }\r\n " + - " }\r\n\r\n /// \r\n /// Draws title screen when no view is currently op" + - "en\r\n /// \r\n protected override bool OnDrawingContent()\r\n {\r\n " + - " var r = base.OnDrawingContent();\r\n\r\n if (viewBeingEdited != null)\r" + - "\n {\r\n return true;\r\n }\r\n\r\n var bounds = Viewport" + - ";\r\n\r\n Application.Driver.SetAttribute(new Attribute(Color.Black));\r\n " + - " Application.Driver.FillRect(bounds,\' \');\r\n\r\n var top = new Rectangle(0" + - ", 0, bounds.Width, rootCommandsListView.Frame.Top - 1);\r\n RenderTitle(top" + - ");\r\n \r\n return r;\r\n }\r\n\r\n private void RenderTitle(Rectangle inA" + - "rea)\r\n {\r\n var assembly = typeof(Label).Assembly;\r\n var informa" + - "tionalVersion = assembly\r\n .GetCustomAttribute()?.InformationalVersion\r\n ?? \"unknown\";\r\n\r\n i" + - "f (informationalVersion.Contains(\"+\"))\r\n {\r\n informationalVers" + - "ion = informationalVersion.Substring(0, informationalVersion.IndexOf(\'+\'));\r\n " + - " }\r\n\r\n // The main ASCII art text block\r\n string artText = \"\"\"" + - "\r\n ___________ .__ .__ " + - " \r\n \\__ ___/__________ _____ |__| ____ _____ | " + - " | \r\n | |_/ __ \\_ __ \\/ \\| |/" + - " \\\\__ \\ | | \r\n | |\\ ___/| | " + - "\\/ Y Y \\ | | \\/ __ \\| |__ \r\n |___" + - "_| \\___ >__| |__|_| /__|___| (____ /____/ \r\n " + - " \\/ \\/ \\/ \\/ " + - " \r\n ________ .__ ________ .__ " + - " \r\n / _____/ __ __|__| \\______ \\ ____ _____|__| ____ ____ __________" + - "_ \r\n / \\ ___| | \\ | | | \\_/ __ \\ / ___/ |/ ___\\ / \\_/ __ \\_ __" + - " \\\r\n \\ \\_\\ \\ | / | | ` \\ ___/ \\___ \\| / /_/ > | \\ ___/| | " + - "\\/\r\n \\______ /____/|__| /_______ /\\___ >____ >__\\___ /|___| /\\___ >__| " + - " \r\n \\/ \\/ \\/ \\/ /_____/ \\/ \\/ " + - " \r\n\"\"\";\r\n\r\n // Standardize the text\r\n artText = artText.Replace(\"" + - "\\r\\n\", \"\\n\");\r\n\r\n // The version information line\r\n string version" + - "Line = $\"(Alpha - {informationalVersion} )\";\r\n string logLine = \"Logs - \"" + - " + _logDirectory;\r\n\r\n // Split the ASCII art into lines\r\n var artL" + - "ines = artText.Split(\'\\n\');\r\n\r\n // Calculate the starting point for cente" + - "ring the art text\r\n int artHeight = artLines.Length;\r\n int artWidt" + - "h = artLines.Max(line => line.Length);\r\n\r\n // Check if there\'s enough spa" + - "ce for the ASCII art and the version line\r\n if (inArea.Width < artWidth |" + - "| inArea.Height < (artHeight + 2)) // +2 allows space for version line\r\n " + - "{\r\n // Not enough space, render the simpler title\r\n\r\n // S" + - "imple title and version\r\n string simpleTitle = \"Terminal Gui Designer" + - "\";\r\n int simpleTitleX = inArea.X + (inArea.Width - simpleTitle.Length" + - ") / 2;\r\n int versionLineX = inArea.X + (inArea.Width - versionLine.Le" + - "ngth) / 2;\r\n\r\n // Create the gradient\r\n var gradient = new" + - " Gradient(\r\n new[]\r\n {\r\n new Color(" + - "\"#FF0000\"), // Red\r\n new Color(\"#FF7F00\"), // Orange\r\n " + - " new Color(\"#FFFF00\"), // Yellow\r\n new Color(\"#00FF00\"), // G" + - "reen\r\n new Color(\"#00FFFF\"), // Cyan\r\n new Color(\"" + - "#0000FF\"), // Blue\r\n new Color(\"#8B00FF\") // Violet\r\n " + - " },\r\n new[] { 10 }\r\n );\r\n var fill = ne" + - "w GradientFill(inArea, gradient, GradientDirection.Diagonal);\r\n\r\n // " + - "Render the simple title\r\n for (int i = 0; i < simpleTitle.Length; i++" + - ")\r\n {\r\n int x = simpleTitleX + i;\r\n int" + - " y = inArea.Y + inArea.Height / 2 - 1; // Center the title vertically\r\n\r\n " + - " var colorAtPoint = fill.GetColor(new Point(x, y));\r\n Dri" + - "ver.SetAttribute(new Attribute(new Color(colorAtPoint), new Color(Color.Black)))" + - ";\r\n this.AddRune(x, y, (Rune)simpleTitle[i]);\r\n }\r\n\r\n " + - " // Render the version line below the simple title\r\n for (i" + - "nt i = 0; i < versionLine.Length; i++)\r\n {\r\n int x = v" + - "ersionLineX + i;\r\n int y = inArea.Y + inArea.Height / 2; // Line " + - "below the title\r\n\r\n var colorAtPoint = fill.GetColor(new Point(x," + - " y));\r\n Driver.SetAttribute(new Attribute(new Color(colorAtPoint)" + - ", new Color(Color.Black)));\r\n this.AddRune(x, y, (Rune)versionLin" + - "e[i]);\r\n }\r\n }\r\n else\r\n {\r\n // Enough" + - " space, render the ASCII art block\r\n\r\n int artStartX = inArea.X + (in" + - "Area.Width - artWidth) / 2;\r\n int artStartY = inArea.Y + (inArea.Heig" + - "ht - artHeight - 1) / 2; // -1 for the version line below\r\n\r\n // Crea" + - "te the gradient\r\n var gradient = new Gradient(\r\n new[]" + - "\r\n {\r\n new Color(\"#FF0000\"), // Red\r\n " + - " new Color(\"#FF7F00\"), // Orange\r\n new Color(\"#FFFF00\"), // Yel" + - "low\r\n new Color(\"#00FF00\"), // Green\r\n new Color(\"" + - "#00FFFF\"), // Cyan\r\n new Color(\"#0000FF\"), // Blue\r\n " + - " new Color(\"#8B00FF\") // Violet\r\n },\r\n new[] { " + - "10 }\r\n );\r\n var fill = new GradientFill(inArea, gradient, " + - "GradientDirection.Diagonal);\r\n\r\n // Render the ASCII art block\r\n " + - " for (int i = 0; i < artLines.Length; i++)\r\n {\r\n " + - " string line = artLines[i];\r\n for (int j = 0; j < line.Length; j+" + - "+)\r\n {\r\n int x = artStartX + j;\r\n " + - " int y = artStartY + i;\r\n\r\n var colorAtPoint = fill.Get" + - "Color(new Point(x, y));\r\n Driver.SetAttribute(new Attribute(n" + - "ew Color(colorAtPoint), new Color(Color.Black)));\r\n this.AddR" + - "une(x, y, (Rune)line[j]);\r\n }\r\n }\r\n\r\n // Re" + - "nder the version line below the ASCII art\r\n int versionLineX = inArea" + - ".X + (inArea.Width - versionLine.Length) / 2;\r\n int versionLineY = ar" + - "tStartY + artHeight;\r\n\r\n for (int i = 0; i < versionLine.Length; i++)" + - "\r\n {\r\n int x = versionLineX + i;\r\n int " + - "y = versionLineY;\r\n\r\n var colorAtPoint = fill.GetColor(new Point(" + - "x, y));\r\n Driver.SetAttribute(new Attribute(new Color(colorAtPoin" + - "t), new Color(Color.Black)));\r\n this.AddRune(x, y, (Rune)versionL" + - "ine[i]);\r\n }\r\n\r\n if (Quiet)\r\n {\r\n " + - " return;\r\n }\r\n\r\n // Render the log directory line below t" + - "he version line\r\n int logLineX = inArea.X + (inArea.Width - logLine.L" + - "ength) / 2;\r\n int logLineY = versionLineY+2;\r\n\r\n for (int " + - "i = 0; i < logLine.Length; i++)\r\n {\r\n int x = logLineX" + - " + i;\r\n int y = logLineY;\r\n\r\n var colorAtPoint = f" + - "ill.GetColor(new Point(x, y));\r\n Driver.SetAttribute(new Attribut" + - "e(new Color(colorAtPoint), new Color(Color.Black)));\r\n this.AddRu" + - "ne(x, y, (Rune)logLine[i]);\r\n }\r\n }\r\n }\r\n\r\n\r\n\r\n\r\n /// \r\n /// Event handler for .\r\n /// <" + - "/summary>\r\n /// The key pressed.\r\n /// " + - "True if key is handled.\r\n public bool HandleKey(Key key)\r\n {\r\n " + - " // if another window is showing don\'t respond to hotkeys\r\n if (!thi" + - "s.IsCurrentTop)\r\n {\r\n return false;\r\n }\r\n\r\n if (" + - "this.editing)\r\n {\r\n return false;\r\n }\r\n\r\n // Giv" + - "e the keyboard manager first shot at consuming\r\n // this key e.g. for typ" + - "ing into menus / reordering menus\r\n // etc\r\n if (this.keyboardMana" + - "ger.HandleKey(\r\n SelectionManager.Instance.GetSingleSelectionOrNull()" + - "?.View ?? this, key))\r\n {\r\n return true;\r\n }\r\n\r\n " + - " try\r\n {\r\n this.editing = true;\r\n SelectionManager." + - "Instance.LockSelection = true;\r\n\r\n string keyString = key.ToString( )" + - ";\r\n if (keyString == this.keyMap.ShowContextMenu && !this.menuOpen)\r\n" + - " {\r\n this.CreateAndShowContextMenu(null, null);\r\n " + - " return true;\r\n }\r\n\r\n if (keyString == this.keyM" + - "ap.EditProperties)\r\n {\r\n this.ShowEditProperties();\r\n " + - " return true;\r\n }\r\n\r\n if (keyString == this." + - "keyMap.Copy)\r\n {\r\n this.Copy();\r\n retur" + - "n true;\r\n }\r\n\r\n if (keyString == this.keyMap.Paste)\r\n " + - " {\r\n this.Paste();\r\n return true;\r\n " + - " }\r\n\r\n if (keyString == this.keyMap.ViewSpecificOperations)\r\n " + - " {\r\n this.ShowViewSpecificOperations();\r\n ret" + - "urn true;\r\n }\r\n\r\n if (keyString == this.keyMap.EditRootPro" + - "perties)\r\n {\r\n if (this.viewBeingEdited == null)\r\n " + - " {\r\n return false;\r\n }\r\n\r\n " + - " this.ShowEditProperties(this.viewBeingEdited);\r\n return tr" + - "ue;\r\n }\r\n\r\n if (keyString == this.keyMap.Open)\r\n " + - " {\r\n this.Open();\r\n return true;\r\n }\r\n" + - "\r\n if (keyString == this.keyMap.Save)\r\n {\r\n " + - " this.Save();\r\n return true;\r\n }\r\n\r\n if (ke" + - "yString == this.keyMap.New)\r\n {\r\n this.New();\r\n " + - " return true;\r\n }\r\n\r\n if (keyString == this.keyMap" + - ".ShowHelp)\r\n {\r\n this.ShowHelp();\r\n ret" + - "urn true;\r\n }\r\n\r\n if (keyString == this.keyMap.AddView)\r\n " + - " {\r\n this.ShowAddViewWindow();\r\n return " + - "true;\r\n }\r\n\r\n if (keyString == this.keyMap.ToggleDragging)" + - "\r\n {\r\n this.enableDrag = !this.enableDrag;\r\n " + - " return true;\r\n }\r\n\r\n if (keyString == this.keyMap.Un" + - "do)\r\n {\r\n OperationManager.Instance.Undo();\r\n " + - " return true;\r\n }\r\n\r\n if (keyString == this.keyMap.R" + - "edo)\r\n {\r\n OperationManager.Instance.Redo();\r\n " + - " return true;\r\n }\r\n\r\n if (keyString == this.keyMap." + - "Delete)\r\n {\r\n this.Delete();\r\n return t" + - "rue;\r\n }\r\n\r\n if (keyString == this.keyMap.ToggleShowFocuse" + - "d)\r\n {\r\n this.enableShowFocused = !this.enableShowFocu" + - "sed;\r\n this.SetNeedsDraw();\r\n return true;\r\n " + - " }\r\n\r\n if (keyString == this.keyMap.ToggleShowBorders)\r\n " + - " {\r\n ShowBorders = !ShowBorders;\r\n this.SetNeed" + - "sDraw();\r\n return true;\r\n }\r\n\r\n if (keyStri" + - "ng == this.keyMap.SelectAll)\r\n {\r\n this.SelectAll();\r\n" + - " return true;\r\n }\r\n\r\n if (keyString == this" + - ".keyMap.MoveUp)\r\n {\r\n this.MoveControl(0, -1);\r\n " + - " return true;\r\n }\r\n\r\n if (keyString == this.keyMa" + - "p.MoveDown)\r\n {\r\n this.MoveControl(0, 1);\r\n " + - " return true;\r\n }\r\n\r\n if (keyString == this.keyMap.Mov" + - "eLeft)\r\n {\r\n this.MoveControl(-1, 0);\r\n " + - " return true;\r\n }\r\n\r\n if (keyString == this.keyMap.MoveRig" + - "ht)\r\n {\r\n this.MoveControl(1, 0);\r\n ret" + - "urn true;\r\n }\r\n\r\n if (keyString == this.keyMap.MoveDown)\r\n" + - " {\r\n this.MoveControl(0, 1);\r\n return t" + - "rue;\r\n }\r\n\r\n if (keyString == this.keyMap.MoveLeft)\r\n " + - " {\r\n this.MoveControl(-1, 0);\r\n return true;" + - "\r\n }\r\n\r\n if (keyString == this.keyMap.MoveRight)\r\n " + - " {\r\n this.MoveControl(1, 0);\r\n return true;\r\n " + - " }\r\n\r\n // Fast moving things\r\n switch (key.KeyCod" + - "e)\r\n {\r\n case KeyCode.CursorUp | KeyCode.CtrlMask:\r\n " + - " this.MoveControl(0, -3);\r\n return true;\r\n " + - " case KeyCode.CursorDown | KeyCode.CtrlMask:\r\n t" + - "his.MoveControl(0, 3);\r\n return true;\r\n case K" + - "eyCode.CursorLeft | KeyCode.CtrlMask:\r\n this.MoveControl(-5, " + - "0);\r\n return true;\r\n case KeyCode.CursorRight " + - "| KeyCode.CtrlMask:\r\n this.MoveControl(5, 0);\r\n " + - " return true;\r\n }\r\n }\r\n catch (Exception ex)\r\n " + - " {\r\n ExceptionViewer.ShowException(\"Error\", ex);\r\n }\r\n " + - " finally\r\n {\r\n SelectionManager.Instance.LockSelection = fal" + - "se;\r\n this.editing = false;\r\n }\r\n\r\n return false;\r\n " + - "}\r\n\r\n /// \r\n /// Gets a value indicating whether there have been " + - "any s tracked by the \r\n " + - "/// since the last save.\r\n /// \r\n /// if unsaved changes exist.\r\n public bool HasUnsavedChanges\r\n {" + - "\r\n get\r\n {\r\n var savedOp = this.LastSavedOperation;\r\n " + - " var currentOp = OperationManager.Instance.GetLastAppliedOperation( )?." + - "UniqueIdentifier;\r\n\r\n // if we have nothing saved\r\n if ( s" + - "avedOp == null )\r\n {\r\n // then we must save if we have" + - " done something\r\n return currentOp != null;\r\n }\r\n\r\n " + - " // we must save if the head of the operations stack doesn\'t match what " + - "we saved\r\n // this lets us save, perform action, undo action and then" + - " still consider us saved\r\n return savedOp != currentOp;\r\n }\r\n " + - " }\r\n\r\n\r\n private string GetHelpWithEmptyFormLoaded()\r\n {\r\n return" + - " $\"{this.keyMap.AddView} to Add a View\";\r\n }\r\n\r\n private string GetHelp()\r" + - "\n {\r\n return $\"\"\"\r\n\r\n {this.keyMap.ShowHelp} - Show Hel" + - "p\r\n {this.keyMap.New} - New Window/Class\r\n {this.k" + - "eyMap.Open} - Open a .Designer.cs file\r\n {this.keyMap.Save} - Sav" + - "e an opened .Designer.cs file\r\n {this.keyMap.ShowContextMenu} - S" + - "how right click context menu;\r\n {this.keyMap.AddView} - Add View\r" + - "\n {this.keyMap.ShowSchemes} - Color Schemes\r\n {thi" + - "s.keyMap.ToggleDragging} - Toggle mouse dragging on/off\r\n {this.k" + - "eyMap.ToggleShowFocused} - Toggle show focused view field name\r\n " + - "{this.keyMap.ToggleShowBorders} - Toggle dotted borders for frameless views\r\n " + - " {this.keyMap.EditProperties} - Edit View Properties\r\n " + - " {this.keyMap.ViewSpecificOperations} - View Specific Operations\r\n " + - " {this.keyMap.EditRootProperties} - Edit Root Properties\r\n {thi" + - "s.keyMap.Delete} - Delete selected View\r\n Shift+Cursor - Move foc" + - "used View\r\n Ctrl+Cursor - Move focused View quickly\r\n " + - " Esc - Quit\r\n {this.keyMap.Undo} - Undo\r\n {this" + - ".keyMap.Redo} - Redo\r\n \"\"\";\r\n }\r\n\r\n private void BuildRootM" + - "enu()\r\n {\r\n /* setup views for when we are not editing a\r\n * v" + - "iew (nothing is loaded) so show the generic\r\n * help (open, new etc.) in" + - " the center of the\r\n * screen\r\n */\r\n\r\n var rootCommands =" + - " new ObservableCollection\r\n {\r\n $\"{this.keyMap.ShowHel" + - "p} - Show Help\",\r\n $\"{this.keyMap.New} - New Window/Class\",\r\n " + - " $\"{this.keyMap.Open} - Open a .Designer.cs file\",\r\n $\"Keybindings" + - "\",\r\n };\r\n\r\n // center all the commands\r\n int maxWidth = roo" + - "tCommands.Max(v => v.Length);\r\n for (int i = 0; i < rootCommands.Count; i" + - "++)\r\n {\r\n rootCommands[i] = rootCommands[i].PadBoth(maxWidth);" + - "\r\n }\r\n\r\n this.rootCommandsListView = new ListView()\r\n {\r\n " + - " X = Pos.Center(),\r\n Y = Pos.Percent(75),\r\n Width" + - " = maxWidth,\r\n Height = 4,\r\n };\r\n rootCommandsListView." + - "SetScheme(new Scheme\r\n {\r\n Normal = new Attribute(new Colo" + - "r(Color.White), new Color(Color.Black)),\r\n Focus = new Attribute(new " + - "Color(Color.Black), new Color(Color.White)),\r\n HotNormal = new Attrib" + - "ute(new Color(Color.White), new Color(Color.Black)),\r\n HotFocus = new" + - " Attribute(new Color(Color.White), new Color(Color.Black)),\r\n Disable" + - "d = new Attribute(new Color(Color.Black), new Color(Color.White))\r\n });\r\n" + - "\r\n this.rootCommandsListView.SetSource(rootCommands);\r\n this.rootC" + - "ommandsListView.SelectedItem = 0;\r\n\r\n this.rootCommandsListView.KeyDown +" + - "= (_, e) =>\r\n {\r\n if (e == Key.Enter)\r\n {\r\n " + - " e.Handled = true;\r\n\r\n switch (this.rootCommandsListView.S" + - "electedItem)\r\n {\r\n case 0:\r\n " + - " this.ShowHelp();\r\n break;\r\n case" + - " 1:\r\n this.New();\r\n break;\r\n " + - " case 2:\r\n this.Open();\r\n " + - " break;\r\n case 3:\r\n this.ChangeKey" + - "bindings();\r\n break;\r\n }\r\n }\r\n\r" + - "\n\r\n if (e == this.keyMap.New)\r\n {\r\n this.Ne" + - "w();\r\n }\r\n\r\n if (e == this.keyMap.ShowHelp)\r\n {" + - "\r\n this.ShowHelp();\r\n }\r\n\r\n if (e == this.k" + - "eyMap.Open)\r\n {\r\n this.Open();\r\n }\r\n " + - " };\r\n\r\n this.Add(this.rootCommandsListView);\r\n }\r\n\r\n private void C" + - "hangeKeybindings()\r\n {\r\n var kb = new KeyBindingsUI(keyMap);\r\n " + - "Application.Run(kb);\r\n \r\n if (kb.Save)\r\n {\r\n Sav" + - "eKeyMap();\r\n }\r\n }\r\n\r\n\r\n private void Editor_Closing(object? sender" + - ", ToplevelClosingEventArgs obj)\r\n {\r\n if (this.viewBeingEdited == null" + - ")\r\n {\r\n return;\r\n }\r\n\r\n if (this.HasUnsavedChang" + - "es)\r\n {\r\n int answer = ChoicesDialog.Query(\"Unsaved Changes\", " + - "$\"You have unsaved changes to {this.viewBeingEdited.SourceCode.DesignerFile.Name" + - "}\", \"Save\", \"Don\'t Save\", \"Cancel\");\r\n\r\n if (answer == 0)\r\n " + - " {\r\n this.Save();\r\n }\r\n else\r\n " + - "if (answer == 1)\r\n {\r\n return;\r\n }\r\n " + - " else\r\n {\r\n obj.Cancel = true;\r\n }\r\n " + - " }\r\n }\r\n\r\n private void CreateAndShowContextMenu(MouseEventArgs? m, De" + - "sign? rightClicked)\r\n {\r\n if (this.viewBeingEdited == null)\r\n {" + - "\r\n return;\r\n }\r\n\r\n var selected = SelectionManager.Inst" + - "ance.Selected.ToArray();\r\n\r\n // BUG: This is an improper exception here a" + - "nd could have unexpected behavior if this method is ever called asynchronously.\r" + - "\n var factory = new OperationFactory(\r\n (p, v) => ValueFac" + - "tory.GetNewValue(p.Design, p, v, out var newValue) ? newValue : throw new Operat" + - "ionCanceledException() );\r\n\r\n var operations = factory\r\n .Crea" + - "teOperations(selected, m, rightClicked, out string name)\r\n .Where(o =" + - "> !o.IsImpossible)\r\n .ToArray();\r\n\r\n var setProps = operations" + - ".OfType();\r\n var others = operations\r\n ." + - "Except(setProps)\r\n .GroupBy(k => k.Category, ToMenuItem);\r\n\r\n " + - "var setPropsItems = setProps.Select(ToMenuItem).ToArray();\r\n bool hasProp" + - "sItems = setPropsItems.Any();\r\n\r\n var all = new List();\r\n\r\n " + - " // only add the set properties category if there are some\r\n if (has" + - "PropsItems)\r\n {\r\n all.Add(new MenuItemv2()\r\n {\r\n " + - " Title = name,\r\n Action = () =>\r\n {\r\n " + - " if (selected.Length == 1 || rightClicked != null)\r\n " + - " {\r\n this.ShowEditProperties(rightClicked ?? sel" + - "ected[0]);\r\n }\r\n },\r\n SubMenu =" + - " new Menuv2(setPropsItems)\r\n });\r\n }\r\n\r\n // For each Ex" + - "traOperation grouped by Category\r\n foreach (var g in others)\r\n {\r\n" + - " // if there is no category\r\n if (string.IsNullOrWhiteSpac" + - "e(g.Key))\r\n {\r\n // add the operations with no category" + - " in alphabetical order\r\n all.AddRange(g.OrderBy(mi => mi.Title));" + - "\r\n }\r\n else\r\n {\r\n // Add categor" + - "ies first\r\n all.Insert(\r\n hasPropsItems ? 1 : " + - "0,\r\n new MenuItemv2()\r\n {\r\n " + - " Title = g.Key,\r\n SubMenu = new Menuv2(g.ToArray(" + - "))\r\n });\r\n }\r\n }\r\n\r\n // there\'s noth" + - "ing we can do\r\n if (all.Count == 0)\r\n {\r\n return;\r\n " + - " }\r\n\r\n var menu = new PopoverMenu(all.ToArray());\r\n Point posit" + - "ion;\r\n if (m != null)\r\n {\r\n position = m.Position;\r\n " + - " }\r\n else\r\n {\r\n var d = SelectionManager.Instance.S" + - "elected.FirstOrDefault() ?? this.viewBeingEdited;\r\n var pt = d.View.C" + - "ontentToScreen(new Point(0, 0));\r\n position = new Point(pt.X, pt.Y);\r" + - "\n }\r\n\r\n this.menuOpen = true;\r\n SelectionManager.Instance.L" + - "ockSelection = true;\r\n \r\n if(m != null)\r\n {\r\n m." + - "Handled = true;\r\n }\r\n\r\n \r\n // TODO: rly? you have to pass i" + - "t its own menu items!?\r\n menu.MakeVisible(position);\r\n menu.Accept" + - "ed += (_, _) =>\r\n {\r\n this.menuOpen = false;\r\n Sele" + - "ctionManager.Instance.LockSelection = false;\r\n };\r\n }\r\n\r\n private s" + - "tatic MenuItemv2 ToMenuItem(IOperation operation)\r\n {\r\n return new Men" + - "uItemv2(operation.ToString(), string.Empty, () => Try(() => OperationManager.Ins" + - "tance.Do(operation)));\r\n\r\n static void Try(Action action)\r\n {\r\n " + - " try\r\n {\r\n // BUG: Thread-safety\r\n " + - " // Race conditions because this is not a valid synchronization mechanism\r\n " + - " SelectionManager.Instance.LockSelection = true;\r\n a" + - "ction();\r\n }\r\n catch (Exception ex)\r\n {\r\n " + - " ExceptionViewer.ShowException(\"Operation failed\", ex);\r\n }\r" + - "\n finally\r\n {\r\n SelectionManager.Instance.L" + - "ockSelection = false;\r\n }\r\n }\r\n }\r\n\r\n\r\n private string? " + - "GetLowerRightTextIfAny()\r\n {\r\n if (this.flashMessage != null)\r\n " + - " {\r\n var m = this.flashMessage;\r\n this.flashMessage = null" + - ";\r\n return m;\r\n }\r\n\r\n if (MenuTracker.Instance.Currentl" + - "yOpenMenuItem != null)\r\n {\r\n return $\"Selected: {MenuTracker.I" + - "nstance.CurrentlyOpenMenuItem.Title}\";\r\n }\r\n\r\n var selected = Sele" + - "ctionManager.Instance.Selected.ToArray();\r\n\r\n string name = selected.Leng" + - "th == 1 ? selected[0].FieldName : $\"{selected.Length} objects\";\r\n\r\n if (s" + - "elected.Any())\r\n {\r\n return $\"Selected: {name} ({this.keyMap.E" + - "ditProperties} to Edit, {this.keyMap.ShowHelp} for Help)\";\r\n }\r\n\r\n " + - " return this.GetHelpWithEmptyFormLoaded();\r\n }\r\n\r\n private void SelectAll(" + - ")\r\n {\r\n if (this.viewBeingEdited == null)\r\n {\r\n retu" + - "rn;\r\n }\r\n\r\n var everyone = this.viewBeingEdited.GetAllDesigns()\r\n " + - " .Where(d => !d.IsRoot)\r\n .ToArray();\r\n\r\n SelectionM" + - "anager.Instance.ForceSetSelection(everyone);\r\n }\r\n\r\n private void Paste()\r" + - "\n {\r\n var d = SelectionManager.Instance.GetMostSelectedContainerOrNull" + - "() ?? this.viewBeingEdited;\r\n\r\n if (d != null)\r\n {\r\n va" + - "r paste = new PasteOperation(d);\r\n\r\n if (paste.IsImpossible)\r\n " + - " {\r\n return;\r\n }\r\n\r\n OperationManager.I" + - "nstance.Do(paste);\r\n }\r\n }\r\n\r\n private void Copy()\r\n {\r\n " + - "var copy = new CopyOperation(SelectionManager.Instance.Selected.ToArray());\r\n " + - " OperationManager.Instance.Do(copy);\r\n }\r\n\r\n private void ShowViewSpec" + - "ificOperations()\r\n {\r\n var d = SelectionManager.Instance.GetSingleSele" + - "ctionOrNull();\r\n\r\n if (d != null)\r\n {\r\n var options = d" + - ".GetExtraOperations().Where(o => !o.IsImpossible).ToArray();\r\n\r\n if (" + - "options.Any() && Modals.Get(\"Operations\", \"Ok\", options, null, out var selected)" + - " && selected != null)\r\n {\r\n OperationManager.Instance." + - "Do(selected);\r\n }\r\n }\r\n }\r\n\r\n private void ShowHelp()\r\n " + - " {\r\n if (menuOpen)\r\n {\r\n return;\r\n }\r\n " + - "var menuItem = MenuTracker.Instance.CurrentlyOpenMenuItem;\r\n\r\n // if we a" + - "re in a menu\r\n if (menuItem != null)\r\n {\r\n return;\r\n " + - " }\r\n \r\n ChoicesDialog.Query(\"Help\", this.GetHelp(), \"Ok\");\r\n " + - " }\r\n\r\n private void MoveControl(int deltaX, int deltaY)\r\n {\r\n this" + - ".DoForSelectedViews((d) => new MoveViewOperation(d, deltaX, deltaY));\r\n }\r\n\r\n" + - " private void Delete()\r\n {\r\n if (this.viewBeingEdited == null)\r\n " + - " {\r\n return;\r\n }\r\n\r\n if (SelectionManager.Instance." + - "Selected.Any())\r\n {\r\n var cmd = new DeleteViewOperation(Select" + - "ionManager.Instance.Selected.ToArray());\r\n OperationManager.Instance." + - "Do(cmd);\r\n }\r\n }\r\n\r\n private void DoForSelectedViews(Func operationFunc, bool allowOnRoot = false)\r\n {\r\n if (this.view" + - "BeingEdited == null)\r\n {\r\n return;\r\n }\r\n\r\n var s" + - "elected = SelectionManager.Instance.Selected.ToArray();\r\n\r\n if (selected." + - "Length > 1)\r\n {\r\n var op = new CompositeOperation(\r\n " + - " SelectionManager.Instance.Selected\r\n .Select(operationFunc)" + - ".ToArray());\r\n\r\n OperationManager.Instance.Do(op);\r\n }\r\n " + - " else if (selected.Length == 1)\r\n {\r\n var viewDesign = select" + - "ed.Single();\r\n\r\n // don\'t delete the root view\r\n if (viewD" + - "esign.IsRoot && !allowOnRoot)\r\n {\r\n return;\r\n " + - " }\r\n\r\n OperationManager.Instance.Do(operationFunc(viewDesign));\r\n " + - " }\r\n }\r\n\r\n private void Open()\r\n {\r\n var ofd = new OpenDial" + - "og()\r\n {\r\n Title = \"Open\",\r\n AllowedTypes = new Lis" + - "t(new[] { new AllowedType(\"View\", SourceCodeFile.ExpectedExtension" + - ") })\r\n };\r\n ofd.Layout();\r\n\r\n Application.Run(ofd, this.Err" + - "orHandler);\r\n\r\n if (!ofd.Canceled)\r\n {\r\n try\r\n " + - " {\r\n var path = ofd.Path.ToString();\r\n\r\n if (str" + - "ing.IsNullOrEmpty(path))\r\n {\r\n return;\r\n " + - " }\r\n\r\n this.Open(new FileInfo(path));\r\n }\r\n " + - " catch (Exception ex)\r\n {\r\n ExceptionViewer.S" + - "howException($\"Failed to open \'{ofd.Path}\'\", ex);\r\n }\r\n }\r\n " + - " }\r\n\r\n private bool ErrorHandler(Exception arg)\r\n {\r\n ExceptionView" + - "er.ShowException(\"Global Exception\", arg);\r\n return true;\r\n }\r\n\r\n p" + - "rivate void Open(FileInfo toOpen)\r\n {\r\n var open = new LoadingDialog(t" + - "oOpen);\r\n\r\n // since we are opening a new view we should\r\n // clea" + - "r the history\r\n OperationManager.Instance.ClearUndoRedo();\r\n Desig" + - "n? instance = null;\r\n SelectionManager.Instance.Clear();\r\n\r\n Task." + - "Run(() =>\r\n {\r\n var decompiler = new CodeToView(new SourceCode" + - "File(toOpen));\r\n instance = decompiler.CreateInstance();\r\n })." + - "ContinueWith(\r\n (t, _) =>\r\n {\r\n // no longe" + - "r loading\r\n Application.Invoke(() => Application.RequestStop());\r" + - "\n\r\n if (t.Exception != null)\r\n {\r\n " + - " Application.Invoke(() =>\r\n ExceptionViewer.ShowExcept" + - "ion($\"Failed to open \'{toOpen.Name}\'\", t.Exception));\r\n retur" + - "n;\r\n }\r\n\r\n // if loaded correctly then\r\n " + - " if (instance != null)\r\n {\r\n this.Replace" + - "ViewBeingEdited(instance);\r\n }\r\n },\r\n TaskS" + - "cheduler.FromCurrentSynchronizationContext());\r\n\r\n Application.Run(open, " + - "this.ErrorHandler);\r\n }\r\n\r\n private void New()\r\n {\r\n if (!Modals" + - ".Get(\"Create New View\", \"Ok\", GetSupportedRootViews(), null, out var selected))\r" + - "\n {\r\n return;\r\n }\r\n\r\n var ofd = new SaveDialog()" + - "\r\n {\r\n Title = \"New\",\r\n AllowedTypes = new List() { new AllowedType(\"C# File\", \".cs\") },\r\n Path = \"MyView." + - "cs\",\r\n };\r\n ofd.Style.PreserveFilenameOnDirectoryChanges = true;\r\n" + - " ofd.Layout();\r\n\r\n Application.Run(ofd);\r\n\r\n if (!ofd.Cance" + - "led)\r\n {\r\n try\r\n {\r\n var path = ofd." + - "Path;\r\n\r\n if (string.IsNullOrWhiteSpace(path) || selected == null" + - ")\r\n {\r\n return;\r\n }\r\n\r\n " + - " var file = new FileInfo(path);\r\n\r\n // Check if we are abo" + - "ut to overwrite some files\r\n // and if so warn the user\r\n " + - " var files = new SourceCodeFile(file);\r\n\r\n if(!CodeDomArgs" + - ".IsValidIdentifier(files.ClassName))\r\n {\r\n Cho" + - "icesDialog.Query(\"Invalid Name\",$\"Invalid class name \'{files.ClassName}\'\",\"Ok\");" + - "\r\n return;\r\n }\r\n\r\n var sb = new" + - " StringBuilder();\r\n\r\n if (files.CsFile.Exists)\r\n {" + - "\r\n sb.AppendLine(files.CsFile.Name);\r\n }\r\n\r\n " + - " if (files.DesignerFile.Exists)\r\n {\r\n " + - " sb.AppendLine(files.DesignerFile.Name);\r\n }\r\n\r\n " + - " if (sb.Length > 0)\r\n {\r\n if (!ChoicesDialog" + - ".Confirm(\"Overwrite Files?\", $\"The following files will be overwritten:{Environm" + - "ent.NewLine}{sb.ToString().TrimEnd()}\", \"Ok\", \"Cancel\"))\r\n {\r" + - "\n return; // user canceled overwrite\r\n " + - " }\r\n }\r\n\r\n this.New(file, selected, null);\r\n " + - " }\r\n catch (Exception ex)\r\n {\r\n Except" + - "ionViewer.ShowException($\"Failed to create \'{ofd.Path}\'\", ex);\r\n " + - "throw;\r\n }\r\n }\r\n }\r\n\r\n private static Type[] GetSupporte" + - "dRootViews()\r\n {\r\n return new Type[] { typeof(Window), typeof(Dialog)," + - " typeof(View), typeof(Toplevel) };\r\n }\r\n\r\n private void New(FileInfo toOpe" + - "n, Type typeToCreate, string? explicitNamespace)\r\n {\r\n var viewToCode " + - "= new ViewToCode();\r\n string? ns = explicitNamespace;\r\n\r\n // TODO:" + - " The following two if statements can be combined and run in a loop until the use" + - "r either cancels or gets it right\r\n // if no explicit one\r\n if (st" + - "ring.IsNullOrWhiteSpace(ns))\r\n {\r\n // prompt user for namespac" + - "e\r\n if (!Modals.GetString(\"Namespace\", \"Enter the namespace for your " + - "class\", \"YourNamespace\", out ns))\r\n {\r\n // user cancel" + - "led typing a namespace\r\n return;\r\n }\r\n }\r\n\r\n " + - " // Validate the namespace\r\n if (string.IsNullOrWhiteSpace(ns) || ns." + - "Contains(\' \') || char.IsDigit(ns.First()))\r\n {\r\n MessageBox.Er" + - "rorQuery(\"Invalid Namespace\", \"Namespace must not contain spaces, be empty or be" + - "gin with a number\", \"Ok\");\r\n return;\r\n }\r\n\r\n // since w" + - "e are creating a new view we should\r\n // clear the history\r\n Opera" + - "tionManager.Instance.ClearUndoRedo();\r\n Design? instance = null;\r\n " + - " SelectionManager.Instance.Clear();\r\n\r\n var open = new LoadingDialog(toOp" + - "en);\r\n\r\n // BUG: If this is not awaited, exceptions at any point of it ca" + - "n be thrown at an indeterminate place and time.\r\n Task.Run(() =>\r\n " + - " {\r\n // Create the view files and compile\r\n instance = vie" + - "wToCode.GenerateNewView(toOpen, ns ?? \"YourNamespace\", typeToCreate);\r\n }" + - ").ContinueWith(\r\n (t, _) =>\r\n {\r\n // no lon" + - "ger loading\r\n Application.Invoke(() => Application.RequestStop())" + - ";\r\n\r\n if (t.Exception != null)\r\n {\r\n " + - " Application.Invoke(() =>\r\n ExceptionViewer.ShowExce" + - "ption($\"Failed to create \'{toOpen.Name}\'\", t.Exception));\r\n r" + - "eturn;\r\n }\r\n\r\n // if loaded correctly then\r\n " + - " if (instance != null)\r\n {\r\n this.Rep" + - "laceViewBeingEdited(instance);\r\n }\r\n },\r\n T" + - "askScheduler.FromCurrentSynchronizationContext());\r\n\r\n Application.Run(op" + - "en, this.ErrorHandler);\r\n }\r\n\r\n private void ReplaceViewBeingEdited(Design" + - " design)\r\n {\r\n Application.Invoke(() =>\r\n {\r\n // rem" + - "ove the old view\r\n if (this.viewBeingEdited != null)\r\n {\r\n" + - " // and dispose it\r\n this.Remove(this.viewBeingEdi" + - "ted.View);\r\n this.viewBeingEdited.View.Dispose();\r\n }\r" + - "\n\r\n // remove list view to prevent it stealing keystrokes and jumping" + - " back\r\n // into input focus\r\n this.Remove(this.rootCommand" + - "sListView);\r\n\r\n // Load new instance\r\n this.viewBeingEdite" + - "d = design;\r\n\r\n // And add it to the editing window\r\n this" + - ".Add(this.viewBeingEdited.View);\r\n });\r\n }\r\n\r\n private void Save()\r" + - "\n {\r\n if (this.viewBeingEdited == null)\r\n {\r\n return" + - ";\r\n }\r\n\r\n var viewToCode = new ViewToCode();\r\n\r\n viewToCode" + - ".GenerateDesignerCs(\r\n this.viewBeingEdited,\r\n this.viewBe" + - "ingEdited.View.GetType().BaseType ?? throw new Exception(\"View being edited had " + - "no base class\"));\r\n\r\n this.flashMessage = $\"Saved {this.viewBeingEdited.S" + - "ourceCode.DesignerFile.Name}\";\r\n this.SetNeedsDraw();\r\n\r\n this.Las" + - "tSavedOperation = OperationManager.Instance.GetLastAppliedOperation()?.UniqueIde" + - "ntifier;\r\n }\r\n\r\n private void ShowAddViewWindow()\r\n {\r\n if (this" + - ".viewBeingEdited == null)\r\n {\r\n return;\r\n }\r\n\r\n " + - "// what is the currently selected design\r\n var toAddTo = SelectionManager" + - ".Instance.GetMostSelectedContainerOrNull() ?? this.viewBeingEdited;\r\n\r\n O" + - "perationManager.Instance.Do(\r\n new AddViewOperation(toAddTo));\r\n }" + - "\r\n\r\n private void ShowEditProperties()\r\n {\r\n var d = SelectionManag" + - "er.Instance.GetSingleSelectionOrNull();\r\n if (d != null)\r\n {\r\n " + - " this.ShowEditProperties(d);\r\n }\r\n }\r\n\r\n private void ShowEd" + - "itProperties(Design d)\r\n {\r\n var edit = new EditDialog(d);\r\n Ap" + - "plication.Run(edit, this.ErrorHandler);\r\n }\r\n}\r\n"; - this.tvEditor.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; - tabView2editorcs.View.Add(this.tvEditor); - tabView2.AddTab(tabView2editorcs, false); - Terminal.Gui.Views.Tab tabView2mouseManagercs; - tabView2mouseManagercs = new Terminal.Gui.Views.Tab(); - tabView2mouseManagercs.DisplayText = "MouseManager.cs"; - tabView2mouseManagercs.View = new View(); - tabView2mouseManagercs.View.Width = Dim.Fill(); - tabView2mouseManagercs.View.Height = Dim.Fill(); - tabView2mouseManagercs.View.CanFocus = true; - this.tvMouseManager.Width = Dim.Fill(0); - this.tvMouseManager.Height = Dim.Fill(0); - this.tvMouseManager.X = 0; - this.tvMouseManager.Y = 0; - this.tvMouseManager.Visible = true; - this.tvMouseManager.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; - this.tvMouseManager.CanFocus = true; - this.tvMouseManager.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; - this.tvMouseManager.AllowsTab = true; - this.tvMouseManager.AllowsReturn = true; - this.tvMouseManager.WordWrap = false; - this.tvMouseManager.Data = "tvMouseManager"; - this.tvMouseManager.Text = "using Terminal.Gui;\r\nusing Terminal.Gui.App;\r\nusing Terminal.Gui.Input;\r\nusing Te" + - "rminal.Gui.ViewBase;\r\nusing TerminalGuiDesigner.Operations;\r\n\r\nnamespace Termina" + - "lGuiDesigner.UI;\r\n\r\n/// \r\n/// Manages responding to root mouse e.g. by\r" + - "\n/// dragging around and/or resizing.\r\n/// \r\npubl" + - "ic class MouseManager\r\n{\r\n private DragOperation? dragOperation = null;\r\n " + - "private ResizeOperation? resizeOperation = null;\r\n\r\n /// \r\n /// I" + - "f the user is dragging a selection box then this is the current area\r\n /// th" + - "at is being pulled over or null if no multi select is underway.\r\n /// \r\n private Point? selectionStart = null;\r\n private Point? selectionEnd " + - "= null;\r\n\r\n /// \r\n /// Gets the container that \'drag a box\' selec" + - "tion is occurring in (if any).\r\n /// See also .\r\n " + - " /// \r\n private View? selectionContainer;\r\n\r\n /// \r\n " + - " /// Gets the current \'drag a box\' selection area that is ongoing (if any).\r\n " + - " /// \r\n public Rectangle? SelectionBox => RectExtensions.FromBetwe" + - "enPoints(this.selectionStart, this.selectionEnd);\r\n\r\n /// \r\n /// " + - "Responds to (by changing a \'drag a box\' sele" + - "ction area\r\n /// or starting a resize etc).\r\n /// \r\n /// The reported by .\r\n /// The root that is open in the .\r\n public void H" + - "andleMouse(MouseEventArgs m, Design viewBeingEdited)\r\n {\r\n // start dr" + - "agging\r\n if (m.Flags.HasFlag(MouseFlags.Button1Pressed)\r\n && t" + - "his.resizeOperation == null && this.dragOperation == null && this.selectionStart" + - " == null)\r\n {\r\n View? drag = viewBeingEdited.View.HitTest(m, o" + - "ut bool isBorder, out bool isLowerRight);\r\n\r\n // if user is ctrl+clic" + - "k\r\n if (m.Flags.HasFlag(MouseFlags.ButtonCtrl) && drag != null)\r\n " + - " {\r\n // then add or remove the clicked item from the group" + - " selection\r\n var addOrRemove = drag.GetNearestDesign();\r\n " + - " var selection = SelectionManager.Instance.Selected.ToList();\r\n\r\n " + - " if (addOrRemove != null && selection.Any())\r\n {\r\n " + - " if (selection.Contains(addOrRemove))\r\n {\r\n " + - " selection.Remove(addOrRemove);\r\n }\r\n " + - " else\r\n {\r\n selection.Add(a" + - "ddOrRemove);\r\n }\r\n\r\n SelectionManager.Inst" + - "ance.ForceSetSelection(selection.ToArray());\r\n return;\r\n " + - " }\r\n }\r\n\r\n // if mousing down in empty space\r\n " + - " if (drag != null && drag.IsContainerView() && !isLowerRight && !isBord" + - "er)\r\n {\r\n // start dragging a selection box\r\n " + - " this.selectionContainer = drag;\r\n this.selectionStart = m." + - "Position;\r\n }\r\n\r\n // if nothing is going on yet\r\n " + - " if (drag != null && drag.Data is Design design && drag.SuperView != null\r\n " + - " && this.resizeOperation == null && this.dragOperation == null && this." + - "selectionStart == null)\r\n {\r\n var parent = drag.SuperV" + - "iew;\r\n\r\n var dest = parent.ScreenToContent(m.Position);\r\n\r\n " + - " if (isLowerRight)\r\n {\r\n this.resizeO" + - "peration = new ResizeOperation(design, dest.X, dest.Y);\r\n }\r\n " + - " else\r\n {\r\n var multiSelected = Sel" + - "ectionManager.Instance.Selected.ToArray();\r\n\r\n // if user is " + - "click and drag moving a single view\r\n // in a multi selection" + - ".\r\n if (multiSelected.Contains(design))\r\n " + - "{\r\n // drag all the views at once\r\n " + - " this.dragOperation = new DragOperation(\r\n design,\r\n" + - " dest.X,\r\n dest.Y,\r\n " + - " multiSelected.Except(new[] { design }).ToArray());\r\n " + - " }\r\n else\r\n {\r\n " + - " // else drag only the non selected one\r\n this.d" + - "ragOperation = new DragOperation(design, dest.X, dest.Y, new Design[0]);\r\n " + - " }\r\n\r\n // don\'t begin an impossible drag!\r\n " + - " if (this.dragOperation.IsImpossible)\r\n {\r\n " + - " this.dragOperation = null;\r\n }\r\n " + - " }\r\n }\r\n }\r\n\r\n // continue dragging a selection b" + - "ox\r\n if (m.Flags.HasFlag(MouseFlags.Button1Pressed) && this.selectionStar" + - "t != null)\r\n {\r\n // move selection box to new mouse position\r\n" + - " this.selectionEnd = m.Position;\r\n viewBeingEdited.View.Se" + - "tNeedsDraw();\r\n return;\r\n }\r\n\r\n // continue dragging a " + - "view\r\n if (m.Flags.HasFlag(MouseFlags.Button1Pressed) && this.dragOperati" + - "on?.BeingDragged.View?.SuperView != null)\r\n {\r\n var dest = thi" + - "s.dragOperation?.BeingDragged.View.SuperView.ScreenToContent(m.Position);\r\n\r\n " + - " if (dest != null && this.dragOperation != null)\r\n {\r\n " + - " this.dragOperation.ContinueDrag(dest.Value);\r\n viewBeing" + - "Edited.View.SetNeedsDraw();\r\n // BUG: Method is gone, will this f" + - "unctionality work still without it?\r\n // Application.DoEvents();\r" + - "\n }\r\n }\r\n\r\n // continue resizing\r\n if (m.Flags.H" + - "asFlag(MouseFlags.Button1Pressed)\r\n && this.resizeOperation != null\r\n" + - " && this.resizeOperation.BeingResized.View.SuperView != null)\r\n " + - " {\r\n var dest = this.resizeOperation.BeingResized.View.SuperView.Scr" + - "eenToContent(m.Position);\r\n\r\n this.resizeOperation.ContinueResize(des" + - "t);\r\n\r\n viewBeingEdited.View.SetNeedsDraw();\r\n // BUG: Met" + - "hod is gone, will this functionality work still without it?\r\n // Appl" + - "ication.DoEvents();\r\n }\r\n\r\n // end things (because mouse released)" + - "\r\n if (!m.Flags.HasFlag(MouseFlags.Button1Pressed))\r\n {\r\n " + - " // end selection box\r\n if (this.selectionStart != null && this.Sel" + - "ectionBox != null && this.selectionContainer != null)\r\n {\r\n " + - " SelectionManager.Instance.SetSelection(\r\n this.selectio" + - "nContainer.GetActualSubviews()\r\n .Where(v => v.IntersectsScre" + - "enRect(this.SelectionBox.Value))\r\n .Select(v => v.GetNearestD" + - "esign())\r\n .Where(d => d != null && !d.IsRoot)\r\n " + - " .Cast()\r\n .ToArray());\r\n\r\n this" + - ".selectionStart = null;\r\n this.selectionEnd = null;\r\n " + - " this.selectionContainer = null;\r\n viewBeingEdited.View.SetNee" + - "dsDraw();\r\n // BUG: Method is gone, will this functionality work " + - "still without it?\r\n // Application.DoEvents();\r\n }\r\n\r\n" + - " // end dragging\r\n if (this.dragOperation != null)\r\n " + - " {\r\n // see if we are dragging into a new container\r\n " + - " var dropInto = viewBeingEdited.View.HitTest(m, out _, out _, this.dragO" + - "peration.BeingDragged.View);\r\n\r\n // TODO: this is quite hacky wor" + - "karound for dropping on things like TabView top row. Need\r\n // a" + - " better solution to this.\r\n\r\n // HitTest might return a sub-contr" + - "ol (e.g. TabView top row tabs)\r\n // So grab the nearest user desi" + - "gnable view. That\'s probably what\r\n // they want to move into an" + - "yway.\r\n var into = dropInto?.GetNearestDesign();\r\n\r\n " + - " if (into != null && dropInto != this.dragOperation.DropInto)\r\n " + - " {\r\n // we are dragging into a new container\r\n " + - " this.dragOperation.DropInto = into.View;\r\n\r\n // end drag" + - "\r\n OperationManager.Instance.Do(this.dragOperation);\r\n " + - " this.dragOperation = null;\r\n }\r\n }\r\n\r\n " + - " // end resize\r\n if (this.resizeOperation != null)\r\n " + - " {\r\n // end resize\r\n OperationManager.Instance.D" + - "o(this.resizeOperation);\r\n this.resizeOperation = null;\r\n " + - " }\r\n }\r\n }\r\n}\r\n"; - this.tvMouseManager.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; - tabView2mouseManagercs.View.Add(this.tvMouseManager); - tabView2.AddTab(tabView2mouseManagercs, false); - this.tabView2.ApplyStyleChanges(); - this.Add(this.tabView2); - this.tabView.Width = 50; - this.tabView.Height = Dim.Percent(75); - this.tabView.X = Pos.Percent(75); - this.tabView.Y = 0; - this.tabView.Visible = true; - this.tabView.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; - this.tabView.CanFocus = true; - this.tabView.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; - this.tabView.Data = "tabView"; - this.tabView.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; - this.tabView.MaxTabTextWidth = 30u; - this.tabView.Style.ShowBorder = true; - this.tabView.Style.ShowTopLine = true; - this.tabView.Style.TabsOnBottom = false; - Terminal.Gui.Views.Tab tabViewdiagnosticTools; - tabViewdiagnosticTools = new Terminal.Gui.Views.Tab(); - tabViewdiagnosticTools.DisplayText = "Diagnostic Tools"; - tabViewdiagnosticTools.View = new View(); - tabViewdiagnosticTools.View.Width = Dim.Fill(); - tabViewdiagnosticTools.View.Height = Dim.Fill(); - tabViewdiagnosticTools.View.CanFocus = true; - this.graphView.Width = Dim.Fill(0); - this.graphView.Height = 5; - this.graphView.X = 1; - this.graphView.Y = 0; - this.graphView.Visible = true; - this.graphView.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; - this.graphView.CanFocus = true; - this.graphView.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; - this.graphView.Data = "graphView"; - this.graphView.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; - this.graphView.GraphColor = new Terminal.Gui.Drawing.Attribute(Terminal.Gui.Drawing.Color.White,Terminal.Gui.Drawing.Color.Black); - this.graphView.ScrollOffset = new System.Drawing.PointF(0F, 0F); - this.graphView.MarginLeft = 0u; - this.graphView.MarginBottom = 0u; - this.graphView.CellSize = new System.Drawing.PointF(1F, 1F); - this.graphView.AxisX.Visible = true; - this.graphView.AxisX.Increment = 1F; - this.graphView.AxisX.ShowLabelsEvery = 5u; - this.graphView.AxisX.Minimum = null; - this.graphView.AxisX.Text = null; - this.graphView.AxisY.Visible = true; - this.graphView.AxisY.Increment = 1F; - this.graphView.AxisY.ShowLabelsEvery = 5u; - this.graphView.AxisY.Minimum = null; - this.graphView.AxisY.Text = null; - tabViewdiagnosticTools.View.Add(this.graphView); - tabView.AddTab(tabViewdiagnosticTools, false); - Terminal.Gui.Views.Tab tabViewgitChanges; - tabViewgitChanges = new Terminal.Gui.Views.Tab(); - tabViewgitChanges.DisplayText = "Git Changes"; - tabViewgitChanges.View = new View(); - tabViewgitChanges.View.Width = Dim.Fill(); - tabViewgitChanges.View.Height = Dim.Fill(); - tabViewgitChanges.View.CanFocus = true; - this.label.Width = Dim.Fill(0); - this.label.Height = Dim.Auto(); - this.label.X = 0; - this.label.Y = 0; - this.label.Visible = true; - this.label.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; - this.label.CanFocus = false; - this.label.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; - this.label.Data = "label"; - this.label.Text = "Branch has active pull request #314 without comments."; - this.label.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; - tabViewgitChanges.View.Add(this.label); - tabView.AddTab(tabViewgitChanges, false); - this.tabView.ApplyStyleChanges(); - this.Add(this.tabView); - this.tabView3.Width = Dim.Fill(0); - this.tabView3.Height = 5; - this.tabView3.X = 0; - this.tabView3.Y = Pos.Bottom(tabView); - this.tabView3.Visible = true; - this.tabView3.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; - this.tabView3.CanFocus = true; - this.tabView3.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; - this.tabView3.Data = "tabView3"; - this.tabView3.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; - this.tabView3.MaxTabTextWidth = 30u; - this.tabView3.Style.ShowBorder = true; - this.tabView3.Style.ShowTopLine = true; - this.tabView3.Style.TabsOnBottom = false; - Terminal.Gui.Views.Tab tabView3callStack; - tabView3callStack = new Terminal.Gui.Views.Tab(); - tabView3callStack.DisplayText = "Call Stack"; - tabView3callStack.View = new View(); - tabView3callStack.View.Width = Dim.Fill(); - tabView3callStack.View.Height = Dim.Fill(); - tabView3callStack.View.CanFocus = true; - tabView3.AddTab(tabView3callStack, false); - Terminal.Gui.Views.Tab tabView3watch1; - tabView3watch1 = new Terminal.Gui.Views.Tab(); - tabView3watch1.DisplayText = "Watch 1"; - tabView3watch1.View = new View(); - tabView3watch1.View.Width = Dim.Fill(); - tabView3watch1.View.Height = Dim.Fill(); - tabView3watch1.View.CanFocus = true; - tabView3.AddTab(tabView3watch1, false); - this.tabView3.ApplyStyleChanges(); - this.Add(this.tabView3); - this.statusBar.Width = Dim.Fill(0); - this.statusBar.Height = Dim.Auto(); - this.statusBar.X = 0; - this.statusBar.Y = Pos.AnchorEnd(0); - this.statusBar.Visible = true; - this.statusBar.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; - this.statusBar.CanFocus = true; - this.statusBar.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; - this.statusBar.Data = "statusBar"; - this.statusBar.Text = ""; - this.statusBar.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; - this.f1EditMe = new Terminal.Gui.Views.Shortcut(((Terminal.Gui.Drivers.KeyCode)(1114223u)), "F1 - Edit Me", null); - this.statusBar.Add(this.f1EditMe); - this.Add(this.statusBar); - } - } -} diff --git a/Showcase/Tabs.cs b/Showcase/Tabs.cs deleted file mode 100644 index 0b1d06f7..00000000 --- a/Showcase/Tabs.cs +++ /dev/null @@ -1,20 +0,0 @@ - -//------------------------------------------------------------------------------ - -// -// This code was generated by: -// TerminalGuiDesigner v2.0.0.0 -// You can make changes to this file and they will not be overwritten when saving. -// -// ----------------------------------------------------------------------------- -namespace Showcase { - using Terminal.Gui; - - - public partial class Tabs { - - public Tabs() { - InitializeComponent(); - } - } -} diff --git a/src/Design.cs b/src/Design.cs index 13acf5aa..fbb167ec 100644 --- a/src/Design.cs +++ b/src/Design.cs @@ -5,6 +5,7 @@ using System.Xml.Linq; using NLog; using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Configuration; using Terminal.Gui.Drawing; using Terminal.Gui.Input; @@ -32,6 +33,8 @@ public class Design private readonly List designableProperties; private readonly Logger logger = LogManager.GetCurrentClassLogger(); + public IApplication App { get; } + /// /// View Types for which does not make sense as a user /// configurable field (e.g. there is a Title field instead). @@ -41,17 +44,17 @@ public class Design typeof(FrameView), typeof(TabView), typeof(Window), - typeof(Toplevel), typeof(GraphView), typeof(HexView), - typeof(LineView), + typeof(Line), typeof(ListView), typeof(MenuBar), typeof(TableView), typeof(TabView), typeof(TreeView), typeof(Dialog), - typeof(NumericUpDown) + typeof(NumericUpDown), + typeof(Runnable) }; /// @@ -61,8 +64,10 @@ public class Design /// The private instance name to use for when writing it out /// to or if . /// The view to wrap. - public Design(SourceCodeFile sourceCode, string fieldName, View view) + /// Application instance + public Design(IApplication app, SourceCodeFile sourceCode, string fieldName, View view) { + App = app; this.View = view; this.SourceCode = sourceCode; this.FieldName = fieldName; @@ -211,20 +216,27 @@ public Design CreateSubControlDesign(string name, View subView) if (subView is TextView txt) { // prevent control from responding to events - txt.MouseClick += (s, e) => this.SuppressNativeClickEvents(s, e); + txt.MouseEvent += (s, e) => this.SuppressNativeClickEvents(s, e); } if (subView is TextField tf) { // prevent control from responding to events - tf.MouseClick += (s,e)=>this.SuppressNativeClickEvents(s,e); + tf.MouseEvent += (s,e)=>this.SuppressNativeClickEvents(s,e); + } + + if (subView is Button btn) + { + // prevent control from responding to events + btn.MouseEvent += (s, e) => this.SuppressNativeClickEvents(s, e, true); + btn.MouseEnter += (s, e) => e.Cancel = true; } - if (subView.GetType().IsGenericType(typeof(Slider<>))) + if (subView.GetType().IsGenericType(typeof(LinearRange<>))) { // TODO: Does not seem to work subView.MouseEvent += (s, e) => SuppressNativeClickEvents(s, e,true); - subView.MouseClick += (s, e) => SuppressNativeClickEvents(s,e, true); + subView.MouseEvent += (s, e) => SuppressNativeClickEvents(s,e, true); } if (subView is TreeView tree) @@ -252,11 +264,10 @@ public Design CreateSubControlDesign(string name, View subView) // in non designed subcomponents e.g. the bar of a true color picker. foreach (var v in subView.GetAllNonDesignableSubviews()) { - v.MouseClick += (s,e)=>this.SuppressNativeClickEvents(s,e,true); v.MouseEvent += (s, e) => this.SuppressNativeClickEvents(s, e, true); } - var d = new Design(this.SourceCode, name, subView); + var d = new Design(App, this.SourceCode, name, subView); return d; } @@ -315,79 +326,79 @@ public IEnumerable GetExtraOperations(Point pos) col = dt.Columns[tv.SelectedColumn]; } - yield return new AddColumnOperation(this, null); + yield return new AddColumnOperation(App, this, null); // no columns are selected so don't offer removal. if (col != null) { - yield return new RemoveColumnOperation(this, col); - yield return new RenameColumnOperation(this, col, null); - yield return new MoveColumnOperation(this, col, -1); - yield return new MoveColumnOperation(this, col, 1); + yield return new RemoveColumnOperation(App, this, col); + yield return new RenameColumnOperation(App, this, col, null); + yield return new MoveColumnOperation(App, this, col, -1); + yield return new MoveColumnOperation(App, this, col, 1); } } if (this.IsContainerView || this.IsRoot) { - yield return new AddViewOperation(this); - yield return new PasteOperation(this); + yield return new AddViewOperation(App,this); + yield return new PasteOperation(App, this); } else { var nearestContainer = this.View.GetNearestContainerDesign(); if (nearestContainer != null) { - yield return new AddViewOperation(nearestContainer); + yield return new AddViewOperation(App, nearestContainer); } } - yield return new DeleteViewOperation(this); + yield return new DeleteViewOperation(App, this); switch ( this.View ) { case TabView tabView: { - yield return new AddTabOperation(this, null); + yield return new AddTabOperation(App, this, null); if (tabView.SelectedTab != null) { - yield return new RemoveTabOperation(this, tabView.SelectedTab); - yield return new RenameTabOperation(this, tabView.SelectedTab, null); - yield return new MoveTabOperation(this, tabView.SelectedTab, -1); - yield return new MoveTabOperation(this, tabView.SelectedTab, 1); + yield return new RemoveTabOperation(App, this, tabView.SelectedTab); + yield return new RenameTabOperation(App, this, tabView.SelectedTab, null); + yield return new MoveTabOperation(App, this, tabView.SelectedTab, -1); + yield return new MoveTabOperation(App, this, tabView.SelectedTab, 1); } break; } case MenuBar mb: { - yield return new AddMenuOperation(this, null); + yield return new AddMenuOperation(App, this, null); var menu = pos.IsEmpty ? mb.GetSelectedMenuItem() : mb.ScreenToMenuBarItem(pos.X); if (menu != null) { - yield return new RemoveMenuOperation(this, menu); - yield return new RenameMenuOperation(this, menu, null); - yield return new MoveMenuOperation(this, menu, -1); - yield return new MoveMenuOperation(this, menu, 1); + yield return new RemoveMenuOperation(App, this, menu); + yield return new RenameMenuOperation(App, this, menu, null); + yield return new MoveMenuOperation(App, this, menu, -1); + yield return new MoveMenuOperation(App, this, menu, 1); } break; } case StatusBar sb: { - yield return new AddStatusItemOperation(this, null); + yield return new AddStatusItemOperation(App, this, null); var item = sb.ScreenToMenuBarItem(pos.X); if (item != null) { - yield return new RemoveStatusItemOperation(this, item); - yield return new RenameStatusItemOperation(this, item, null); - yield return new SetShortcutOperation(this, item, null); - yield return new MoveStatusItemOperation(this, item, -1); - yield return new MoveStatusItemOperation(this, item, 1); + yield return new RemoveStatusItemOperation(App, this, item); + yield return new RenameStatusItemOperation(App, this, item, null); + yield return new SetShortcutOperation(App, this, item, null); + yield return new MoveStatusItemOperation(App, this, item, -1); + yield return new MoveStatusItemOperation(App, this, item, 1); } break; @@ -569,16 +580,20 @@ private void CreateSubControlDesigns(View view) } } - private void SuppressNativeClickEvents(object? sender, MouseEventArgs obj, bool alsoSuppressClick = false) + private void SuppressNativeClickEvents(object? sender, Mouse obj, bool alsoSuppressClick = false) { if (alsoSuppressClick) { obj.Handled = true; + if(sender is View v && obj.Flags == MouseFlags.LeftButtonClicked) + { + v.SetFocus(); + } } else { // Suppress everything except single click (selection) - obj.Handled = obj.Flags != MouseFlags.Button1Clicked; + obj.Handled = obj.Flags != MouseFlags.LeftButtonClicked; } } @@ -588,9 +603,9 @@ private void RegisterCheckboxDesignTimeChanges(CheckBox cb) // prevent space toggling the checkbox // (gives better typing experience e.g. "my lovely checkbox") cb.KeyBindings.Remove(Key.Space); - cb.MouseClick += (s, e) => + cb.MouseEvent += (s, e) => { - if (e.Flags.HasFlag(MouseFlags.Button1Clicked)) + if (e.Flags.HasFlag(MouseFlags.LeftButtonClicked)) { e.Handled = true; cb.SetFocus(); @@ -616,9 +631,10 @@ private IEnumerable LoadDesignableProperties() yield return this.CreateSuppressedProperty(nameof(View.CanFocus), true); yield return this.CreateProperty(nameof(this.View.ShadowStyle)); + // its important that this comes before Text because // changing the validator clears the text - if (this.View is TextValidateField) + if (this.View.GetType() == typeof(TextValidateField)) // Use == because subclasses TimeEditor and DateEditor don't support setting custom Provider { yield return this.CreateProperty(nameof(TextValidateField.Provider)); } @@ -628,17 +644,17 @@ private IEnumerable LoadDesignableProperties() yield return this.CreateProperty(nameof(TextField.Secret)); } - if (isGenericType && viewType.GetGenericTypeDefinition() == typeof(Slider<>)) + if (isGenericType && viewType.GetGenericTypeDefinition() == typeof(LinearRange<>)) { - yield return this.CreateProperty(nameof(Slider.Options)); - yield return this.CreateProperty(nameof(Slider.Orientation)); - yield return this.CreateProperty(nameof(Slider.RangeAllowSingle)); - yield return this.CreateProperty(nameof(Slider.AllowEmpty)); - yield return this.CreateProperty(nameof(Slider.MinimumInnerSpacing)); - yield return this.CreateProperty(nameof(Slider.LegendsOrientation)); - yield return this.CreateProperty(nameof(Slider.ShowLegends)); - yield return this.CreateProperty(nameof(Slider.ShowEndSpacing)); - yield return this.CreateProperty(nameof(Slider.Type)); + yield return this.CreateProperty(nameof(LinearRange.Options)); + yield return this.CreateProperty(nameof(LinearRange.Orientation)); + yield return this.CreateProperty(nameof(LinearRange.RangeAllowSingle)); + yield return this.CreateProperty(nameof(LinearRange.AllowEmpty)); + yield return this.CreateProperty(nameof(LinearRange.MinimumInnerSpacing)); + yield return this.CreateProperty(nameof(LinearRange.LegendsOrientation)); + yield return this.CreateProperty(nameof(LinearRange.ShowLegends)); + yield return this.CreateProperty(nameof(LinearRange.ShowEndSpacing)); + yield return this.CreateProperty(nameof(LinearRange.Type)); } if (this.View is SpinnerView) @@ -654,16 +670,11 @@ private IEnumerable LoadDesignableProperties() { // Do not allow tab at design time so that we don't get stuck in the View (adding more tabs each time!) // But let user edit if they want - yield return this.CreateSuppressedProperty(nameof(TextView.AllowsTab), false); - yield return this.CreateProperty(nameof(TextView.AllowsReturn)); + yield return this.CreateSuppressedProperty(nameof(TextView.TabKeyAddsTab), false); + yield return this.CreateProperty(nameof(TextView.EnterKeyAddsLine)); yield return this.CreateProperty(nameof(TextView.WordWrap)); } - if (this.View is Toplevel) - { - yield return this.CreateProperty(nameof(Toplevel.Modal)); - } - // Allow changing the FieldName on anything but root where // such an action would break things badly if (!this.IsRoot) @@ -696,10 +707,9 @@ private IEnumerable LoadDesignableProperties() yield return this.CreateProperty(nameof(Button.IsDefault)); } - if (this.View is LineView) + if (this.View is Line) { - yield return this.CreateProperty(nameof(LineView.LineRune)); - yield return this.CreateProperty(nameof(LineView.Orientation)); + yield return this.CreateProperty(nameof(Line.Orientation)); } if (this.View is ProgressBar) @@ -711,9 +721,10 @@ private IEnumerable LoadDesignableProperties() yield return this.CreateProperty(nameof(ProgressBar.SegmentCharacter)); } + if (this.View is CheckBox) { - yield return this.CreateProperty(nameof(CheckBox.CheckedState)); + yield return this.CreateProperty(nameof(CheckBox.Value)); } if (this.View is ColorPicker cp) { @@ -725,8 +736,6 @@ private IEnumerable LoadDesignableProperties() if (this.View is ListView lv) { yield return this.CreateProperty(nameof(ListView.Source)); - yield return this.CreateProperty(nameof(ListView.AllowsMarking)); - yield return this.CreateProperty(nameof(ListView.AllowsMultipleSelection)); } if (this.View is GraphView gv) @@ -798,9 +807,9 @@ private IEnumerable LoadDesignableProperties() yield return this.CreateSubProperty(nameof(TabStyle.TabsOnBottom), nameof(TabView.Style), tabView.Style); } - if (this.View is RadioGroup) + if (this.View is OptionSelector) { - yield return this.CreateProperty(nameof(RadioGroup.RadioLabels)); + yield return this.CreateProperty(nameof(OptionSelector.Labels)); } if (viewType.IsGenericType(typeof(NumericUpDown<>))) @@ -839,8 +848,8 @@ private bool ShowTextProperty() return false; } - // Do not let Text be set on Slider or Slider<> implementations as weird stuff happens - if(this.View.GetType().Name.StartsWith("Slider") || View is RadioGroup || View.GetType().IsGenericType(typeof(NumericUpDown<>))) + // Do not let Text be set on LinearRange or LinearRange<> implementations as weird stuff happens + if(this.View.GetType().Name.StartsWith("LinearRange") || View is OptionSelector || View.GetType().IsGenericType(typeof(NumericUpDown<>))) { return false; } diff --git a/src/DesignState.cs b/src/DesignState.cs index 6eb8dbfc..9d066c96 100644 --- a/src/DesignState.cs +++ b/src/DesignState.cs @@ -61,6 +61,12 @@ private void DrawContentComplete(object? sender, DrawEventArgs r) if (this.Design.View.IsBorderlessContainerView() && Editor.ShowBorders) { this.DrawBorderlessViewFrame(r.NewViewport); + + foreach(var child in this.Design.View.SubViews) + { + child.SetNeedsDraw(); + child.Draw(r.DrawContext); + } } } @@ -72,7 +78,7 @@ private void DrawBorderlessViewFrame(Rectangle r) SelectionManager.Instance.SelectedScheme.Normal : this.Design.View.GetScheme().Normal; - Application.Driver.SetAttribute(color); + Design.App.Driver!.SetAttribute(color); var v = this.Design.View; diff --git a/src/FromCode/CodeToView.cs b/src/FromCode/CodeToView.cs index 08e2cfc4..06ea2db0 100644 --- a/src/FromCode/CodeToView.cs +++ b/src/FromCode/CodeToView.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Emit; using NLog; using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; using TerminalGuiDesigner.ToCode; @@ -28,14 +29,18 @@ namespace TerminalGuiDesigner.FromCode; /// public class CodeToView { + private readonly IApplication app; + /// /// Initializes a new instance of the class. Opens the provided /// and extracts , etc. /// + /// /// Files on disk that will be read by this class (e.g. MyView.cs and MyView.Designer.cs). /// Thrown if file cannot be parsed, does not exist or has multiple class files in it. - public CodeToView(SourceCodeFile sourceFile) + public CodeToView(IApplication app, SourceCodeFile sourceFile) { + this.app = app; this.SourceFile = sourceFile; // Parse .cs file using Roslyn SyntaxTree @@ -122,7 +127,7 @@ public Design CreateInstance() throw new Exception($"Could not create instance of {instances[0].FullName}", ex); } - var toReturn = new Design(this.SourceFile, Design.RootDesignName, view); + var toReturn = new Design(app, this.SourceFile, Design.RootDesignName, view); toReturn.CreateSubControlDesigns(); // Record the design in Data field so it can be found later by controls diff --git a/src/Keys.yaml b/src/Keys.yaml index 1dbd356a..f5ae5ad8 100644 --- a/src/Keys.yaml +++ b/src/Keys.yaml @@ -13,7 +13,7 @@ ToggleDragging: F3 AddView: F2 ToggleShowFocused: Ctrl+L ToggleShowBorders: Ctrl+B -RightClick: Button3Clicked +RightClick: RightButtonClicked Copy: Ctrl+C Paste: Ctrl+V Rename: Ctrl+R diff --git a/src/MenuBarExtensions.cs b/src/MenuBarExtensions.cs index d3c8ef49..c2d32621 100644 --- a/src/MenuBarExtensions.cs +++ b/src/MenuBarExtensions.cs @@ -20,12 +20,12 @@ public static class MenuBarExtensions { int selected = menuBar.GetNonNullNonPublicFieldValue( "selected" ); - if (selected < 0 || selected >= menuBar.Menus.Length) + if (selected < 0 || selected >= menuBar.SubViews.OfType().Count()) { return null; } - return menuBar.Menus[selected]; + return menuBar.SubViews.OfType().ElementAt(selected); } /// @@ -41,7 +41,7 @@ public static class MenuBarExtensions const int initialWhitespace = 1; const int afterEachItemWhitespace = 2; - if (menuBar.Menus.Length == 0) + if (menuBar.SubViews.OfType().Count() == 0) { return null; } @@ -58,7 +58,7 @@ public static class MenuBarExtensions int distance = initialWhitespace; Dictionary menuXLocations = new(); - foreach (var mb in menuBar.Menus) + foreach (var mb in menuBar.SubViews.OfType()) { menuXLocations.Add(distance, mb); distance += mb.Title.GetColumns() + afterEachItemWhitespace; @@ -77,4 +77,112 @@ public static class MenuBarExtensions // Return the last menu item that begins rendering before this X point return menuXLocations.Last(m => m.Key <= clientPoint.X).Value; } + + /// + /// + /// A MenuItem can have 2 kinds of submenu. If the MenuItem is an element on a + /// is on a MenuBar (i.e. a top level menu item like File, Edit, View etc) then it will + /// have a PopoverMenu. + /// + /// + /// Otherwise if it is a regular MenuItem entry e.g. File->New then it may have an SubMenu + /// ordinary SubMenu i.e. not a popover. + /// + /// The MenuItem to get the menu from. + /// + /// The Menu containing child items, or null if none exists. + public static Menu? GetChildMenu(this MenuItem menuItem, out bool wasPopover) + { + if (menuItem is MenuBarItem mbi) + { + wasPopover = true; + return mbi.PopoverMenu?.Root; + } + + wasPopover = false; + return menuItem.SubMenu; + } + + /// + /// Gets all MenuItem children from this MenuItem's menu. + /// + /// The MenuItem to get children from. + /// + /// List of MenuItem children, or empty list if no menu exists. + public static List GetMenuItems(this MenuItem menuItem, out bool wasPopover) + { + var menu = menuItem.GetChildMenu(out wasPopover); + return menu?.SubViews.OfType().ToList() ?? new List(); + } + + /// + /// Replaces all MenuItem children in this MenuItem's menu with the specified items. + /// This handles the complexity of removing and re-adding items in the correct order. + /// + /// The MenuItem whose children should be replaced. + /// The new list of MenuItems in the desired order. + public static void SetMenuItems(this MenuItem menuItem, List newItems) + { + var menu = menuItem.GetChildMenu(out _); + if (menu == null) + { + return; + } + + // Get all current views and separate MenuItems from non-MenuItems (like Lines) + var allViews = menu.SubViews.ToList(); + var nonMenuItems = allViews.Where(v => v is not MenuItem).ToList(); + + // Remove all MenuItems + foreach (var item in allViews.OfType().ToList()) + { + menu.Remove(item); + } + + // Add new MenuItems in order + foreach (var item in newItems) + { + menu.Add(item); + } + + // Re-add non-MenuItem views (like separators) + foreach (var item in nonMenuItems) + { + menu.Add(item); + } + } + + /// + /// Inserts a MenuItem at the specified index among other MenuItems. + /// This handles finding the correct position among all SubViews. + /// + /// The parent MenuItem to insert into. + /// The index among MenuItems (not SubViews) to insert at. + /// The MenuItem to insert. + public static void InsertMenuItem(this MenuItem menuItem, int index, MenuItem itemToInsert) + { + var items = menuItem.GetMenuItems(out _); + items.Insert(Math.Min(index, items.Count), itemToInsert); + menuItem.SetMenuItems(items); + + itemToInsert.SetFocus(); + } + + /// + /// Removes a MenuItem from this MenuItem's menu. + /// + /// The parent MenuItem to remove from. + /// The MenuItem to remove. + /// True if the item was found and removed. + public static bool RemoveMenuItem(this MenuItem menuItem, MenuItem itemToRemove) + { + var menu = menuItem.GetChildMenu(out _); + if (menu == null) + { + return false; + } + + menu.Remove(itemToRemove); + return true; + } } diff --git a/src/MenuTracker.cs b/src/MenuTracker.cs index 30a65d77..2090f35c 100644 --- a/src/MenuTracker.cs +++ b/src/MenuTracker.cs @@ -1,7 +1,9 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; +using TerminalGuiDesigner.UI; namespace TerminalGuiDesigner; @@ -22,12 +24,6 @@ private MenuTracker() /// public static MenuTracker Instance { get; } = new(); - /// - /// Gets the currently selected if any. To work - /// you must subscribe all to this class so that - /// it can watch etc. - /// - public MenuItem? CurrentlyOpenMenuItem { get; private set; } /// /// Registers listeners for to track open/close. @@ -41,11 +37,41 @@ public void Register(MenuBar mb) return; } - mb.MenuAllClosed += this.MenuAllClosed; - mb.MenuOpened += this.MenuOpened; - mb.MenuClosing += this.MenuClosing; - this.bars.Add(mb); + + // Subscribe to menu open/close events for all MenuBarItems + foreach (var menuBarItem in mb.SubViews.OfType()) + { + SubscribeToMenuBarItem(menuBarItem); + } + } + + private void SubscribeToMenuBarItem(MenuBarItem menuBarItem) + { + if (menuBarItem.PopoverMenu != null) + { + menuBarItem.PopoverMenuOpenChanged += OnPopoverMenuOpenChanged; + } + } + + private void UnsubscribeFromMenuBarItem(MenuBarItem menuBarItem) + { + if (menuBarItem.PopoverMenu != null) + { + menuBarItem.PopoverMenuOpenChanged -= OnPopoverMenuOpenChanged; + } + } + + private void OnPopoverMenuOpenChanged(object? sender, ValueChangedEventArgs e) + { + if (sender is MenuBarItem menuBarItem) + { + // Convert empty menus when closing + if (!e.NewValue) + { + this.ConvertEmptyMenus(); + } + } } /// @@ -54,14 +80,16 @@ public void Register(MenuBar mb) /// to stop tracking. public void UnregisterMenuBar( MenuBar? mb ) { - if ( !bars.TryTake( out mb ) ) + if (mb == null || !bars.TryTake(out mb)) { return; } - mb.MenuAllClosed -= MenuAllClosed; - mb.MenuOpened -= MenuOpened; - mb.MenuClosing -= MenuClosing; + // Unsubscribe from all MenuBarItems + foreach (var menuBarItem in mb.SubViews.OfType()) + { + UnsubscribeFromMenuBarItem(menuBarItem); + } } /// @@ -77,16 +105,16 @@ public void UnregisterMenuBar( MenuBar? mb ) /// The item whose parent you want to find. /// The that owns or. /// null if not found or parent not registered (see ). - /// The immediate parent of . + /// The immediate parent of . Can be MenuBarItem or MenuItem. /// Result may be a top level menu (e.g. File, View) /// or a sub-menu parent (e.g. View=>Windows). - private MenuBarItem? GetParent( MenuItem item, out MenuBar? hostBar ) + private MenuItem? GetParent( MenuItem item, out MenuBar? hostBar ) { foreach (var bar in this.bars) { - foreach (var sub in bar.Menus) + foreach (var sub in bar.SubViews.OfType()) { - var candidate = this.GetParent(item, sub); + var candidate = this.FindParentRecursive(item, sub); if (candidate != null) { @@ -110,13 +138,13 @@ public void UnregisterMenuBar( MenuBar? mb ) /// /// /// When this method returns , the immediate parent of .
Otherwise, - /// + /// . Can be either a MenuBarItem or a MenuItem with a SubMenu. /// /// /// Search is recursive and dips into sub-menus.
For sub-menus it is the immediate parent that is returned. ///
/// A indicating if the search was successful or not. - public bool TryGetParent( MenuItem item, [NotNullWhen( true )] out MenuBar? hostBar, [NotNullWhen( true )] out MenuBarItem? parentItem ) + public bool TryGetParent( MenuItem item, [NotNullWhen( true )] out MenuBar? hostBar, [NotNullWhen( true )] out MenuItem? parentItem ) { var parentCandidate = GetParent( item, out hostBar ); if ( parentCandidate is null ) @@ -144,7 +172,7 @@ public Dictionary ConvertEmptyMenus( ) Dictionary dictionary = []; foreach (var b in this.bars) { - foreach (var bi in b.Menus) + foreach (var bi in b.SubViews.OfType()) { foreach ( ( MenuBarItem? convertedMenuBarItem, MenuItem? convertedMenuItem ) in this.ConvertEmptyMenus( dictionary, b, bi ) ) { @@ -173,89 +201,117 @@ internal static bool ConvertMenuBarItemToRegularItemIfEmpty( MenuBarItem bar, [N { added = null; - // bar still has more children so don't convert - if ( bar.Children.Length != 0 ) - { - return false; - } - - if ( !Instance.TryGetParent( bar, out _, out MenuBarItem? parent ) ) - { - return false; - } + // In the new API, MenuBarItem can only exist at the top level of a MenuBar + // So this conversion doesn't apply the same way + // However, we can check if a MenuItem with a SubMenu has become empty + // and should have its SubMenu removed - int idx = Array.IndexOf( parent.Children, bar ); + // Check if the bar is actually a top-level MenuBarItem (which should keep its structure) + // or if it's being used in a submenu context (which shouldn't happen in the new API) - if (idx < 0) + // For now, we'll check if it has any menu items in its PopoverMenu + if (bar.PopoverMenu?.Root?.SubViews.OfType().Any() == true) { + // bar still has children so don't convert return false; } - // bar has no children so convert to MenuItem - parent.Children[ idx ] = added = new( ) - { - Title = bar.Title, - Data = bar.Data, - ShortcutKey = bar.ShortcutKey - }; - - return true; + // In the new API, we don't convert MenuBarItems to MenuItems + // MenuBarItems stay as MenuBarItems even if empty (they're top-level) + // This method is less relevant in the new structure + return false; } /// private Dictionary ConvertEmptyMenus(Dictionary dictionary, MenuBar bar, MenuBarItem mbi) { - foreach (var c in mbi.Children.OfType()) + // In the new API, we need to look for MenuItems with empty SubMenus + // and potentially remove those SubMenus + if (mbi.PopoverMenu?.Root != null) { - this.ConvertEmptyMenus(dictionary,bar, c); - if ( ConvertMenuBarItemToRegularItemIfEmpty( c, out MenuItem? added)) + foreach (var menuItem in mbi.PopoverMenu.Root.SubViews.OfType()) { - dictionary.TryAdd( c, added ); - - bar.CloseMenu(false); - bar.OpenMenu(); + // Recursively check for empty submenus + ConvertEmptySubMenus(menuItem); } } return dictionary; } - private void MenuClosing(object? sender, MenuClosingEventArgs obj) - { - this.CurrentlyOpenMenuItem = null; - } - - private void MenuOpened(object? sender, MenuOpenedEventArgs obj) + /// + /// Helper method to recursively convert empty submenus + /// In the new API, this removes empty SubMenus from MenuItems + /// + private void ConvertEmptySubMenus(MenuItem menuItem) { - this.CurrentlyOpenMenuItem = obj.MenuItem; - this.ConvertEmptyMenus( ); - } + if (menuItem.SubMenu != null) + { + // First recursively process children + foreach (var child in menuItem.SubMenu.SubViews.OfType()) + { + ConvertEmptySubMenus(child); + } - private void MenuAllClosed(object? sender, EventArgs e) - { - this.CurrentlyOpenMenuItem = null; + // If the submenu is now empty, remove it + if (menuItem.SubMenu.SubViews.OfType().Any() == false) + { + menuItem.SubMenu = null; + } + } } - private MenuBarItem? GetParent(MenuItem item, MenuBarItem sub) + /// + /// Recursively searches for the immediate parent of a MenuItem within a MenuItem hierarchy. + /// Uses the extension methods to simplify the logic. + /// + /// The MenuItem to find the parent of + /// The MenuItem to search within (could be MenuBarItem or MenuItem) + /// The immediate parent MenuItem, or null if not found in this branch + private MenuItem? FindParentRecursive(MenuItem item, MenuItem potentialParent) { - // if we have a reference to the item then - // it means that we are the parent (we contain it) - if (sub.Children.Contains(item)) + // Check if the item is directly in this MenuItem's children + var children = potentialParent.GetMenuItems(out _); + if (children.Contains(item)) { - return sub; + return potentialParent; } - // recursively check dropdowns - foreach (var dropdown in sub.Children.OfType()) + // Recursively check each child's submenu + foreach (var child in children) { - var candidate = this.GetParent(item, dropdown); - - if (candidate != null) + if (child.SubMenu != null) { - return candidate; + var result = FindParentRecursive(item, child); + if (result != null) + { + return result; + } } } return null; } + + internal static MenuItem? GetFocusedMenuItemIfAny(IApplication app) + { + var m = app.Popovers?.Popovers?.FirstOrDefault(p => p.Visible) as PopoverMenu; + + // Don't let user edit the literal popup context menu in main app (that appears + // when right clicking in empty space). + if(m?.Data is string s && s == Editor.DesignerCorePopoverName) + { + return null; + } + + var focused = m?.Focused; + + int maxIterations = 10; + while(focused != null && focused is Menu menu && maxIterations-- > 0) + { + focused = menu.Focused; + } + + return focused as MenuItem; + } } diff --git a/src/Operations/AddViewOperation.cs b/src/Operations/AddViewOperation.cs index 4fdc2cc1..a29d4356 100644 --- a/src/Operations/AddViewOperation.cs +++ b/src/Operations/AddViewOperation.cs @@ -28,7 +28,7 @@ public class AddViewOperation : Operation /// Field name to assign to when wrapping it as a /// . This determines the private field name that it will have in the .Designer.cs /// file. - public AddViewOperation(View add, Design to, string? fieldName) + public AddViewOperation(IApplication app, View add, Design to, string? fieldName) : base(app) { this.add = add; this.fieldName = fieldName == null @@ -41,9 +41,10 @@ public AddViewOperation(View add, Design to, string? fieldName) /// Initializes a new instance of the class. /// This overload asks users what view type they want at runtime (See ). ///
+ /// The application instance. /// A (which should be ) /// to add any newly created to. - public AddViewOperation(Design design) + public AddViewOperation(IApplication app, Design design):base(app) { this.to = design; } @@ -79,15 +80,20 @@ protected override bool DoImpl() { if (this.add == null) { + if (this.App == null) + { + throw new InvalidOperationException("App is required for interactive add operations"); + } + var selectable = ViewFactory.SupportedViewTypes.ToArray(); - if (Modals.Get("Type of Control", "Add", true, selectable, this.TypeNameDelegate, false, null, out var selected) && selected != null) + if (Modals.Get(this.App, "Type of Control", "Add", true, selectable, this.TypeNameDelegate, false, null, out var selected) && selected != null) { if (selected.IsGenericType) { var allowedTTypes = TTypes.GetSupportedTTypesForGenericViewOfType(selected).ToArray(); - if(Modals.Get("Enter a Type for ", "Choose", true, allowedTTypes, this.TypeNameDelegate, false, null, out var selectedTType) && selectedTType != null) + if(Modals.Get(this.App, "Enter a Type for ", "Choose", true, allowedTTypes, this.TypeNameDelegate, false, null, out var selectedTType) && selectedTType != null) { selected = selected.MakeGenericType(new[] { selectedTType }); } @@ -114,7 +120,7 @@ protected override bool DoImpl() var v = this.GetViewToAddTo(); v.Add(this.add); - if (Application.Driver != null) + if (App?.Driver != null) { this.add.SetFocus(); } diff --git a/src/Operations/CompositeOperation.cs b/src/Operations/CompositeOperation.cs index 0c1dcb08..d35b4c05 100644 --- a/src/Operations/CompositeOperation.cs +++ b/src/Operations/CompositeOperation.cs @@ -1,4 +1,5 @@ using System.Collections.ObjectModel; +using Terminal.Gui.App; namespace TerminalGuiDesigner.Operations; @@ -24,7 +25,7 @@ public class CompositeOperation : Operation /// ///
/// All operations to perform in . - public CompositeOperation(params Operation[] operations) + public CompositeOperation(IApplication app, params Operation[] operations):base(app) { this.operations = operations; diff --git a/src/Operations/CopyOperation.cs b/src/Operations/CopyOperation.cs index e7febe4a..2bfd2c41 100644 --- a/src/Operations/CopyOperation.cs +++ b/src/Operations/CopyOperation.cs @@ -1,3 +1,5 @@ +using Terminal.Gui.App; + namespace TerminalGuiDesigner.Operations; /// @@ -14,8 +16,9 @@ public class CopyOperation : Operation /// Initializes a new instance of the class. When /// run copies to . /// + /// The application instance. /// One or more designs to copy. - public CopyOperation(params Design[] toCopy) + public CopyOperation(IApplication app, params Design[] toCopy) : base(app) { if (toCopy.Any()) { diff --git a/src/Operations/DeleteViewOperation.cs b/src/Operations/DeleteViewOperation.cs index c0121607..30936b1f 100644 --- a/src/Operations/DeleteViewOperation.cs +++ b/src/Operations/DeleteViewOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; namespace TerminalGuiDesigner.Operations; @@ -22,8 +23,9 @@ public class DeleteViewOperation : Operation /// /// Initializes a new instance of the class. /// + /// The application instance. /// Wrappers for the you want to delete. - public DeleteViewOperation(params Design[] delete) + public DeleteViewOperation(IApplication app, params Design[] delete) : base(app) { this.delete = delete; this.from = delete.Select(d => d.View.SuperView).ToArray(); diff --git a/src/Operations/DragOperation.cs b/src/Operations/DragOperation.cs index f0082b4f..53c39b51 100644 --- a/src/Operations/DragOperation.cs +++ b/src/Operations/DragOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; using Terminal.Gui.Views; @@ -25,13 +26,14 @@ public partial class DragOperation : Operation /// Initializes a new instance of the class. /// Begins a drag operation in which is moved. ///
+ /// The application instance. /// The primary design being moved. Cannot be the root design (see ). /// The client X coordinate position of the mouse when dragging began. Final location is calculated as an offset from this point. /// This is not necessarily the X/Y of (e.g. if mouse click drag starts in from middle of View area) /// The client Y coordinate position of the mouse when dragging began. Final location is calculated as an offset from this point. /// This is not necessarily the X/Y of (e.g. if mouse click drag starts in from middle of View area) /// Other Designs that are also multi selected and should be dragged at the same time. - public DragOperation(Design beingDragged, int originalX, int originalY, Design[]? alsoDrag) + public DragOperation(IApplication app, Design beingDragged, int originalX, int originalY, Design[]? alsoDrag) : base(app) { // TODO: how does this respond when alsoDrag has some that are not in // same view as beingDragged - write unit test diff --git a/src/Operations/Generics/AddOperation.cs b/src/Operations/Generics/AddOperation.cs index 36f3e692..78221c57 100644 --- a/src/Operations/Generics/AddOperation.cs +++ b/src/Operations/Generics/AddOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; using Terminal.Gui.Views; using TerminalGuiDesigner.UI.Windows; @@ -23,6 +24,7 @@ public abstract class AddOperation : GenericArrayOperation /// /// Initializes a new instance of the class. /// + /// The application instance. /// Method to get the current collection. /// Method to save the new collection to the . /// Method to turn an element into a string (e.g. for ToString()). @@ -30,13 +32,14 @@ public abstract class AddOperation : GenericArrayOperation /// Wrapper for a of type . /// The name to use for the new object or null to prompt user at runtime. public AddOperation( + IApplication app, ArrayGetterDelegate arrayGetter, ArraySetterDelegate arraySetter, StringGetterDelegate stringGetter, ArrayElementFactory elementFactory, Design design, string? name) - : base(arrayGetter, arraySetter, stringGetter, design) + : base(app, arrayGetter, arraySetter, stringGetter, design) { this.name = name; this.elementFactory = elementFactory; @@ -71,7 +74,7 @@ protected override bool DoImpl() if (uniqueName == null) { - if (!Modals.GetString("Name", "Name", $"My{typeof(T2).Name}", out uniqueName)) + if (!Modals.GetString(App!, "Name", "Name", $"My{typeof(T2).Name}", out uniqueName)) { // user canceled adding return false; diff --git a/src/Operations/Generics/GenericArrayElementOperation.cs b/src/Operations/Generics/GenericArrayElementOperation.cs index 2cd12df9..cbe0c4ad 100644 --- a/src/Operations/Generics/GenericArrayElementOperation.cs +++ b/src/Operations/Generics/GenericArrayElementOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; namespace TerminalGuiDesigner.Operations.Generics; @@ -16,6 +17,7 @@ public abstract class GenericArrayElementOperation : GenericArrayOperati /// /// Initializes a new instance of the class. /// + /// The application instance. /// Method for getting current collection. /// Method for storing new collection. /// Method for turning array element to string. @@ -24,12 +26,13 @@ public abstract class GenericArrayElementOperation : GenericArrayOperati /// Thrown if is not in collection /// or is not wrapping . public GenericArrayElementOperation( + IApplication app, ArrayGetterDelegate arrayGetter, ArraySetterDelegate arraySetter, StringGetterDelegate stringGetter, Design design, T2 element) - : base(arrayGetter, arraySetter, stringGetter, design) + : base(app, arrayGetter, arraySetter, stringGetter, design) { this.OperateOn = element; diff --git a/src/Operations/Generics/GenericArrayOperation.cs b/src/Operations/Generics/GenericArrayOperation.cs index 0126d810..f71ecdaa 100644 --- a/src/Operations/Generics/GenericArrayOperation.cs +++ b/src/Operations/Generics/GenericArrayOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; namespace TerminalGuiDesigner.Operations.Generics; @@ -16,16 +17,18 @@ public abstract class GenericArrayOperation : GenericOperation /// /// Initializes a new instance of the class. /// + /// The application instance. /// Method for getting the current collection. /// Method for setting the new collection. /// Method for turning an element into a user readable string (Name, Title etc). /// Wrapper for a of Type . public GenericArrayOperation( + IApplication app, ArrayGetterDelegate arrayGetter, ArraySetterDelegate arraySetter, StringGetterDelegate stringGetter, Design design) - : base(design) + : base(app, design) { this.StringGetter = stringGetter; this.ArrayGetter = arrayGetter; diff --git a/src/Operations/Generics/GenericOperation.cs b/src/Operations/Generics/GenericOperation.cs index 26adb654..4a69e378 100644 --- a/src/Operations/Generics/GenericOperation.cs +++ b/src/Operations/Generics/GenericOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; using Terminal.Gui.Views; @@ -15,9 +16,10 @@ public abstract class GenericOperation : Operation /// /// Initializes a new instance of the class. /// + /// The application instance. /// Design Wrapper for a of Type . /// Thrown if does not wrap a . - public GenericOperation(Design design) + public GenericOperation(IApplication app, Design design) : base(app) { if (design.View is not T t) { diff --git a/src/Operations/Generics/MoveOperation.cs b/src/Operations/Generics/MoveOperation.cs index 6848c98c..debf8a34 100644 --- a/src/Operations/Generics/MoveOperation.cs +++ b/src/Operations/Generics/MoveOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; using Terminal.Gui.Views; @@ -24,6 +25,7 @@ public abstract class MoveOperation : GenericArrayElementOperation /// Initializes a new instance of the class. ///
+ /// The application instance. /// Method for retrieving the Array that should be modified. /// Method to invoke with the new Array order. /// Method for turning an Array element into a string (e.g. for ). @@ -31,13 +33,14 @@ public abstract class MoveOperation : GenericArrayElementOperationThe Array element to move. /// Negative to move left, positive to move right. protected MoveOperation( + IApplication app, ArrayGetterDelegate arrayGetter, ArraySetterDelegate arraySetter, StringGetterDelegate stringGetter, Design design, T2 toMove, int adjustment) - : base(arrayGetter, arraySetter, stringGetter, design, toMove) + : base(app, arrayGetter, arraySetter, stringGetter, design, toMove) { var array = arrayGetter(this.View); diff --git a/src/Operations/Generics/RemoveOperation.cs b/src/Operations/Generics/RemoveOperation.cs index 9d2e453d..b9e023f9 100644 --- a/src/Operations/Generics/RemoveOperation.cs +++ b/src/Operations/Generics/RemoveOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; using Terminal.Gui.Views; @@ -18,18 +19,20 @@ public abstract class RemoveOperation : GenericArrayElementOperation /// Initializes a new instance of the class. ///
+ /// The application instance. /// Method for getting the current collection. /// Method for setting a new collection on a of Type . /// Method for getting the 'name' from an element (Name, Title etc). /// Wrapper for a of Type which owns the collection (e.g. ). /// Element to remove. public RemoveOperation( + IApplication app, ArrayGetterDelegate arrayGetter, ArraySetterDelegate arraySetter, StringGetterDelegate stringGetter, Design design, T2 toRemove) - : base(arrayGetter, arraySetter, stringGetter, design, toRemove) + : base(app, arrayGetter, arraySetter, stringGetter, design, toRemove) { this.idx = Array.IndexOf(arrayGetter(this.View), toRemove); } diff --git a/src/Operations/Generics/RenameOperation.cs b/src/Operations/Generics/RenameOperation.cs index 9b28cddd..32943ebe 100644 --- a/src/Operations/Generics/RenameOperation.cs +++ b/src/Operations/Generics/RenameOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; using Terminal.Gui.Views; using TerminalGuiDesigner.UI.Windows; @@ -22,6 +23,7 @@ public abstract class RenameOperation : GenericArrayElementOperation /// Initializes a new instance of the class. ///
+ /// The application instance. /// Method for retrieving the Array that should be modified. /// Method to invoke with the new Array order. /// Method for turning an Array element into a string (e.g. for ). @@ -30,6 +32,7 @@ public abstract class RenameOperation : GenericArrayElementOperationElement to rename. /// New name to use or null to prompt user for name. protected RenameOperation( + IApplication app, ArrayGetterDelegate arrayGetter, ArraySetterDelegate arraySetter, StringGetterDelegate stringGetter, @@ -37,7 +40,7 @@ protected RenameOperation( Design design, T2 toRename, string? newName) - : base(arrayGetter, arraySetter, stringGetter, design, toRename) + : base(app, arrayGetter, arraySetter, stringGetter, design, toRename) { this.stringSetter = stringSetter; this.originalName = this.StringGetter(toRename); @@ -74,7 +77,7 @@ protected override bool DoImpl() { if (string.IsNullOrWhiteSpace(this.newName)) { - if (Modals.GetString($"Rename {typeof(T2).Name}", "Name", this.originalName?.ToString(), out string? n) && n != null) + if (Modals.GetString(App!, $"Rename {typeof(T2).Name}", "Name", this.originalName?.ToString(), out string? n) && n != null) { this.newName = n; } diff --git a/src/Operations/MenuOperations/AddMenuItemOperation.cs b/src/Operations/MenuOperations/AddMenuItemOperation.cs index 32936047..0de3d842 100644 --- a/src/Operations/MenuOperations/AddMenuItemOperation.cs +++ b/src/Operations/MenuOperations/AddMenuItemOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; namespace TerminalGuiDesigner.Operations.MenuOperations; @@ -21,10 +22,11 @@ public class AddMenuItemOperation : MenuItemOperation /// Initializes a new instance of the class. When /// performed the operation will add a new below . ///
+ /// The application instance. /// An existing to add the new one below. Must be a /// sub-menu (e.g. Open, Copy) not a top level menu (e.g. File, Edit). - public AddMenuItemOperation(MenuItem adjacentTo) - : base(adjacentTo) + public AddMenuItemOperation(IApplication app, MenuItem adjacentTo) + : base(app, adjacentTo) { } @@ -45,7 +47,7 @@ protected override void UndoImpl() return; } - var remove = new RemoveMenuItemOperation(this.added); + var remove = new RemoveMenuItemOperation(App, this.added); remove.Do(); } @@ -65,20 +67,17 @@ private bool Add(MenuItem menuItem) return false; } - var children = this.Parent.Children.ToList(); + var children = this.Parent.GetMenuItems(out _); var currentItemIdx = children.IndexOf(this.OperateOn); - // We are the parent but parents children don't contain - // us. Thats bad. TODO: log this + // We are the parent but parents children don't contain us. That's bad. TODO: log this if (currentItemIdx == -1) { return false; } int insertAt = Math.Max(0, currentItemIdx + 1); - - children.Insert(insertAt, menuItem); - this.Parent.Children = children.ToArray(); + this.Parent.InsertMenuItem(insertAt, menuItem); this.Bar?.SetNeedsDraw(); diff --git a/src/Operations/MenuOperations/AddMenuOperation.cs b/src/Operations/MenuOperations/AddMenuOperation.cs index 63c0e78e..e683391c 100644 --- a/src/Operations/MenuOperations/AddMenuOperation.cs +++ b/src/Operations/MenuOperations/AddMenuOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; @@ -14,12 +15,14 @@ public class AddMenuOperation : AddOperation /// Calling will add a new top level menu to the /// wrapped by . /// + /// The application instance. /// wrapper for a view of Type . /// Optional explicit name to add with or null to prompt user interactively. /// Thrown if the is not wrapping a . - public AddMenuOperation(Design design, string? name) + public AddMenuOperation(IApplication app, Design design, string? name) : base( - (v) => v.Menus, + app, + (v) => v.SubViews.OfType().ToArray(), (v, a) => v.Menus = a, (s) => s.Title.ToString() ?? "blank menu", (v, n) => new(n, new MenuItem[] { new() { Title = ViewFactory.DefaultMenuItemText } }), diff --git a/src/Operations/MenuOperations/ConvertMenuItemToSeperatorOperation.cs b/src/Operations/MenuOperations/ConvertMenuItemToSeperatorOperation.cs index 1aaf598a..b2203736 100644 --- a/src/Operations/MenuOperations/ConvertMenuItemToSeperatorOperation.cs +++ b/src/Operations/MenuOperations/ConvertMenuItemToSeperatorOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; namespace TerminalGuiDesigner.Operations.MenuOperations; @@ -6,19 +7,21 @@ namespace TerminalGuiDesigner.Operations.MenuOperations; /// /// /// Converts a into a Separator (horizontal line in menu). -/// In Terminal.Gui this is represented as a null in the . +/// In the new Terminal.Gui API this is represented as a Line view. /// /// public class ConvertMenuItemToSeperatorOperation : MenuItemOperation { private int removedAtIdx; + private Line? addedLine; /// /// Initializes a new instance of the class. /// - /// A to replace with a separator (null) in it's parent . - public ConvertMenuItemToSeperatorOperation(MenuItem toConvert) - : base(toConvert) + /// The application instance. + /// A to replace with a separator (Line) in it's parent menu. + public ConvertMenuItemToSeperatorOperation(IApplication app, MenuItem toConvert) + : base(app, toConvert) { } @@ -31,15 +34,42 @@ protected override void RedoImpl() /// protected override void UndoImpl() { - if (this.Parent == null || this.OperateOn == null) + if (this.Parent == null || this.OperateOn == null || this.addedLine == null) { return; } - var children = this.Parent.Children.ToList(); + var menu = this.Parent.GetChildMenu(out _); + if (menu == null) + { + return; + } + + // Find the index of the separator line and restore MenuItem at that position + var allViews = menu.SubViews.ToList(); + int lineIdx = allViews.IndexOf(this.addedLine); + + if (lineIdx >= 0) + { + // Remove the separator + menu.Remove(this.addedLine); + + // Rebuild views with MenuItem at the correct position + var nonLineViews = allViews.Where(v => v != this.addedLine).ToList(); + nonLineViews.Insert(Math.Min(lineIdx, nonLineViews.Count), this.OperateOn); + + // Clear and re-add all views in order + foreach (var v in allViews.Where(v => v != this.addedLine).ToList()) + { + menu.Remove(v); + } + + foreach (var v in nonLineViews) + { + menu.Add(v); + } + } - children[this.removedAtIdx] = this.OperateOn; - this.Parent.Children = children.ToArray(); this.Bar?.SetNeedsDraw(); } @@ -51,12 +81,43 @@ protected override bool DoImpl() return false; } - var children = this.Parent.Children.ToList(); + var menu = this.Parent.GetChildMenu(out _); + if (menu == null) + { + return false; + } - this.removedAtIdx = Math.Max(0, children.IndexOf(this.OperateOn)); - children[this.removedAtIdx] = null; + var items = this.Parent.GetMenuItems(out _); + this.removedAtIdx = Math.Max(0, items.IndexOf(this.OperateOn)); + + // Find the actual index in SubViews + var allViews = menu.SubViews.ToList(); + int actualIdx = allViews.IndexOf(this.OperateOn); + + if (actualIdx < 0) + { + return false; + } + + // Remove the MenuItem + menu.Remove(this.OperateOn); + + // Create Line separator and rebuild views with it at the correct position + this.addedLine = new Line { Orientation = Terminal.Gui.ViewBase.Orientation.Horizontal }; + var newViews = allViews.Where(v => v != this.OperateOn).ToList(); + newViews.Insert(Math.Min(actualIdx, newViews.Count), this.addedLine); + + // Clear and re-add all views in order + foreach (var v in allViews.Where(v => v != this.OperateOn).ToList()) + { + menu.Remove(v); + } + + foreach (var v in newViews) + { + menu.Add(v); + } - this.Parent.Children = children.ToArray(); this.Bar?.SetNeedsDraw(); return true; diff --git a/src/Operations/MenuOperations/MenuItemOperation.cs b/src/Operations/MenuOperations/MenuItemOperation.cs index b1942db2..643bc77f 100644 --- a/src/Operations/MenuOperations/MenuItemOperation.cs +++ b/src/Operations/MenuOperations/MenuItemOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; namespace TerminalGuiDesigner.Operations.MenuOperations; @@ -12,12 +13,13 @@ public abstract class MenuItemOperation : Operation /// /// Initializes a new instance of the class. /// + /// The application instance. /// that you will operate on. - protected MenuItemOperation(MenuItem operateOn) + protected MenuItemOperation(IApplication app, MenuItem operateOn) : base(app) { // if taking a new line add an extra menu item // menuItem.Parent doesn't work for root menu items - if ( !MenuTracker.Instance.TryGetParent( operateOn, out MenuBar? bar, out MenuBarItem? parent ) ) + if ( !MenuTracker.Instance.TryGetParent( operateOn, out MenuBar? bar, out MenuItem? parent ) ) { IsImpossible = true; return; @@ -40,8 +42,9 @@ protected MenuItemOperation(MenuItem operateOn) /// This may be a top level entry on the (File, Edit etc) /// or it could be a sub entry of that if is in a sub menu /// (e.g. File=>*New*=>Document - where Parent is *New*). + /// In the new API, this can be either a MenuBarItem or a MenuItem with a SubMenu. /// - public MenuBarItem? Parent { get; private set; } + public MenuItem? Parent { get; private set; } /// /// Gets the that will be affected by this . diff --git a/src/Operations/MenuOperations/MoveMenuItemLeftOperation.cs b/src/Operations/MenuOperations/MoveMenuItemLeftOperation.cs index eedc9240..098694b0 100644 --- a/src/Operations/MenuOperations/MoveMenuItemLeftOperation.cs +++ b/src/Operations/MenuOperations/MoveMenuItemLeftOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; namespace TerminalGuiDesigner.Operations.MenuOperations; @@ -21,12 +22,20 @@ public class MoveMenuItemLeftOperation : MenuItemOperation /// Initializes a new instance of the class. /// This operation pulls a out of a sub-menu onto the level above. /// + /// The application instance. /// The to move to parent containing menu. - public MoveMenuItemLeftOperation(MenuItem toMove) - : base(toMove) + public MoveMenuItemLeftOperation(IApplication app, MenuItem toMove) + : base(app, toMove) { - // command is already invalid or user is trying to move a menu item that is not in a sub-menu - if (this.IsImpossible || this.Bar == null || this.Bar.Menus.Any(m => m.Children.Contains(toMove))) + // command is already invalid + if (this.IsImpossible || this.Bar == null) + { + this.IsImpossible = true; + return; + } + + // Check if the item is in a top-level PopoverMenu (can't move left from there) + if (this.Bar.SubViews.OfType().Any(m => m.PopoverMenu?.Root?.SubViews.Contains(toMove) == true)) { this.IsImpossible = true; return; @@ -34,7 +43,8 @@ public MoveMenuItemLeftOperation(MenuItem toMove) if (this.Parent != null) { - this.pulledFromIndex = Array.IndexOf(this.Parent.Children, this.OperateOn); + var items = this.Parent.GetMenuItems(out _); + this.pulledFromIndex = items.IndexOf(this.OperateOn); } } @@ -52,7 +62,7 @@ protected override void UndoImpl() return; } - new MoveMenuItemRightOperation(this.OperateOn) + new MoveMenuItemRightOperation(App, this.OperateOn) { InsertionIndex = this.pulledFromIndex, } @@ -67,22 +77,19 @@ protected override bool DoImpl() return false; } - if ( !MenuTracker.Instance.TryGetParent( Parent, out _, out MenuBarItem? parentsParent ) ) + if (!MenuTracker.Instance.TryGetParent(Parent, out _, out MenuItem? parentsParent)) { return false; } - // Figure out where the parent MenuBarItem was in the list because - // after we remove ourselves from its sublist it might - // turn into a MenuItem (i.e. we loose the reference). - var children = parentsParent.Children.ToList(); - var parentsIdx = children.IndexOf(this.Parent); + // Figure out where the parent is in the list + var parentsParentItems = parentsParent.GetMenuItems(out _); + var parentsIdx = parentsParentItems.IndexOf(this.Parent); - // remove us - if (new RemoveMenuItemOperation(this.OperateOn).Do()) + // remove us from our current location + if (new RemoveMenuItemOperation(App, this.OperateOn).Do()) { - // We are the parent but parents children don't contain - // us. That's bad. TODO: log this + // We are the parent but parents children don't contain us. That's bad. TODO: log this if (parentsIdx == -1) { return false; @@ -90,8 +97,8 @@ protected override bool DoImpl() int insertAt = Math.Max(0, parentsIdx + 1); - children.Insert(insertAt, this.OperateOn); - parentsParent.Children = children.ToArray(); + // Insert into the parent's parent menu + parentsParent.InsertMenuItem(insertAt, this.OperateOn); MenuTracker.Instance.ConvertEmptyMenus(); diff --git a/src/Operations/MenuOperations/MoveMenuItemOperation.cs b/src/Operations/MenuOperations/MoveMenuItemOperation.cs index 1c171149..97b28e90 100644 --- a/src/Operations/MenuOperations/MoveMenuItemOperation.cs +++ b/src/Operations/MenuOperations/MoveMenuItemOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; namespace TerminalGuiDesigner.Operations.MenuOperations; @@ -17,11 +18,12 @@ public class MoveMenuItemOperation : MenuItemOperation /// /// Initializes a new instance of the class. /// + /// The application instance. /// The that should change places relative to other /// on its . /// True to move up on the screen (array index decreases). False to move down on the screen (array index increases). - public MoveMenuItemOperation(MenuItem toMove, bool up) - : base(toMove) + public MoveMenuItemOperation(IApplication app, MenuItem toMove, bool up) + : base(app, toMove) { this.up = up; @@ -32,7 +34,7 @@ public MoveMenuItemOperation(MenuItem toMove, bool up) return; } - this.siblings = this.Parent.Children.ToList(); + this.siblings = this.Parent.GetMenuItems(out _); this.currentItemIdx = this.siblings.IndexOf(this.OperateOn); if (this.currentItemIdx < 0) @@ -79,7 +81,9 @@ private bool Move(int amount) // push it in at the destination this.siblings.Insert(moveTo, this.OperateOn); - this.Parent.Children = this.siblings.ToArray(); + + // Update the menu with the new order + this.Parent.SetMenuItems(this.siblings); this.Bar?.SetNeedsDraw(); diff --git a/src/Operations/MenuOperations/MoveMenuItemRightOperation.cs b/src/Operations/MenuOperations/MoveMenuItemRightOperation.cs index 8a96b0da..9fec2c99 100644 --- a/src/Operations/MenuOperations/MoveMenuItemRightOperation.cs +++ b/src/Operations/MenuOperations/MoveMenuItemRightOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; namespace TerminalGuiDesigner.Operations.MenuOperations; @@ -14,11 +15,22 @@ public class MoveMenuItemRightOperation : MenuItemOperation /// /// Initializes a new instance of the class. /// + /// The application instance. /// Moves the to the sub-menu of the above it. - public MoveMenuItemRightOperation(MenuItem toMove) - : base(toMove) + public MoveMenuItemRightOperation(IApplication app, MenuItem toMove) + : base(app, toMove) { - if (this.Parent?.GetChildrenIndex(toMove) == 0) + if (this.Parent == null || this.OperateOn == null) + { + this.IsImpossible = true; + return; + } + + var items = this.Parent.GetMenuItems(out _); + int idx = items.IndexOf(toMove); + + // Can't move right if we're the first item (no item above to become parent) + if (idx <= 0) { this.IsImpossible = true; } @@ -39,7 +51,7 @@ protected override void RedoImpl() return; } - new MoveMenuItemRightOperation(this.OperateOn).Do(); + new MoveMenuItemRightOperation(App, this.OperateOn).Do(); } /// @@ -50,7 +62,7 @@ protected override void UndoImpl() return; } - new MoveMenuItemLeftOperation(this.OperateOn).Do(); + new MoveMenuItemLeftOperation(App, this.OperateOn).Do(); } /// @@ -62,7 +74,7 @@ protected override bool DoImpl() } // When user hits shift right - var children = this.Parent.Children.ToList(); + var children = this.Parent.GetMenuItems(out _); var currentItemIdx = children.IndexOf(this.OperateOn); var aboveIdx = currentItemIdx - 1; @@ -72,31 +84,26 @@ protected override bool DoImpl() return false; } - var addTo = this.ConvertToMenuBarItem(children, aboveIdx); - - // pull us out - children.Remove(this.OperateOn); + // Get or create a submenu on the item above + var itemAbove = children[aboveIdx]; + if (itemAbove.SubMenu == null) + { + itemAbove.SubMenu = new Menu(); + } - // add us to the sub-menu - var submenuChildren = addTo.Children.ToList(); + // Remove us from current menu + this.Parent.RemoveMenuItem(this.OperateOn); + // Add us to the submenu of the item above if (this.InsertionIndex != null) { - submenuChildren.Insert( - Math.Min(this.InsertionIndex.Value, submenuChildren.Count), - this.OperateOn); + itemAbove.InsertMenuItem(this.InsertionIndex.Value, this.OperateOn); } else { - submenuChildren.Add(this.OperateOn); + itemAbove.SubMenu.Add(this.OperateOn); } - // update the main menu - this.Parent.Children = children.ToArray(); - - // update the sub-menu - addTo.Children = submenuChildren.ToArray(); - this.Bar?.SetNeedsDraw(); return true; @@ -109,9 +116,12 @@ private MenuBarItem ConvertToMenuBarItem(List children, int idx) return mb; } - var added = new MenuBarItem(children[idx].Title, new MenuItem[0], null); + var added = new MenuBarItem() + { + Title = children[idx].Title + }; added.Data = children[idx].Data; - added.ShortcutKey = children[idx].ShortcutKey; + added.Key = children[idx].Key; children.RemoveAt(idx); children.Insert(idx, added); diff --git a/src/Operations/MenuOperations/MoveMenuOperation.cs b/src/Operations/MenuOperations/MoveMenuOperation.cs index 6e918151..6cd9f32c 100644 --- a/src/Operations/MenuOperations/MoveMenuOperation.cs +++ b/src/Operations/MenuOperations/MoveMenuOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; @@ -15,12 +16,14 @@ public class MoveMenuOperation : MoveOperation /// Creates an operation that will change the ordering of top level menus within /// a . /// + /// The application instance. /// Wrapper for a . /// The top level menu to move. /// Negative to move menu left, positive to move menu right. - public MoveMenuOperation(Design design, MenuBarItem toMove, int adjustment) + public MoveMenuOperation(IApplication app, Design design, MenuBarItem toMove, int adjustment) : base( - v => v.Menus, + app, + v => v.SubViews.OfType().ToArray(), (v, a) => v.Menus = a, s => s.Title.ToString() ?? "blank menu", design, diff --git a/src/Operations/MenuOperations/RemoveMenuItemOperation.cs b/src/Operations/MenuOperations/RemoveMenuItemOperation.cs index 15958a8a..9617f43b 100644 --- a/src/Operations/MenuOperations/RemoveMenuItemOperation.cs +++ b/src/Operations/MenuOperations/RemoveMenuItemOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; using Terminal.Gui.Views; @@ -38,9 +39,10 @@ public class RemoveMenuItemOperation : MenuItemOperation /// /// Initializes a new instance of the class. /// + /// The application instance. /// The that should be removed (deleted). - public RemoveMenuItemOperation(MenuItem toRemove) - : base(toRemove) + public RemoveMenuItemOperation(IApplication app, MenuItem toRemove) + : base(app, toRemove) { } @@ -66,12 +68,8 @@ protected override void UndoImpl() return; } - this.Parent.Children = - [ - .. Parent.Children[ .. removedAtIdx ], - this.OperateOn, - .. Parent.Children[ removedAtIdx .. ] - ]; + // Re-insert the item at its original position + this.Parent.InsertMenuItem(this.removedAtIdx, this.OperateOn); this.Bar?.SetNeedsDraw(); // if any MenuBarItem were converted to vanilla MenuItem @@ -81,12 +79,18 @@ .. Parent.Children[ removedAtIdx .. ] { foreach (var converted in this.convertedMenuBars) { - if(MenuTracker.Instance.TryGetParent(converted.Value,out _, out MenuBarItem? grandparent)) + if(MenuTracker.Instance.TryGetParent(converted.Value, out _, out MenuItem? grandparent)) { - int replacementIndex = Array.IndexOf(grandparent.Children, converted.Value); - if(replacementIndex >=0 && replacementIndex < grandparent.Children.Length) + if (grandparent != null) { - grandparent.Children[ replacementIndex ] = converted.Key; + var grandparentItems = grandparent.GetMenuItems(out _); + int replacementIndex = grandparentItems.IndexOf(converted.Value); + if(replacementIndex >= 0 && replacementIndex < grandparentItems.Count) + { + // Replace the converted MenuItem with the MenuBarItem + grandparentItems[replacementIndex] = converted.Key; + grandparent.SetMenuItems(grandparentItems); + } } } } @@ -96,23 +100,31 @@ .. Parent.Children[ removedAtIdx .. ] // side effect of the removal then put them back if (this.prunedEmptyTopLevelMenus != null && this.Bar != null) { - var l = this.Bar.Menus.ToList(); + var currentMenus = this.Bar.SubViews.OfType().ToList(); - // for each index they used to be at - foreach (var kvp in this.prunedEmptyTopLevelMenus.OrderBy(k => k)) + // for each index they used to be at, add them back + foreach (var kvp in this.prunedEmptyTopLevelMenus.OrderBy(k => k.Key)) { - // put them back - l.Insert(kvp.Key, kvp.Value); + currentMenus.Insert(Math.Min(kvp.Key, currentMenus.Count), kvp.Value); } - this.Bar.Menus = l.ToArray(); + // Rebuild the MenuBar with all menus in order + foreach (var menuBarItem in this.Bar.SubViews.OfType().ToList()) + { + this.Bar.Remove(menuBarItem); + } + + foreach (var menuBarItem in currentMenus) + { + this.Bar.Add(menuBarItem); + } } if (this.Bar != null && this.barRemovedFrom != null) { this.barRemovedFrom.Add(this.Bar); - // lets clear this in case the user some + // lets clear this in case the user somehow // manages to undo this command multiple // times this.barRemovedFrom = null; @@ -127,12 +139,10 @@ protected override bool DoImpl() return false; } - this.removedAtIdx = Math.Max( 0, Array.IndexOf( Parent.Children, OperateOn ) ); - this.Parent.Children = - [ - .. Parent.Children[ ..removedAtIdx ], - .. Parent.Children[ ( removedAtIdx + 1 ).. ] - ]; + var items = this.Parent.GetMenuItems(out _); + this.removedAtIdx = Math.Max(0, items.IndexOf(this.OperateOn)); + + this.Parent.RemoveMenuItem(this.OperateOn); this.Bar?.SetNeedsDraw(); if (this.Bar != null) @@ -141,22 +151,25 @@ .. Parent.Children[ ( removedAtIdx + 1 ).. ] } // if a top level menu now has no children - var empty = this.Bar?.Menus.Where(bi => bi.Children.Length == 0).ToArray(); - if (empty?.Any() == true) + var allMenus = this.Bar?.SubViews.OfType().ToArray(); + var empty = allMenus?.Where(bi => bi.PopoverMenu?.Root?.SubViews.OfType().Any() != true).ToArray(); + if (empty?.Any() == true && allMenus != null) { // remember where they were - this.prunedEmptyTopLevelMenus = empty.ToDictionary(e => Array.IndexOf(this.Bar.Menus, e), v => v); + this.prunedEmptyTopLevelMenus = empty.ToDictionary(e => Array.IndexOf(allMenus, e), v => v); - // and remove them - this.Bar.Menus = this.Bar.Menus.Except(this.prunedEmptyTopLevelMenus.Values).ToArray(); + // and remove them from the MenuBar + foreach (var emptyMenu in empty) + { + this.Bar!.Remove(emptyMenu); + } } // if we just removed the last menu header // leaving a completely blank menu bar - if (this.Bar?.Menus.Length == 0 && this.Bar.SuperView != null) + if (this.Bar?.SubViews.OfType().Any() != true && this.Bar?.SuperView != null) { // remove the bar completely - this.Bar.CloseMenu(false); this.barRemovedFrom = this.Bar.SuperView; this.barRemovedFrom.Remove(this.Bar); } diff --git a/src/Operations/MenuOperations/RemoveMenuOperation.cs b/src/Operations/MenuOperations/RemoveMenuOperation.cs index 8ebe9dda..309fccf8 100644 --- a/src/Operations/MenuOperations/RemoveMenuOperation.cs +++ b/src/Operations/MenuOperations/RemoveMenuOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; @@ -14,12 +15,14 @@ public class RemoveMenuOperation : RemoveOperation /// /// Initializes a new instance of the class. /// + /// The application instance. /// Wrapper for a upon which you wish to operate. /// The to remove. /// Thrown if does not wrap a . - public RemoveMenuOperation(Design design, MenuBarItem toRemove) + public RemoveMenuOperation(IApplication app, Design design, MenuBarItem toRemove) : base( - v => v.Menus, + app, + v => v.SubViews.OfType().ToArray(), (v, a) => v.Menus = a, s => s.Title.ToString() ?? "blank menu", design, diff --git a/src/Operations/MenuOperations/RenameMenuItemOperation.cs b/src/Operations/MenuOperations/RenameMenuItemOperation.cs index b7ff46e7..0d07d10b 100644 --- a/src/Operations/MenuOperations/RenameMenuItemOperation.cs +++ b/src/Operations/MenuOperations/RenameMenuItemOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; using TerminalGuiDesigner.ToCode; using TerminalGuiDesigner.UI.Windows; @@ -21,9 +22,10 @@ public class RenameMenuItemOperation : MenuItemOperation /// Initializes a new instance of the class. /// Note that this operation renames the field name in .Designer.cs not the . /// + /// The application instance. /// The column to choose a new private field name for. - public RenameMenuItemOperation(MenuItem toRename) - : base(toRename) + public RenameMenuItemOperation(IApplication app, MenuItem toRename) + : base(app, toRename) { this.originalName = toRename.Data as string; } @@ -55,7 +57,7 @@ protected override bool DoImpl() } // TODO: make this an optional constructor field so it can be unit tested - if (Modals.GetString("Menu Item Name", "Name", this.originalName, out string? newName)) + if (Modals.GetString(App!, "Menu Item Name", "Name", this.originalName, out string? newName)) { if (string.IsNullOrWhiteSpace(newName)) { diff --git a/src/Operations/MenuOperations/RenameMenuOperation.cs b/src/Operations/MenuOperations/RenameMenuOperation.cs index 62177d33..dda95227 100644 --- a/src/Operations/MenuOperations/RenameMenuOperation.cs +++ b/src/Operations/MenuOperations/RenameMenuOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; @@ -12,12 +13,14 @@ public class RenameMenuOperation : RenameOperation /// /// Initializes a new instance of the class. /// + /// The application instance. /// Wrapper for a upon which you wish to operate. /// The to rename. /// The new name to use. - public RenameMenuOperation(Design design, MenuBarItem toRename, string? newName) + public RenameMenuOperation(IApplication app, Design design, MenuBarItem toRename, string? newName) : base( - v => v.Menus, + app, + v => v.SubViews.OfType().ToArray(), (v, a) => v.Menus = a, s => s.Title.ToString() ?? "blank menu", (v, s) => v.Title = s, diff --git a/src/Operations/MoveViewOperation.cs b/src/Operations/MoveViewOperation.cs index 585f33ed..a361b13f 100644 --- a/src/Operations/MoveViewOperation.cs +++ b/src/Operations/MoveViewOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; namespace TerminalGuiDesigner.Operations; @@ -15,12 +16,13 @@ public class MoveViewOperation : Operation /// Moves a within it's current container by a fixed amount /// (e.g. nudging with Shift+Cursor). /// + /// The application instance. /// Wrapper of the to move. /// The amount to move in the X plane. Positive for Right and Negative for Left. /// Ignored if is relative (e.g. ). /// The amount to move in the Y plane. Positive for Down and Negative for Up. /// Ignored if is relative (e.g. ). - public MoveViewOperation(Design toMove, int deltaX, int deltaY) + public MoveViewOperation(IApplication app, Design toMove, int deltaX, int deltaY) : base(app) { this.BeingMoved = toMove; this.OriginX = toMove.View.X; diff --git a/src/Operations/Operation.cs b/src/Operations/Operation.cs index 0b0fe5ae..a8f06729 100644 --- a/src/Operations/Operation.cs +++ b/src/Operations/Operation.cs @@ -9,6 +9,11 @@ namespace TerminalGuiDesigner.Operations; /// public abstract class Operation : IOperation { + /// + /// The application instance. May be null for operations that don't need to show dialogs. + /// + public IApplication App { get; } + /// /// The number of times the operation has been performed. /// @@ -43,6 +48,12 @@ public override string ToString() return this.GetOperationName(); } + protected Operation(IApplication app) + { + App = app; + + } + /// /// Returns false if or if failed. public bool Do() diff --git a/src/Operations/OperationFactory.cs b/src/Operations/OperationFactory.cs index 57587b88..1661de04 100644 --- a/src/Operations/OperationFactory.cs +++ b/src/Operations/OperationFactory.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Input; using Terminal.Gui.ViewBase; using TerminalGuiDesigner.ToCode; @@ -11,15 +12,18 @@ namespace TerminalGuiDesigner.Operations; /// public class OperationFactory { + private readonly IApplication app; private PropertyValueGetterDelegate valueGetter; /// /// Initializes a new instance of the class. /// + /// The application instance. /// Delegate for getting new values. This /// will be passed to created operations e.g. . - public OperationFactory(PropertyValueGetterDelegate valueGetter) + public OperationFactory(IApplication app, PropertyValueGetterDelegate valueGetter) { + this.app = app; this.valueGetter = valueGetter; } @@ -33,7 +37,7 @@ public OperationFactory(PropertyValueGetterDelegate valueGetter) /// the that the mouse was over at the time it was clicked (see . /// String that represents what the returned act upon e.g. "myLabel" or "8 objects". /// Collection of all that can be offered to user as runnable given the current selection. - public IEnumerable CreateOperations(Design[] selected, MouseEventArgs? m, Design? rightClicked, out string name) + public IEnumerable CreateOperations(Design[] selected, Mouse? m, Design? rightClicked, out string name) { List toReturn = new(); @@ -78,7 +82,7 @@ public IEnumerable CreateOperations(Design[] selected, MouseEventArg if (all.Count == selected.Length) { // create an operation to change them all at once - props.Add(new SetPropertyOperation(all.Select(v => v.Design).ToArray(), propertyName, this.valueGetter)); + props.Add(new SetPropertyOperation(this.app, all.Select(v => v.Design).ToArray(), propertyName, this.valueGetter)); } } @@ -91,21 +95,21 @@ public IEnumerable CreateOperations(Design[] selected, MouseEventArg if (SelectionManager.Instance.Selected.Any()) { - toReturn.Add(new CopyOperation(SelectionManager.Instance.Selected.ToArray())); + toReturn.Add(new CopyOperation(this.app, SelectionManager.Instance.Selected.ToArray())); } else if (rightClicked != null) { - toReturn.Add(new CopyOperation(rightClicked)); + toReturn.Add(new CopyOperation(this.app, rightClicked)); } return toReturn; } - private IEnumerable CreateOperations(MouseEventArgs? m, Design d) + private IEnumerable CreateOperations(Mouse? m, Design d) { - var ops = m == null ? + var ops = m == null || !m.Position.HasValue? d.GetExtraOperations() : - d.GetExtraOperations(d.View.ScreenToContent(m.Position)); + d.GetExtraOperations(d.View.ScreenToContent(m.Position.Value)); foreach (var extra in ops.Where(c => !c.IsImpossible)) { @@ -114,7 +118,7 @@ private IEnumerable CreateOperations(MouseEventArgs? m, Design d) foreach (var prop in d.GetDesignableProperties().OrderBy(p => p.GetHumanReadableName())) { - yield return new SetPropertyOperation(d, prop, this.valueGetter); + yield return new SetPropertyOperation(this.app, d, prop, this.valueGetter); } } } \ No newline at end of file diff --git a/src/Operations/PasteOperation.cs b/src/Operations/PasteOperation.cs index 930c6956..447d30ef 100644 --- a/src/Operations/PasteOperation.cs +++ b/src/Operations/PasteOperation.cs @@ -1,5 +1,6 @@ using System.Data; using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; using Terminal.Gui.Views; @@ -23,10 +24,11 @@ public class PasteOperation : Operation /// /// Initializes a new instance of the class. /// + /// The application instance. /// The container into which to /// add the . This allows for copying from one container /// (e.g. ) but pasting into another. - public PasteOperation(Design addTo) + public PasteOperation(IApplication app, Design addTo) : base(app) { this.toCopy = CopyOperation.LastCopiedDesign; this.toCopy = this.PruneChildViews(this.toCopy); @@ -144,7 +146,7 @@ private void Paste(View copy, Design into) private bool Paste(Design copy, Design into) { var clone = ViewFactory.Create(copy.View.GetType()); - var addOperation = new AddViewOperation(clone, into, null); + var addOperation = new AddViewOperation(App, clone, into, null); // couldn't add for some reason if (!addOperation.Do()) diff --git a/src/Operations/ResizeOperation.cs b/src/Operations/ResizeOperation.cs index 6d59d99f..f4cd68ee 100644 --- a/src/Operations/ResizeOperation.cs +++ b/src/Operations/ResizeOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; namespace TerminalGuiDesigner.Operations; @@ -12,12 +13,13 @@ public class ResizeOperation : Operation /// /// Initializes a new instance of the class. /// + /// The application instance. /// Wrapper for the that is to be resized. /// Client coordinate X point within /// where the mouse cursor is positioned for resizing. /// Client coordinate Y point within /// where the mouse cursor is positioned for resizing. - public ResizeOperation(Design beingResized, int destX, int destY) + public ResizeOperation(IApplication app, Design beingResized, int destX, int destY) : base(app) { this.BeingResized = beingResized; this.OriginalWidth = beingResized.View.Width; diff --git a/src/Operations/SetPropertyOperation.cs b/src/Operations/SetPropertyOperation.cs index 9f03a57d..b404c40c 100644 --- a/src/Operations/SetPropertyOperation.cs +++ b/src/Operations/SetPropertyOperation.cs @@ -1,5 +1,6 @@ using System.Reflection; using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; using TerminalGuiDesigner.ToCode; using TerminalGuiDesigner.UI.Windows; @@ -32,12 +33,13 @@ public class SetPropertyOperation : Operation /// time. Throw in delegate if you want to perform last /// minute cancellation instead of returning a new value to set. /// + /// The application instance. /// A single on which to change a single . /// Property to change (see ). /// Delegate for fetching the new value for the when /// command is run e.g. via a dialog. - public SetPropertyOperation(Design design, Property property, PropertyValueGetterDelegate valueGetter) - : this(design, property, property.GetValue(), null) + public SetPropertyOperation(IApplication app, Design design, Property property, PropertyValueGetterDelegate valueGetter) + : this(app, design, property, property.GetValue(), null) { this.valueGetter = valueGetter; } @@ -46,11 +48,12 @@ public SetPropertyOperation(Design design, Property property, PropertyValueGette /// Initializes a new instance of the class. /// Operation that changes the to have a specific . /// + /// The application instance. /// A single on which to change a single . /// Property to change (see ). /// The old value that had. /// The new value you want to assign to . - public SetPropertyOperation(Design design, Property property, object? oldValue, object? newValue) + public SetPropertyOperation(IApplication app, Design design, Property property, object? oldValue, object? newValue) : base(app) { this.mementos = new[] { @@ -71,6 +74,7 @@ public SetPropertyOperation(Design design, Property property, object? oldValue, /// Constructor for setting the same property on multiple views at once (e.g. change color scheme on /// all multi selected views). /// + /// The application instance. /// All for which you want to change /// . /// The name of a designable on @@ -78,7 +82,7 @@ public SetPropertyOperation(Design design, Property property, object? oldValue, /// Delegate for fetching the new value for /// the when command is run e.g. via a . /// Thrown if is not found amongst properties. - public SetPropertyOperation(Design[] designs, string propertyName, PropertyValueGetterDelegate valueGetter) + public SetPropertyOperation(IApplication app, Design[] designs, string propertyName, PropertyValueGetterDelegate valueGetter) : base(app) { this.valueGetter = valueGetter; var mementos = new List(); diff --git a/src/Operations/StatusBarOperations/AddStatusItemOperation.cs b/src/Operations/StatusBarOperations/AddStatusItemOperation.cs index 194a4e03..7f8fcbf5 100644 --- a/src/Operations/StatusBarOperations/AddStatusItemOperation.cs +++ b/src/Operations/StatusBarOperations/AddStatusItemOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Drivers; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; @@ -13,10 +14,12 @@ public class AddStatusItemOperation : AddOperation /// /// Initializes a new instance of the class. /// + /// The application instance. /// Wrapper for a . /// Name for the new item created or null to prompt user. - public AddStatusItemOperation(Design design, string? name) + public AddStatusItemOperation(IApplication app, Design design, string? name) : base( + app, (d) => d.GetShortcuts(), (d, v) => d.SetShortcuts(v), (v) => v.Title.ToString() ?? Operation.Unnamed, diff --git a/src/Operations/StatusBarOperations/MoveStatusItemOperation.cs b/src/Operations/StatusBarOperations/MoveStatusItemOperation.cs index d518406d..035bb0e9 100644 --- a/src/Operations/StatusBarOperations/MoveStatusItemOperation.cs +++ b/src/Operations/StatusBarOperations/MoveStatusItemOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; @@ -12,11 +13,13 @@ public class MoveStatusItemOperation : MoveOperation /// /// Initializes a new instance of the class. /// + /// The application instance. /// Wrapper for a . /// The to move. /// Negative for left, positive for right. - public MoveStatusItemOperation(Design design, Shortcut toMove, int adjustment) + public MoveStatusItemOperation(IApplication app, Design design, Shortcut toMove, int adjustment) : base( + app, (v) => v.GetShortcuts(), (v, a) => v.SetShortcuts(a), (e) => e.Title?.ToString() ?? Operation.Unnamed, diff --git a/src/Operations/StatusBarOperations/RemoveStatusItemOperation.cs b/src/Operations/StatusBarOperations/RemoveStatusItemOperation.cs index 35fd5799..5a27eb6f 100644 --- a/src/Operations/StatusBarOperations/RemoveStatusItemOperation.cs +++ b/src/Operations/StatusBarOperations/RemoveStatusItemOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; using TerminalGuiDesigner.UI.Windows; @@ -13,10 +14,12 @@ public class RemoveStatusItemOperation : RemoveOperation /// /// Initializes a new instance of the class. /// + /// The application instance. /// Wrapper for a . /// A to remove from bar. - public RemoveStatusItemOperation(Design design, Shortcut toRemove) + public RemoveStatusItemOperation(IApplication app, Design design, Shortcut toRemove) : base( + app, (v) => v.GetShortcuts(), (v, a) => v.SetShortcuts(a), (e) => e.Title?.ToString() ?? Operation.Unnamed, diff --git a/src/Operations/StatusBarOperations/RenameStatusItemOperation.cs b/src/Operations/StatusBarOperations/RenameStatusItemOperation.cs index a78618f3..badad70f 100644 --- a/src/Operations/StatusBarOperations/RenameStatusItemOperation.cs +++ b/src/Operations/StatusBarOperations/RenameStatusItemOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; using TerminalGuiDesigner.UI.Windows; @@ -13,11 +14,13 @@ public class RenameStatusItemOperation : RenameOperation /// /// Initializes a new instance of the class. /// + /// The application instance. /// Design wrapper for a . /// The to rename. /// The new name to use or null to prompt user. - public RenameStatusItemOperation(Design design, Shortcut toRename, string? newName) + public RenameStatusItemOperation(IApplication app, Design design, Shortcut toRename, string? newName) : base( + app, (d) => d.GetShortcuts(), (d, v) => d.SetShortcuts(v), (v) => v.Title.ToString() ?? Operation.Unnamed, diff --git a/src/Operations/StatusBarOperations/SetShortcutOperation.cs b/src/Operations/StatusBarOperations/SetShortcutOperation.cs index 3b59154a..8f599494 100644 --- a/src/Operations/StatusBarOperations/SetShortcutOperation.cs +++ b/src/Operations/StatusBarOperations/SetShortcutOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Input; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; @@ -18,11 +19,13 @@ public class SetShortcutOperation : GenericArrayElementOperation /// Initializes a new instance of the class. /// + /// The application instance. /// Wrapper for a . /// The whose shortcut you want to change. /// The new shortcut or null to prompt user at runtime. - public SetShortcutOperation(Design design, Shortcut statusItem, Key? shortcut) + public SetShortcutOperation(IApplication app, Design design, Shortcut statusItem, Key? shortcut) : base( + app, (v) => v.GetShortcuts(), (v, a) => v.SetShortcuts(a), (e) => e.Title?.ToString() ?? Operation.Unnamed, @@ -55,7 +58,7 @@ protected override bool DoImpl() { if (this.shortcut == Key.Empty) { - this.shortcut = Modals.GetShortcut(); + this.shortcut = Modals.GetShortcut(App!); } this.OperateOn.Key = this.shortcut; diff --git a/src/Operations/TabOperations/AddTabOperation.cs b/src/Operations/TabOperations/AddTabOperation.cs index 5040fe39..d897813e 100644 --- a/src/Operations/TabOperations/AddTabOperation.cs +++ b/src/Operations/TabOperations/AddTabOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; @@ -13,10 +14,12 @@ public class AddTabOperation : AddOperation /// /// Initializes a new instance of the class. /// + /// The application instance. /// Wrapper for that will be operated on. /// Name for the new tab or null to prompt user. - public AddTabOperation(Design design, string? name) + public AddTabOperation(IApplication app, Design design, string? name) : base( + app, (t) => t.Tabs.ToArray(), (v, a) => v.ReOrderTabs(a), tab => tab.DisplayText.ToString() ?? "unnamed tab", diff --git a/src/Operations/TabOperations/MoveTabOperation.cs b/src/Operations/TabOperations/MoveTabOperation.cs index 6533928f..e54228f3 100644 --- a/src/Operations/TabOperations/MoveTabOperation.cs +++ b/src/Operations/TabOperations/MoveTabOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; @@ -15,11 +16,13 @@ public class MoveTabOperation : MoveOperation /// Creates an operation that will change the ordering of tabs within /// a . /// + /// The application instance. /// Wrapper for a . /// The Tab to move. /// Negative to move tab left, positive to move tab right. - public MoveTabOperation(Design design, Tab toMove, int adjustment) + public MoveTabOperation(IApplication app, Design design, Tab toMove, int adjustment) : base( + app, (t) => t.Tabs.ToArray(), (v, a) => v.ReOrderTabs(a), tab => tab.Text.ToString() ?? "unnamed tab", diff --git a/src/Operations/TabOperations/RemoveTabOperation.cs b/src/Operations/TabOperations/RemoveTabOperation.cs index 5e69c954..fb6c55cc 100644 --- a/src/Operations/TabOperations/RemoveTabOperation.cs +++ b/src/Operations/TabOperations/RemoveTabOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; @@ -13,11 +14,13 @@ public class RemoveTabOperation : RemoveOperation /// Initializes a new instance of the class. /// Removes from a . /// + /// The application instance. /// Wrapper for a from which you want to remove the tab. /// The tab to remove. /// Thrown if does not wrap a . - public RemoveTabOperation(Design design, Tab toRemove) + public RemoveTabOperation(IApplication app, Design design, Tab toRemove) : base( + app, (t) => t.Tabs.ToArray(), (v, a) => v.ReOrderTabs(a), tab => tab.Text.ToString() ?? "unnamed tab", diff --git a/src/Operations/TabOperations/RenameTabOperation.cs b/src/Operations/TabOperations/RenameTabOperation.cs index b21d52bc..37d74d55 100644 --- a/src/Operations/TabOperations/RenameTabOperation.cs +++ b/src/Operations/TabOperations/RenameTabOperation.cs @@ -1,4 +1,5 @@ using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; @@ -14,11 +15,13 @@ public class RenameTabOperation : RenameOperation /// Initializes a new instance of the class. /// This command changes the on a . /// + /// The application instance. /// Wrapper for a . /// Tab to rename. /// New name to use or null to prompt. - public RenameTabOperation(Design design, Tab toRename, string? newName) + public RenameTabOperation(IApplication app, Design design, Tab toRename, string? newName) : base( + app, (t) => t.Tabs.ToArray(), (v, a) => v.ReOrderTabs(a), tab => tab.DisplayText.ToString() ?? "unnamed tab", diff --git a/src/Operations/TableViewOperations/AddColumnOperation.cs b/src/Operations/TableViewOperations/AddColumnOperation.cs index d39bc423..be47fef9 100644 --- a/src/Operations/TableViewOperations/AddColumnOperation.cs +++ b/src/Operations/TableViewOperations/AddColumnOperation.cs @@ -1,5 +1,6 @@ using System.Data; using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; using TerminalGuiDesigner.UI.Windows; @@ -14,11 +15,13 @@ public class AddColumnOperation : AddOperation /// /// Initializes a new instance of the class. /// + /// The application instance. /// Wrapper for a . /// The name for the new column or null to prompt at runtime with a dialog. /// Thrown if is not wrapping a . - public AddColumnOperation(Design design, string? newColumnName) + public AddColumnOperation(IApplication app, Design design, string? newColumnName) : base( + app, (v) => v.GetDataTable().Columns.Cast().ToArray(), (v, a) => v.ReOrderColumns(a), (c) => c.ColumnName, diff --git a/src/Operations/TableViewOperations/MoveColumnOperation.cs b/src/Operations/TableViewOperations/MoveColumnOperation.cs index 9fd08d1f..3f07e1e3 100644 --- a/src/Operations/TableViewOperations/MoveColumnOperation.cs +++ b/src/Operations/TableViewOperations/MoveColumnOperation.cs @@ -1,5 +1,6 @@ using System.Data; using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; @@ -16,11 +17,13 @@ public class MoveColumnOperation : MoveOperation /// Creates an operation that will change the ordering of columns within /// a . /// + /// The application instance. /// Wrapper for a . /// The to move. /// Negative to move left, positive to move right. - public MoveColumnOperation(Design design, DataColumn column, int adjustment) + public MoveColumnOperation(IApplication app, Design design, DataColumn column, int adjustment) : base( + app, (v) => v.GetDataTable().Columns.Cast().ToArray(), (v, a) => v.ReOrderColumns(a), (c) => c.ColumnName, diff --git a/src/Operations/TableViewOperations/RemoveColumnOperation.cs b/src/Operations/TableViewOperations/RemoveColumnOperation.cs index e5f302f7..a14b39b7 100644 --- a/src/Operations/TableViewOperations/RemoveColumnOperation.cs +++ b/src/Operations/TableViewOperations/RemoveColumnOperation.cs @@ -1,5 +1,6 @@ using System.Data; using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; @@ -14,10 +15,12 @@ public class RemoveColumnOperation : RemoveOperation /// /// Initializes a new instance of the class. /// + /// The application instance. /// Wrapper for a . /// Column to remove. - public RemoveColumnOperation(Design design, DataColumn column) + public RemoveColumnOperation(IApplication app, Design design, DataColumn column) : base( + app, (v) => v.GetDataTable().Columns.Cast().ToArray(), (v, a) => v.ReOrderColumns(a), (c) => c.ColumnName, diff --git a/src/Operations/TableViewOperations/RenameColumnOperation.cs b/src/Operations/TableViewOperations/RenameColumnOperation.cs index a6631ee8..43f4349f 100644 --- a/src/Operations/TableViewOperations/RenameColumnOperation.cs +++ b/src/Operations/TableViewOperations/RenameColumnOperation.cs @@ -1,5 +1,6 @@ using System.Data; using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Views; using TerminalGuiDesigner.Operations.Generics; @@ -13,12 +14,14 @@ public class RenameColumnOperation : RenameOperation /// /// Initializes a new instance of the class. /// + /// The application instance. /// The wrapper for a . /// The column to rename. /// New name to use or null to prompt user. /// Thrown if does not wrap a . - public RenameColumnOperation(Design design, DataColumn column, string? newName) + public RenameColumnOperation(IApplication app, Design design, DataColumn column, string? newName) : base( + app, (v) => v.GetDataTable().Columns.Cast().ToArray(), (v, a) => v.ReOrderColumns(a), (c) => c.ColumnName, diff --git a/src/Options.cs b/src/Options.cs index a00df27f..b54b334c 100644 --- a/src/Options.cs +++ b/src/Options.cs @@ -61,9 +61,8 @@ public static IEnumerable Examples /// /// Gets or sets a which driver to use. /// - [Option('d', HelpText = "Driver to use. v2, v2net, v2win, WindowsDriver, CursesDriver or NetDriver", - Default = "v2")] - public string Driver { get; set; } = "v2"; + [Option('d', HelpText = "Driver to use")] + public string Driver { get; set; } /// diff --git a/src/Program.cs b/src/Program.cs index b53e973c..eb377d5a 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -21,9 +21,9 @@ public static void Main(string[] args) { Editor.Experimental = o.Experimental; Editor.Quiet = o.Quiet; - - Application.Init(null,o.Driver); - var editor = new Editor(); + + using var app = Application.Create().Init(o.Driver); + var editor = new Editor(app); editor.Run(o); }); } diff --git a/src/Properties/launchSettings.json b/src/Properties/launchSettings.json index dc17da51..4a20f313 100644 --- a/src/Properties/launchSettings.json +++ b/src/Properties/launchSettings.json @@ -4,13 +4,8 @@ "commandName": "WSL2", "distributionName": "" }, - "v2net": { - "commandName": "Project", - "commandLineArgs": "-d v2net" - }, - "v2win": { - "commandName": "Project", - "commandLineArgs": "-d v2win" + "run": { + "commandName": "Project" } } } \ No newline at end of file diff --git a/src/TTypes.cs b/src/TTypes.cs index e0272497..75486d82 100644 --- a/src/TTypes.cs +++ b/src/TTypes.cs @@ -41,7 +41,7 @@ public static CodeExpression ToCode(CodeDomArgs args, Design design, object? val /// public static IEnumerable GetSupportedTTypesForGenericViewOfType(Type viewType) { - if (viewType == typeof(Slider<>)) + if (viewType == typeof(LinearRange<>)) { return new[] { typeof(int), typeof(string), typeof(int), typeof(double), typeof(bool) }; } @@ -63,6 +63,7 @@ public static IEnumerable GetSupportedTTypesForGenericViewOfType(Type view return new[] { typeof(object), typeof(FileSystemInfo) }; } + throw new NotSupportedException($"Generic View {viewType} is not yet supported"); } diff --git a/src/TerminalGuiDesigner.csproj b/src/TerminalGuiDesigner.csproj index 22ec0fd1..712d6f36 100644 --- a/src/TerminalGuiDesigner.csproj +++ b/src/TerminalGuiDesigner.csproj @@ -10,9 +10,9 @@ - net8.0 + net10.0 $(DefineConstants) - 12 + 13 Exe true true @@ -20,7 +20,7 @@ ./nupkg enable TerminalGuiDesigner - 2.0.0-develop.4538 + 2.0.0-develop.5217 Thomas Nind enable MIT @@ -33,7 +33,7 @@ logo.png README.md - 2.0.0-alpha.4538 + 2.0.0-alpha.5217 * Update to latest nuget package * Fix crash caused moving PosRelative view without its pair into different container View 2.0.0-alpha.4519 @@ -70,7 +70,7 @@ * Fix right click on TableView headers * Improve context menu layout for menus, status bars etc * Improved private field naming conventions - * RadioGroup can now be set to horizontal + * OptionSelector can now be set to horizontal v1.0.19 * Added `-e` experimental mode flag that lets you create new Toplevel and View classes * Added dotted border around Views that do not have any visible boundary (e.g. `View`, `TabView` with ShowBorder off) @@ -101,7 +101,7 @@ * Deleting the last item on a MenuBar now removes the MenuBar too * Added multi copy/paste (drag selection box and copy/paste). Still restricted to non container views (i.e. not TabView etc) v1.0.13 - * Changing LineView Orientation now properly flips rune and Width/Height + * Changing Line Orientation now properly flips rune and Width/Height * Fixed mouse drag moving and resizing container views (e.g. TabView) * Fixed bug where you were able to copy/paste the root view v1.0.12 @@ -154,21 +154,21 @@ - + - - - - + + + + - + - - + + diff --git a/src/ToCode/MenuBarItemsToCode.cs b/src/ToCode/MenuBarItemsToCode.cs index 5c96504d..9de3f8c5 100644 --- a/src/ToCode/MenuBarItemsToCode.cs +++ b/src/ToCode/MenuBarItemsToCode.cs @@ -47,88 +47,101 @@ public void ToCode(CodeDomArgs args) MenuBarItem m1 = new MenuBarItem(); m1.Children = new []{m1_1}; - mb.Menus = new []{m1}; - */ - + mb.Menus = new []{m1};*/ + // TODO: Let user name these List menus = new(); - foreach (var child in this.menuBar.Menus) + + foreach (var child in this.menuBar.SubViews.OfType()) { this.ToCode(args, child, out string fieldName); menus.Add(fieldName); - } - this.AddPropertyAssignment( - args, - $"this.{this.design.FieldName}.{nameof(this.menuBar.Menus)}", - new CodeArrayCreateExpression( - typeof(MenuBarItem), - menus.Select(c => - new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), c)) - .ToArray())); - } - private void ToCode(CodeDomArgs args, MenuBarItem child, out string fieldName) + this.AddMethodCall(args, + new CodeFieldReferenceExpression( + new CodeThisReferenceExpression(), this.design.FieldName), + "Add", + // or the name of the field for each menu item + new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName) + ); + } + } + + private void ToCode(CodeDomArgs args, MenuItem child, out string fieldName) { - fieldName = this.GetUniqueFieldName(args, child); - this.AddFieldToClass(args, child.GetType(), fieldName); - this.AddConstructorCall(args, $"this.{fieldName}", child.GetType()); - this.AddPropertyAssignment(args, $"this.{fieldName}.{nameof(MenuItem.Title)}", child.Title); + CreateMenuMembersAndPropertyAssignments(args, child, out fieldName); List children = new(); + bool wasPopover; + // TODO: Make recursive for more children // plus again let user name these - foreach (var sub in child.Children) + foreach (var mi in child.GetMenuItems(out wasPopover)) { - if (sub is MenuBarItem bar) - { - this.ToCode(args, bar, out string f); - children.Add(f); - } - else - if (sub == null) + string subFieldName; + + // If it has its own children e.g. + // File->New->Project + if (mi.SubMenu != null) { - // its a menu separator (in Terminal.Gui separators are indicated by having a null element). - children.Add(null); + ToCode(args, mi, out subFieldName); } else { - string subFieldName = this.GetUniqueFieldName(args, sub); - this.AddFieldToClass(args, sub.GetType(), subFieldName); - this.AddConstructorCall(args, $"this.{subFieldName}", sub.GetType()); - this.AddPropertyAssignment(args, $"this.{subFieldName}.{nameof(MenuItem.Title)}", sub.Title); - this.AddPropertyAssignment(args, $"this.{subFieldName}.{nameof(MenuItem.Data)}", subFieldName); - - if (sub.ShortcutKey != KeyCode.Null) - { - this.AddPropertyAssignment( - args, - $"this.{subFieldName}.{nameof(MenuItem.ShortcutKey)}", - new CodeCastExpression( - new CodeTypeReference(typeof(KeyCode)), - new CodePrimitiveExpression((uint)sub.ShortcutKey))); - } - - children.Add(subFieldName); + + // It has no children of its own e.g. its just Edit->Paste + CreateMenuMembersAndPropertyAssignments(args, mi, out subFieldName); } + + children.Add(subFieldName); + } + + if (wasPopover) + { + // this.fileMenu.PopoverMenu = new PopoverMenu([editMeMenuItem]); + this.AddPropertyAssignment(args, $"this.{fieldName}.{nameof(MenuBarItem.PopoverMenu)}", + new CodeSnippetExpression($"new PopoverMenu([{string.Join(",", children)}])")); + } + else + { + // this.newMenu.SubMenu = new Menu([carMenuItem]); + this.AddPropertyAssignment(args, $"this.{fieldName}.{nameof(MenuBarItem.SubMenu)}", + new CodeSnippetExpression($"new Menu([{string.Join(",", children)}])")); } + } + + /// + /// Creates all class fields and property assignments for excluding + /// child menu items (which are handled in ) + /// + /// + /// + /// + private void CreateMenuMembersAndPropertyAssignments(CodeDomArgs args, MenuItem child, out string fieldName) + { + // ------------ Class Fields ------------- + + // private Terminal.Gui.Views.MenuBarItem fileMenu; + fieldName = this.GetUniqueFieldName(args, child); + this.AddFieldToClass(args, child.GetType(), fieldName); + + // --------- InitializeComponent() --------- + + // this.fileMenu = new Terminal.Gui.Views.MenuBarItem(); + this.AddConstructorCall(args, $"this.{fieldName}", child.GetType()); - // we have created fields and constructor calls for our menu - // now set the menu to an array of all those fields - this.AddPropertyAssignment( - args, - $"this.{fieldName}.{nameof(MenuBarItem.Children)}", - new CodeArrayCreateExpression( - typeof(MenuItem), - children.Select(c => - - // the array elements have null for separator - c is null ? new CodePrimitiveExpression() : - - // or the name of the field for each menu item - (CodeExpression)new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), c)) - .ToArray())); + // this.fileMenu.Title = "_File"; + this.AddPropertyAssignment(args, $"this.{fieldName}.{nameof(MenuItem.Title)}", child.Title); + + // TODO: Verify that all ToString exactly match the static property + // this.fileMenu.Key = Key.F9; + if (child.Key != KeyCode.Null) + { + this.AddPropertyAssignment(args, $"this.{fieldName}.{nameof(MenuItem.Key)}", + new CodeSnippetExpression($"Key.{child.Key}")); + } } private string GetUniqueFieldName(CodeDomArgs args, MenuItem item) diff --git a/src/ToCode/Property.cs b/src/ToCode/Property.cs index 78bec35f..cf2759be 100644 --- a/src/ToCode/Property.cs +++ b/src/ToCode/Property.cs @@ -100,28 +100,6 @@ public virtual void SetValue(object? value) { value = AdjustValueBeingSet(value); - // if a LineView and changing Orientation then also flip - // the Height/Width and set appropriate new rune - if (this.PropertyInfo.Name == nameof(LineView.Orientation) - && this.Design.View is LineView v && value is Orientation newOrientation) - { - switch (newOrientation) - { - case Orientation.Horizontal: - v.Width = v.Height; - v.Height = 1; - v.LineRune = Glyphs.HLine; - - break; - case Orientation.Vertical: - v.Height = v.Width; - v.Width = 1; - v.LineRune = Glyphs.VLine; - break; - default: - throw new ArgumentException($"Unknown Orientation {newOrientation}"); - } - } this.PropertyInfo.SetValue(this.DeclaringObject, value); @@ -230,6 +208,7 @@ public virtual CodeExpression GetRhs() var type = val.GetType(); + // But less specific if (type.IsArray) { var elementType = type.GetElementType(); @@ -240,6 +219,20 @@ public virtual CodeExpression GetRhs() values.Select(v => v.ToCodePrimitiveExpression()).ToArray()); } + + if (type.IsGenericType(typeof(IReadOnlyList<>))) + { + var elementType = type.GetGenericArguments()[0]; + + if(elementType.IsPrimitive || elementType == typeof(string)) + { + var values = ((IEnumerable)val).Cast().ToList(); + return new CodeArrayCreateExpression( + elementType ?? throw new Exception($"Type {type} was an IReadOnlyList<> but {nameof(Type.GetGenericArguments)} returned null"), + values.Select(v => v.ToCodePrimitiveExpression()).ToArray()); + } + } + if (val is IList valList) { var elementType = type.GetElementTypeEx() @@ -308,13 +301,18 @@ private CodeExpression ValueFactory(object val) var type = val.GetType(); + if( type.IsValueType || type == typeof(string)) + { + return new CodePrimitiveExpression(val); + } + // TODO: Could move lots of logic in GetRHS into here - if (type.GetGenericTypeDefinition() == typeof(SliderOption<>)) + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(LinearRangeOption<>)) { // TODO: this feels very brittle! - var a1 = type.GetPropertyOrThrow(nameof(SliderOption.Legend)).GetValue(val); - var a2 = (Rune)type.GetPropertyOrThrow(nameof(SliderOption.LegendAbbr)).GetValue(val)!; - var a3 = type.GetPropertyOrThrow(nameof(SliderOption.Data)).GetValue(val); + var a1 = type.GetPropertyOrThrow(nameof(LinearRangeOption.Legend)).GetValue(val); + var a2 = (Rune)type.GetPropertyOrThrow(nameof(LinearRangeOption.LegendAbbr)).GetValue(val)!; + var a3 = type.GetPropertyOrThrow(nameof(LinearRangeOption.Data)).GetValue(val); return new CodeObjectCreateExpression( diff --git a/src/ToCode/TabToCode.cs b/src/ToCode/TabToCode.cs index 0760fe18..0a7f551d 100644 --- a/src/ToCode/TabToCode.cs +++ b/src/ToCode/TabToCode.cs @@ -53,7 +53,7 @@ public void ToCode(CodeDomArgs args) this.AddPropertyAssignment(args, $"{tabName}.View.CanFocus", new CodeSnippetExpression("true")); // create code statements for everything in the Tab (recursive) - var viewToCode = new ViewToCode(); + var viewToCode = new ViewToCode(design.App); viewToCode.AddSubViewsToDesignerCs(this.tab.View, args, new CodeSnippetExpression($"{tabName}.View")); // add the constructed tab to the TabView diff --git a/src/ToCode/ViewToCode.cs b/src/ToCode/ViewToCode.cs index d3491e60..b3c5f81e 100644 --- a/src/ToCode/ViewToCode.cs +++ b/src/ToCode/ViewToCode.cs @@ -2,6 +2,7 @@ using System.CodeDom.Compiler; using Microsoft.CSharp; using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.ViewBase; using Terminal.Gui.Views; using TerminalGuiDesigner.FromCode; @@ -13,6 +14,16 @@ namespace TerminalGuiDesigner.ToCode; /// public class ViewToCode { + private readonly IApplication app; + + /// + /// Initializes a new instance of the class. + /// + /// The application instance. + public ViewToCode(IApplication app) + { + this.app = app; + } /// /// Returns the code that would be added to the MyWindow.cs file of a new window /// so that it is ready for use with the MyWindow.Designer.cs file (in which @@ -91,7 +102,7 @@ public Design GenerateNewView(FileInfo csFilePath, string namespaceName, Type vi FixDimensionsForNewRootView(prototype, viewType); // use the prototype to create a designer cs file - var design = new Design(sourceFile, Design.RootDesignName, prototype); + var design = new Design(app, sourceFile, Design.RootDesignName, prototype); design.CreateSubControlDesigns(); this.GenerateDesignerCs(design, viewType); @@ -100,7 +111,7 @@ public Design GenerateNewView(FileInfo csFilePath, string namespaceName, Type vi * NOTE: prototype is not the same instance that is returned; */ - var decompiler = new CodeToView(sourceFile); + var decompiler = new CodeToView(app, sourceFile); return decompiler.CreateInstance(); } @@ -135,7 +146,7 @@ private void FixDimensionsForNewRootView(View prototype, Type viewType) public void GenerateDesignerCs(Design rootDesign, Type viewType) { var file = rootDesign.SourceCode; - var rosylyn = new CodeToView(file); + var rosylyn = new CodeToView(app, file); var ns = new CodeNamespace(rosylyn.Namespace); ns.Imports.Add(new CodeNamespaceImport("System")); diff --git a/src/TypeExtensions.cs b/src/TypeExtensions.cs index e918741f..a6508a2d 100644 --- a/src/TypeExtensions.cs +++ b/src/TypeExtensions.cs @@ -18,26 +18,29 @@ public static class TypeExtensions public static Type? GetElementTypeEx(this Type type) { var elementType = type.GetElementType(); - if (elementType != null) - { return elementType; - } if (type.IsAssignableTo(typeof(IList)) && type.IsGenericType) { - return type.GetGenericArguments().Single(); + var args = type.GetGenericArguments(); + if (args.Length != 1) + throw new InvalidOperationException($"Expected exactly one generic argument for IList type {type}, but found {args.Length}."); + return args[0]; } - + if (type.IsGenericType(typeof(IEnumerable<>))) { - return type.GetGenericArguments().Single(); + var args = type.GetGenericArguments(); + if (args.Length != 1) + throw new InvalidOperationException($"Expected exactly one generic argument for IEnumerable type {type}, but found {args.Length}."); + return args[0]; } - return null; } + /// /// Returns true if is an implementation of a generic parent /// type . @@ -47,7 +50,29 @@ public static class TypeExtensions /// public static bool IsGenericType(this Type type, Type genericParentHypothesis) { - return type.IsGenericType && type.GetGenericTypeDefinition() == genericParentHypothesis; + if (!genericParentHypothesis.IsGenericTypeDefinition) + return false; + + // Interfaces (e.g. IReadOnlyList) + if (genericParentHypothesis.IsInterface && + type.GetInterfaces().Any(i => + i.IsGenericType && + i.GetGenericTypeDefinition() == genericParentHypothesis)) + { + return true; + } + + // Base types + self (e.g. List : Collection) + for (var t = type; t != null; t = t.BaseType) + { + if (t.IsGenericType && + t.GetGenericTypeDefinition() == genericParentHypothesis) + { + return true; + } + } + + return false; } /// diff --git a/src/UI/Editor.cs b/src/UI/Editor.cs index dd39c04f..9c274158 100644 --- a/src/UI/Editor.cs +++ b/src/UI/Editor.cs @@ -2,10 +2,8 @@ using System.Reflection; using System.Text; using System.Text.Json; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Serilog; -using Terminal.Gui; using Terminal.Gui.App; using Terminal.Gui.Drawing; using Terminal.Gui.Drivers; @@ -22,12 +20,13 @@ namespace TerminalGuiDesigner.UI; /// -/// Root that is visible on loading the +/// Root that is visible on loading the /// application. Hooks key and mouse events and mounts as a sub-view whatever file /// the user opens. /// -public class Editor : Toplevel, IErrorReporter +public class Editor : Runnable, IErrorReporter { + private readonly IApplication app; private KeyMap keyMap; private readonly KeyboardManager keyboardManager; private readonly MouseManager mouseManager; @@ -66,11 +65,18 @@ public class Editor : Toplevel, IErrorReporter /// public const string Error = "Error"; + /// + /// String to assign to popover data field so that we don't accidentally edit our own context menus! + /// + public const string? DesignerCorePopoverName = "CoreDesignerPopupMenu"; + /// /// Initializes a new instance of the class. /// - public Editor() + /// + public Editor(IApplication app) { + this.app = app; // Bug: This will have strange inheritance behavior if Editor is inherited from. this.CanFocus = true; @@ -81,12 +87,14 @@ public Editor() LoadKeyMap(); - this.keyboardManager = new KeyboardManager(this.keyMap); - this.mouseManager = new MouseManager() + this.keyboardManager = new KeyboardManager(app, this.keyMap); + this.mouseManager = new MouseManager(app) { ErrorReporter = this }; - this.Closing += this.Editor_Closing; + + // TODO: save changes + //this.Closing += this.Editor_Closing; this.BuildRootMenu(); } @@ -114,7 +122,7 @@ private void LoadKeyMap() catch (Exception ex) { // if there is bad yaml use the defaults - ExceptionViewer.ShowException("Failed to read keybindings from configuration file", ex); + ExceptionViewer.ShowException(app,"Failed to read keybindings from configuration file", ex); this.keyMap = new KeyMap(); } } @@ -130,7 +138,7 @@ private void SaveKeyMap() } catch (Exception ex) { - ExceptionViewer.ShowException("Failed to save keybindings from configuration file", ex); + ExceptionViewer.ShowException(app, "Failed to save keybindings from configuration file", ex); } } @@ -204,13 +212,12 @@ public void Run(Options options) } catch (Exception ex) { - MessageBox.ErrorQuery("Error Loading Designer", ex.Message, "Ok"); - Application.Shutdown(); + MessageBox.ErrorQuery(app, "Error Loading Designer", ex.Message, "Ok"); return; } } - Application.KeyDown += (_, k) => + app.Keyboard.KeyDown += (_, k) => { if (this.editing || this.viewBeingEdited == null) { @@ -226,11 +233,11 @@ public void Run(Options options) } catch (Exception ex) { - ExceptionViewer.ShowException("Error processing keystroke", ex); + ExceptionViewer.ShowException(app, "Error processing keystroke", ex); } }; - Application.MouseEvent += (s, m) => + app.Mouse.MouseEvent += (s, m) => { // if another window is showing don't respond to mouse if (!this.IsCurrentTop) @@ -239,7 +246,7 @@ public void Run(Options options) } // If disabling drag we suppress all but right click (button 3) - if (!m.Flags.HasFlag(MouseFlags.Button3Clicked) && !this.enableDrag) + if (!m.Flags.HasFlag(MouseFlags.MiddleButtonClicked) && !this.enableDrag) { return; } @@ -256,7 +263,7 @@ public void Run(Options options) // right click if (m.Flags.HasFlag(this.keyMap.RightClick)) { - var hit = this.viewBeingEdited.View.HitTest(m, out _, out _); + var hit = this.viewBeingEdited.View.HitTest(app, m, out _, out _); if (hit != null) { @@ -270,12 +277,11 @@ public void Run(Options options) } catch (Exception ex) { - ExceptionViewer.ShowException("Error processing mouse", ex); + ExceptionViewer.ShowException(app,"Error processing mouse", ex); } }; - Application.Run(this, this.ErrorHandler); - Application.Shutdown(); + app.Run(this, this.ErrorHandler); } /// @@ -342,9 +348,9 @@ protected override void OnDrawComplete(DrawContext? context) /// /// Draws title screen when no view is currently open /// - protected override bool OnDrawingContent() + protected override bool OnDrawingContent(DrawContext? context) { - var r = base.OnDrawingContent(); + var r = base.OnDrawingContent(context); if (viewBeingEdited != null) { @@ -352,9 +358,9 @@ protected override bool OnDrawingContent() } var bounds = Viewport; - - Application.Driver.SetAttribute(new Attribute(Color.Black)); - Application.Driver.FillRect(bounds,' '); + + SetAttribute(new Attribute(Color.Black)); + FillRect(bounds,new Rune(' ')); var top = new Rectangle(0, 0, bounds.Width, rootCommandsListView.Frame.Top - 1); RenderTitle(top); @@ -737,7 +743,7 @@ public bool HandleKey(Key key) } catch (Exception ex) { - ExceptionViewer.ShowException("Error", ex); + ExceptionViewer.ShowException(app, "Error", ex); } finally { @@ -892,9 +898,9 @@ private void BuildRootMenu() private void ChangeKeybindings() { - var kb = new KeyBindingsUI(keyMap); - Application.Run(kb); - + var kb = new KeyBindingsUI(app, keyMap); + app.Run(kb); + if (kb.Save) { SaveKeyMap(); @@ -902,7 +908,7 @@ private void ChangeKeybindings() } - private void Editor_Closing(object? sender, ToplevelClosingEventArgs obj) + private void Editor_Closing(object? sender, CancelEventArgs obj) { if (this.viewBeingEdited == null) { @@ -911,7 +917,7 @@ private void Editor_Closing(object? sender, ToplevelClosingEventArgs obj) if (this.HasUnsavedChanges) { - int answer = ChoicesDialog.Query("Unsaved Changes", $"You have unsaved changes to {this.viewBeingEdited.SourceCode.DesignerFile.Name}", "Save", "Don't Save", "Cancel"); + int answer = ChoicesDialog.Query(app, "Unsaved Changes", $"You have unsaved changes to {this.viewBeingEdited.SourceCode.DesignerFile.Name}", "Save", "Don't Save", "Cancel"); if (answer == 0) { @@ -929,7 +935,7 @@ private void Editor_Closing(object? sender, ToplevelClosingEventArgs obj) } } - private void CreateAndShowContextMenu(MouseEventArgs? m, Design? rightClicked) + private void CreateAndShowContextMenu(Mouse? m, Design? rightClicked) { if (this.viewBeingEdited == null) { @@ -940,7 +946,8 @@ private void CreateAndShowContextMenu(MouseEventArgs? m, Design? rightClicked) // BUG: This is an improper exception here and could have unexpected behavior if this method is ever called asynchronously. var factory = new OperationFactory( - (p, v) => ValueFactory.GetNewValue(p.Design, p, v, out var newValue) ? newValue : throw new OperationCanceledException() ); + app, + (p, v) => ValueFactory.GetNewValue(app, p.Design, p, v, out var newValue) ? newValue : throw new OperationCanceledException() ); var operations = factory .CreateOperations(selected, m, rightClicked, out string name) @@ -950,27 +957,44 @@ private void CreateAndShowContextMenu(MouseEventArgs? m, Design? rightClicked) var setProps = operations.OfType(); var others = operations .Except(setProps) - .GroupBy(k => k.Category, ToMenuItem); + .GroupBy(k => k.Category, p=>ToMenuItem(app,p)); - var setPropsItems = setProps.Select(ToMenuItem).ToArray(); + var setPropsItems = setProps.Select(p=>ToMenuItem(app,p)).ToList(); bool hasPropsItems = setPropsItems.Any(); - var all = new List(); + var all = new List(); // only add the set properties category if there are some if (hasPropsItems) { - all.Add(new MenuItemv2() + // Also add to the submenu an 'All' version + // Workaround for https://github.com/gui-cs/Terminal.Gui/issues/4876 + // Previously user could do it by selecting the root 'Properties' expandable + // menu + setPropsItems.Insert(0, new MenuItem() { + Title="(All)", + Action = () => + { + if (selected.Length == 1 || rightClicked != null) + { + this.ShowEditProperties(rightClicked ?? selected[0]); + } + }, + }); + + all.Add(new MenuItem() { Title = name, - Action = () => + SubMenu = new Menu(setPropsItems) + /* + * No longer supported, see https://github.com/gui-cs/Terminal.Gui/issues/4876 + * ,Action = () => { if (selected.Length == 1 || rightClicked != null) { this.ShowEditProperties(rightClicked ?? selected[0]); } - }, - SubMenu = new Menuv2(setPropsItems) + },*/ }); } @@ -988,10 +1012,10 @@ private void CreateAndShowContextMenu(MouseEventArgs? m, Design? rightClicked) // Add categories first all.Insert( hasPropsItems ? 1 : 0, - new MenuItemv2() + new MenuItem() { Title = g.Key, - SubMenu = new Menuv2(g.ToArray()) + SubMenu = new Menu(g.ToArray()) }); } } @@ -1003,10 +1027,12 @@ private void CreateAndShowContextMenu(MouseEventArgs? m, Design? rightClicked) } var menu = new PopoverMenu(all.ToArray()); + menu.Data = DesignerCorePopoverName; + Point position; - if (m != null) + if (m != null && m.Position.HasValue) { - position = m.Position; + position = m.Position.Value; } else { @@ -1023,21 +1049,24 @@ private void CreateAndShowContextMenu(MouseEventArgs? m, Design? rightClicked) m.Handled = true; } - - // TODO: rly? you have to pass it its own menu items!? + // This internal method call shouldn't be required + menu.App = app; + app.Popovers!.Register(menu); + menu.MakeVisible(position); menu.Accepted += (_, _) => { this.menuOpen = false; SelectionManager.Instance.LockSelection = false; + app.Popovers.DeRegister(menu); }; } - - private static MenuItemv2 ToMenuItem(IOperation operation) + + private static MenuItem ToMenuItem(IApplication application, IOperation operation) { - return new MenuItemv2(operation.ToString(), string.Empty, () => Try(() => OperationManager.Instance.Do(operation))); + return new MenuItem(operation.ToString(), string.Empty, () => Try(application,() => OperationManager.Instance.Do(operation))); - static void Try(Action action) + static void Try(IApplication application, Action action) { try { @@ -1048,7 +1077,7 @@ static void Try(Action action) } catch (Exception ex) { - ExceptionViewer.ShowException("Operation failed", ex); + ExceptionViewer.ShowException(application, "Operation failed", ex); } finally { @@ -1067,9 +1096,10 @@ static void Try(Action action) return m; } - if (MenuTracker.Instance.CurrentlyOpenMenuItem != null) + var selectedMenuItem = MenuTracker.GetFocusedMenuItemIfAny(app); + if (selectedMenuItem != null) { - return $"Selected: {MenuTracker.Instance.CurrentlyOpenMenuItem.Title}"; + return $"Selected: {selectedMenuItem.Title}"; } var selected = SelectionManager.Instance.Selected.ToArray(); @@ -1104,7 +1134,7 @@ private void Paste() if (d != null) { - var paste = new PasteOperation(d); + var paste = new PasteOperation(app, d); if (paste.IsImpossible) { @@ -1117,7 +1147,7 @@ private void Paste() private void Copy() { - var copy = new CopyOperation(SelectionManager.Instance.Selected.ToArray()); + var copy = new CopyOperation(app, SelectionManager.Instance.Selected.ToArray()); OperationManager.Instance.Do(copy); } @@ -1129,7 +1159,7 @@ private void ShowViewSpecificOperations() { var options = d.GetExtraOperations().Where(o => !o.IsImpossible).ToArray(); - if (options.Any() && Modals.Get("Operations", "Ok", options, null, out var selected) && selected != null) + if (options.Any() && Modals.Get(app, "Operations", "Ok", options, null, out var selected) && selected != null) { OperationManager.Instance.Do(selected); } @@ -1142,20 +1172,20 @@ private void ShowHelp() { return; } - var menuItem = MenuTracker.Instance.CurrentlyOpenMenuItem; + var menuItem = MenuTracker.GetFocusedMenuItemIfAny(app); // if we are in a menu if (menuItem != null) { return; } - - ChoicesDialog.Query("Help", this.GetHelp(), "Ok"); + + ChoicesDialog.Query(app, "Help", this.GetHelp(), "Ok"); } private void MoveControl(int deltaX, int deltaY) { - this.DoForSelectedViews((d) => new MoveViewOperation(d, deltaX, deltaY)); + this.DoForSelectedViews((d) => new MoveViewOperation(app, d, deltaX, deltaY)); } private void Delete() @@ -1167,7 +1197,7 @@ private void Delete() if (SelectionManager.Instance.Selected.Any()) { - var cmd = new DeleteViewOperation(SelectionManager.Instance.Selected.ToArray()); + var cmd = new DeleteViewOperation(app, SelectionManager.Instance.Selected.ToArray()); if (cmd.IsImpossible && cmd.PreventDeleting.Any()) @@ -1205,6 +1235,7 @@ private void DoForSelectedViews(Func operationFunc, bool allo if (selected.Length > 1) { var op = new CompositeOperation( + app, SelectionManager.Instance.Selected .Select(operationFunc).ToArray()); @@ -1233,7 +1264,7 @@ private void Open() }; ofd.Layout(); - Application.Run(ofd, this.ErrorHandler); + app.Run(ofd, this.ErrorHandler); if (!ofd.Canceled) { @@ -1250,14 +1281,14 @@ private void Open() } catch (Exception ex) { - ExceptionViewer.ShowException($"Failed to open '{ofd.Path}'", ex); + ExceptionViewer.ShowException(app,$"Failed to open '{ofd.Path}'", ex); } } } private bool ErrorHandler(Exception arg) { - ExceptionViewer.ShowException("Global Exception", arg); + ExceptionViewer.ShowException(app,"Global Exception", arg); return true; } @@ -1273,18 +1304,18 @@ private void Open(FileInfo toOpen) Task.Run(() => { - var decompiler = new CodeToView(new SourceCodeFile(toOpen)); + var decompiler = new CodeToView(app, new SourceCodeFile(toOpen)); instance = decompiler.CreateInstance(); }).ContinueWith( (t, _) => { // no longer loading - Application.Invoke(() => Application.RequestStop()); + app.Invoke(() => app.RequestStop()); if (t.Exception != null) { - Application.Invoke(() => - ExceptionViewer.ShowException($"Failed to open '{toOpen.Name}'", t.Exception)); + app.Invoke(() => + ExceptionViewer.ShowException(app, $"Failed to open '{toOpen.Name}'", t.Exception)); return; } @@ -1296,12 +1327,12 @@ private void Open(FileInfo toOpen) }, TaskScheduler.FromCurrentSynchronizationContext()); - Application.Run(open, this.ErrorHandler); + app.Run(open, this.ErrorHandler); } private void New() { - if (!Modals.Get("Create New View", "Ok", GetSupportedRootViews(), null, out var selected)) + if (!Modals.Get(app, "Create New View", "Ok", GetSupportedRootViews(), null, out var selected)) { return; } @@ -1315,7 +1346,7 @@ private void New() ofd.Style.PreserveFilenameOnDirectoryChanges = true; ofd.Layout(); - Application.Run(ofd); + app.Run(ofd); if (!ofd.Canceled) { @@ -1336,7 +1367,7 @@ private void New() if(!CodeDomArgs.IsValidIdentifier(files.ClassName)) { - ChoicesDialog.Query("Invalid Name",$"Invalid class name '{files.ClassName}'","Ok"); + ChoicesDialog.Query(app, "Invalid Name",$"Invalid class name '{files.ClassName}'","Ok"); return; } @@ -1354,7 +1385,7 @@ private void New() if (sb.Length > 0) { - if (!ChoicesDialog.Confirm("Overwrite Files?", $"The following files will be overwritten:{Environment.NewLine}{sb.ToString().TrimEnd()}", "Ok", "Cancel")) + if (!ChoicesDialog.Confirm(app, "Overwrite Files?", $"The following files will be overwritten:{Environment.NewLine}{sb.ToString().TrimEnd()}", "Ok", "Cancel")) { return; // user canceled overwrite } @@ -1364,7 +1395,7 @@ private void New() } catch (Exception ex) { - ExceptionViewer.ShowException($"Failed to create '{ofd.Path}'", ex); + ExceptionViewer.ShowException(app, $"Failed to create '{ofd.Path}'", ex); throw; } } @@ -1372,12 +1403,12 @@ private void New() private static Type[] GetSupportedRootViews() { - return new Type[] { typeof(Window), typeof(Dialog), typeof(View), typeof(Toplevel) }; + return new Type[] { typeof(Window), typeof(Dialog), typeof(View), typeof(Runnable) }; } private void New(FileInfo toOpen, Type typeToCreate, string? explicitNamespace) { - var viewToCode = new ViewToCode(); + var viewToCode = new ViewToCode(app); string? ns = explicitNamespace; // TODO: The following two if statements can be combined and run in a loop until the user either cancels or gets it right @@ -1385,7 +1416,7 @@ private void New(FileInfo toOpen, Type typeToCreate, string? explicitNamespace) if (string.IsNullOrWhiteSpace(ns)) { // prompt user for namespace - if (!Modals.GetString("Namespace", "Enter the namespace for your class", "YourNamespace", out ns)) + if (!Modals.GetString(app, "Namespace", "Enter the namespace for your class", "YourNamespace", out ns)) { // user cancelled typing a namespace return; @@ -1395,7 +1426,7 @@ private void New(FileInfo toOpen, Type typeToCreate, string? explicitNamespace) // Validate the namespace if (string.IsNullOrWhiteSpace(ns) || ns.Contains(' ') || char.IsDigit(ns.First())) { - MessageBox.ErrorQuery("Invalid Namespace", "Namespace must not contain spaces, be empty or begin with a number", "Ok"); + MessageBox.ErrorQuery(app, "Invalid Namespace", "Namespace must not contain spaces, be empty or begin with a number", "Ok"); return; } @@ -1416,12 +1447,12 @@ private void New(FileInfo toOpen, Type typeToCreate, string? explicitNamespace) (t, _) => { // no longer loading - Application.Invoke(() => Application.RequestStop()); + app.Invoke(() => app.RequestStop()); if (t.Exception != null) { - Application.Invoke(() => - ExceptionViewer.ShowException($"Failed to create '{toOpen.Name}'", t.Exception)); + app.Invoke(() => + ExceptionViewer.ShowException(app, $"Failed to create '{toOpen.Name}'", t.Exception)); return; } @@ -1433,12 +1464,12 @@ private void New(FileInfo toOpen, Type typeToCreate, string? explicitNamespace) }, TaskScheduler.FromCurrentSynchronizationContext()); - Application.Run(open, this.ErrorHandler); + app.Run(open, this.ErrorHandler); } private void ReplaceViewBeingEdited(Design design) { - Application.Invoke(() => + app.Invoke(() => { // remove the old view if (this.viewBeingEdited != null) @@ -1467,7 +1498,7 @@ private void Save() return; } - var viewToCode = new ViewToCode(); + var viewToCode = new ViewToCode(app); viewToCode.GenerateDesignerCs( this.viewBeingEdited, @@ -1490,7 +1521,7 @@ private void ShowAddViewWindow() var toAddTo = SelectionManager.Instance.GetMostSelectedContainerOrNull() ?? this.viewBeingEdited; OperationManager.Instance.Do( - new AddViewOperation(toAddTo)); + new AddViewOperation(app, toAddTo)); } private void ShowEditProperties() @@ -1504,7 +1535,7 @@ private void ShowEditProperties() private void ShowEditProperties(Design d) { - var edit = new EditDialog(d); - Application.Run(edit, this.ErrorHandler); + var edit = new EditDialog(app, d); + app.Run(edit, this.ErrorHandler); } } \ No newline at end of file diff --git a/src/UI/KeyMap.cs b/src/UI/KeyMap.cs index a4d323b4..1bc363f4 100644 --- a/src/UI/KeyMap.cs +++ b/src/UI/KeyMap.cs @@ -60,7 +60,7 @@ public KeyMap( ) Key.F2.ToString( ), Key.L.WithCtrl.ToString( ), Key.B.WithCtrl.ToString( ), - MouseFlags.Button3Clicked, + MouseFlags.RightButtonClicked, Key.C.WithCtrl.ToString( ), Key.V.WithCtrl.ToString( ), Key.R.WithCtrl.ToString( ), @@ -128,7 +128,7 @@ public KeyMap( ) public string Rename { get; init; } = Rename; /// - /// Gets the mouse button that opens the right click context menu. Defaults to which is + /// Gets the mouse button that opens the right click context menu. Defaults to which is /// the right mouse button. /// [JsonConverter( typeof( JsonStringEnumConverter ) )] diff --git a/src/UI/KeyboardManager.cs b/src/UI/KeyboardManager.cs index 761b2a89..6037c889 100644 --- a/src/UI/KeyboardManager.cs +++ b/src/UI/KeyboardManager.cs @@ -17,15 +17,18 @@ namespace TerminalGuiDesigner.UI; /// public class KeyboardManager { + private readonly IApplication app; private readonly KeyMap keyMap; private SetPropertyOperation? currentOperation; /// /// Initializes a new instance of the class. /// + /// /// User configurable keybindings for class functionality. - public KeyboardManager(KeyMap keyMap) + public KeyboardManager(IApplication app, KeyMap keyMap) { + this.app = app; this.keyMap = keyMap; } @@ -39,7 +42,7 @@ public KeyboardManager(KeyMap keyMap) /// if should be suppressed. public bool HandleKey(View focusedView, Key keystroke) { - var menuItem = MenuTracker.Instance.CurrentlyOpenMenuItem; + var menuItem = MenuTracker.GetFocusedMenuItemIfAny(app); // if we are in a menu if (menuItem != null) @@ -73,7 +76,7 @@ public bool HandleKey(View focusedView, Key keystroke) var nameProp = d.GetDesignableProperties().OfType().FirstOrDefault(); if (nameProp != null) { - EditDialog.SetPropertyToNewValue(d, nameProp, nameProp.GetValue()); + EditDialog.SetPropertyToNewValue(app, d, nameProp, nameProp.GetValue()); return true; } } @@ -99,22 +102,21 @@ private bool HandleKeyPressInMenu(View focusedView, MenuItem menuItem, Key keyst if (keystroke.ToString( ) == this.keyMap.Rename) { OperationManager.Instance.Do( - new RenameMenuItemOperation(menuItem)); + new RenameMenuItemOperation(this.app, menuItem)); return true; } if (keystroke == Key.Enter) { OperationManager.Instance.Do( - new AddMenuItemOperation(menuItem)); + new AddMenuItemOperation(this.app, menuItem)); - ChangeKeyTo(keystroke, Key.CursorDown); - return false; + return true; } if (keystroke.ToString( ) == this.keyMap.SetShortcut) { - menuItem.ShortcutKey = Modals.GetShortcut().KeyCode; + menuItem.Key = Modals.GetShortcut(app).KeyCode; focusedView.SetNeedsDraw(); return false; @@ -123,7 +125,7 @@ private bool HandleKeyPressInMenu(View focusedView, MenuItem menuItem, Key keyst if (keystroke.ToString( ) == this.keyMap.MoveRight) { OperationManager.Instance.Do( - new MoveMenuItemRightOperation(menuItem)); + new MoveMenuItemRightOperation(this.app, menuItem)); ChangeKeyTo(keystroke, Key.CursorUp); return true; @@ -132,7 +134,7 @@ private bool HandleKeyPressInMenu(View focusedView, MenuItem menuItem, Key keyst if (keystroke.ToString( ) == this.keyMap.MoveLeft) { OperationManager.Instance.Do( - new MoveMenuItemLeftOperation(menuItem)); + new MoveMenuItemLeftOperation(this.app, menuItem)); ChangeKeyTo(keystroke, Key.CursorDown); return false; @@ -141,7 +143,7 @@ private bool HandleKeyPressInMenu(View focusedView, MenuItem menuItem, Key keyst if (keystroke.ToString( ) == this.keyMap.MoveUp) { OperationManager.Instance.Do( - new MoveMenuItemOperation(menuItem, true)); + new MoveMenuItemOperation(this.app, menuItem, true)); ChangeKeyTo(keystroke, Key.CursorUp); return false; } @@ -149,7 +151,7 @@ private bool HandleKeyPressInMenu(View focusedView, MenuItem menuItem, Key keyst if (keystroke.ToString( ) == this.keyMap.MoveDown) { OperationManager.Instance.Do( - new MoveMenuItemOperation(menuItem, false)); + new MoveMenuItemOperation(this.app, menuItem, false)); ChangeKeyTo(keystroke, Key.CursorDown); return false; } @@ -160,18 +162,19 @@ private bool HandleKeyPressInMenu(View focusedView, MenuItem menuItem, Key keyst { // deleting the menu item using backspace to // remove all characters in the title or the Del key - var remove = new RemoveMenuItemOperation(menuItem); + var remove = new RemoveMenuItemOperation(this.app, menuItem); if (OperationManager.Instance.Do(remove)) { // if we are removing the last item if (remove.PrunedTopLevelMenu) { // if we deleted the last menu item + /* if (remove.Bar?.Menus.Length == 0) { remove.Bar.CloseMenu(false); return true; - } + }*/ // convert keystroke to left, // so we move to the next menu @@ -213,7 +216,7 @@ private bool HandleKeyPressInMenu(View focusedView, MenuItem menuItem, Key keyst if (newValue.Equals("---")) { if (OperationManager.Instance.Do( - new ConvertMenuItemToSeperatorOperation(menuItem))) + new ConvertMenuItemToSeperatorOperation(this.app, menuItem))) { return true; } @@ -262,7 +265,7 @@ private void ChangeKeyTo(Key keystroke, Key newKey) private void StartOperation(Design d) { // these can already handle editing themselves - if (d.View is DateField || d.View is TextField || d.View is TextView) + if (d.View is TextField || d.View is TextView) { return; } @@ -271,7 +274,7 @@ private void StartOperation(Design d) if (textProp != null) { - this.currentOperation = new SetPropertyOperation(d, textProp, d.View.Text, d.View.Text); + this.currentOperation = new SetPropertyOperation(this.app, d, textProp, d.View.Text, d.View.Text); } } diff --git a/src/UI/MouseManager.cs b/src/UI/MouseManager.cs index 8c7fefb7..2d61c75b 100644 --- a/src/UI/MouseManager.cs +++ b/src/UI/MouseManager.cs @@ -12,6 +12,7 @@ namespace TerminalGuiDesigner.UI; /// public class MouseManager { + private readonly IApplication app; private DragOperation? dragOperation = null; private ResizeOperation? resizeOperation = null; @@ -22,6 +23,11 @@ public class MouseManager private Point? selectionStart = null; private Point? selectionEnd = null; + public MouseManager(IApplication app) + { + this.app = app; + } + /// /// Gets the container that 'drag a box' selection is occurring in (if any). /// See also . @@ -40,21 +46,21 @@ public class MouseManager public IErrorReporter? ErrorReporter; /// - /// Responds to (by changing a 'drag a box' selection area + /// Responds to (by changing a 'drag a box' selection area /// or starting a resize etc). /// - /// The reported by . + /// The reported by . /// The root that is open in the . - public void HandleMouse(MouseEventArgs m, Design viewBeingEdited) + public void HandleMouse(Mouse m, Design viewBeingEdited) { // start dragging - if (m.Flags.HasFlag(MouseFlags.Button1Pressed) + if (m.Flags.HasFlag(MouseFlags.LeftButtonPressed) && this.resizeOperation == null && this.dragOperation == null && this.selectionStart == null) { - View? drag = viewBeingEdited.View.HitTest(m, out bool isBorder, out bool isLowerRight); + View? drag = viewBeingEdited.View.HitTest(app, m, out bool isBorder, out bool isLowerRight); // if user is ctrl+click - if (m.Flags.HasFlag(MouseFlags.ButtonCtrl) && drag != null) + if (m.Flags.HasFlag(MouseFlags.Ctrl) && drag != null) { // then add or remove the clicked item from the group selection var addOrRemove = drag.GetNearestDesign(); @@ -90,11 +96,11 @@ public void HandleMouse(MouseEventArgs m, Design viewBeingEdited) { var parent = drag.SuperView; - var dest = parent.ScreenToContent(m.Position); + var dest = parent.ScreenToContent(m.Position ?? Point.Empty); if (isLowerRight) { - this.resizeOperation = new ResizeOperation(design, dest.X, dest.Y); + this.resizeOperation = new ResizeOperation(this.app, design, dest.X, dest.Y); } else { @@ -106,6 +112,7 @@ public void HandleMouse(MouseEventArgs m, Design viewBeingEdited) { // drag all the views at once this.dragOperation = new DragOperation( + this.app, design, dest.X, dest.Y, @@ -114,7 +121,7 @@ public void HandleMouse(MouseEventArgs m, Design viewBeingEdited) else { // else drag only the non selected one - this.dragOperation = new DragOperation(design, dest.X, dest.Y, new Design[0]); + this.dragOperation = new DragOperation(this.app, design, dest.X, dest.Y, new Design[0]); } // don't begin an impossible drag! @@ -127,7 +134,7 @@ public void HandleMouse(MouseEventArgs m, Design viewBeingEdited) } // continue dragging a selection box - if (m.Flags.HasFlag(MouseFlags.Button1Pressed) && this.selectionStart != null) + if (m.Flags.HasFlag(MouseFlags.LeftButtonPressed) && this.selectionStart != null) { // move selection box to new mouse position this.selectionEnd = m.Position; @@ -136,9 +143,9 @@ public void HandleMouse(MouseEventArgs m, Design viewBeingEdited) } // continue dragging a view - if (m.Flags.HasFlag(MouseFlags.Button1Pressed) && this.dragOperation?.BeingDragged.View?.SuperView != null) + if (m.Flags.HasFlag(MouseFlags.LeftButtonPressed) && this.dragOperation?.BeingDragged.View?.SuperView != null && m.Position.HasValue) { - var dest = this.dragOperation?.BeingDragged.View.SuperView.ScreenToContent(m.Position); + var dest = this.dragOperation?.BeingDragged.View.SuperView.ScreenToContent(m.Position.Value); if (dest != null && this.dragOperation != null) { @@ -150,11 +157,12 @@ public void HandleMouse(MouseEventArgs m, Design viewBeingEdited) } // continue resizing - if (m.Flags.HasFlag(MouseFlags.Button1Pressed) + if (m.Flags.HasFlag(MouseFlags.LeftButtonPressed) && this.resizeOperation != null - && this.resizeOperation.BeingResized.View.SuperView != null) + && this.resizeOperation.BeingResized.View.SuperView != null + && m.Position.HasValue) { - var dest = this.resizeOperation.BeingResized.View.SuperView.ScreenToContent(m.Position); + var dest = this.resizeOperation.BeingResized.View.SuperView.ScreenToContent(m.Position.Value); this.resizeOperation.ContinueResize(dest); @@ -164,7 +172,7 @@ public void HandleMouse(MouseEventArgs m, Design viewBeingEdited) } // end things (because mouse released) - if (!m.Flags.HasFlag(MouseFlags.Button1Pressed)) + if (!m.Flags.HasFlag(MouseFlags.LeftButtonPressed)) { // end selection box if (this.selectionStart != null && this.SelectionBox != null && this.selectionContainer != null) @@ -189,7 +197,7 @@ public void HandleMouse(MouseEventArgs m, Design viewBeingEdited) if (this.dragOperation != null) { // see if we are dragging into a new container - var dropInto = viewBeingEdited.View.HitTest(m, out _, out _, this.dragOperation.BeingDragged.View); + var dropInto = viewBeingEdited.View.HitTest(app, m, out _, out _, this.dragOperation.BeingDragged.View); // TODO: this is quite hacky workaround for dropping on things like TabView top row. Need // a better solution to this. diff --git a/src/UI/ValueFactory.cs b/src/UI/ValueFactory.cs index b328a36c..0d6ead3e 100644 --- a/src/UI/ValueFactory.cs +++ b/src/UI/ValueFactory.cs @@ -47,26 +47,25 @@ static class ValueFactory typeof(DateTime?) }; - internal static bool GetNewValue(string propertyName, Design design, Type type, object? oldValue, out object? newValue, bool allowMultiLine) + internal static bool GetNewValue(IApplication app, string propertyName, Design design, Type type, object? oldValue, out object? newValue, bool allowMultiLine) { newValue = null; - - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(SliderOption<>)) + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(LinearRangeOption<>)) { - return RunEditor(new SliderOptionEditor(type.GetGenericArguments()[0], oldValue), out newValue); + return RunEditor(app, new SliderOptionEditor(app, type.GetGenericArguments()[0], oldValue), out newValue); } if (type == typeof(Pos)) { - return RunEditor(new PosEditor(design, (Pos)oldValue ?? throw new Exception("Pos property was unexpectedly null")), out newValue); + return RunEditor(app, new PosEditor(app, design, (Pos)oldValue ?? throw new Exception("Pos property was unexpectedly null")), out newValue); } if (type == typeof(Size)) { - return RunEditor(new SizeEditor((Size)(oldValue ?? throw new Exception($"Property {propertyName} is of Type Size but its current value is null"))), out newValue); + return RunEditor(app, new SizeEditor(app, (Size)(oldValue ?? throw new Exception($"Property {propertyName} is of Type Size but its current value is null"))), out newValue); } if (type == typeof(Point)) { var oldPoint = (Point)(oldValue ?? throw new Exception($"Property {propertyName} is of Type Point but its current value is null")); - var result = RunEditor(new PointEditor(oldPoint.X, oldPoint.Y), out newValue); + var result = RunEditor(app, new PointEditor(app, oldPoint.X, oldPoint.Y), out newValue); if (newValue != null) { @@ -78,22 +77,23 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, if (type == typeof(PointF)) { var oldPointF = (PointF)(oldValue ?? throw new Exception($"Property {propertyName} is of Type PointF but its current value is null")); - return RunEditor(new PointEditor(oldPointF.X, oldPointF.Y), out newValue); + return RunEditor(app, new PointEditor(app, oldPointF.X, oldPointF.Y), out newValue); } if (type == typeof(Dim)) { - return RunEditor(new DimEditor(design, (Dim)oldValue), out newValue); + return RunEditor(app, new DimEditor(app, design, (Dim)oldValue), out newValue); } if (type == typeof(bool)) { - int answer = ChoicesDialog.Query(propertyName, $"New value for {type}", "Yes", "No"); + int answer = ChoicesDialog.Query(app, propertyName, $"New value for {type}", "Yes", "No"); newValue = answer == 0 ? true : false; return answer != -1; } if ( - type.IsGenericType(typeof(IEnumerable<>)) || - type.IsAssignableTo(typeof(IList)) + type != typeof(string) && + (type.IsGenericType(typeof(IEnumerable<>)) || + type.IsAssignableTo(typeof(IList))) ) { var elementType = type.GetElementTypeEx() @@ -102,6 +102,7 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, if (elementType.IsValueType || elementType == typeof(string)) { if (Modals.GetArray( + app, propertyName, "New Array Value", type.GetElementType() ?? throw new Exception("Property was an Array but GetElementType returned null"), @@ -114,8 +115,8 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, } else { - var designer = new ArrayEditor(design, type.GetElementTypeEx(), (IList)oldValue); - Application.Run(designer); + var designer = new ArrayEditor(app, design, type.GetElementTypeEx(), (IList)oldValue); + app.Run(designer); if (!designer.Cancelled) { @@ -143,6 +144,7 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, .ToArray(); if (Modals.TryGetArray( + app, propertyName, "New List Value", oldValueAsArrayOfStrings, @@ -153,9 +155,9 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, } } else - if (type.IsEnum) + if (IsEnumOrNullableEnum(type, oldValue, out var enumValue)) { - if (Modals.GetEnum(propertyName, "New Enum Value", type, (Enum?)oldValue, out var resultEnum)) + if (Modals.GetEnum(app, propertyName, "New Enum Value", type, enumValue, out var resultEnum)) { newValue = resultEnum; return true; @@ -165,7 +167,7 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, if (ConvertChangeTypeSupports.Contains(type)) { var oldValueConverted = oldValue == null ? null : Convert.ChangeType(oldValue, type); - if (Modals.GetString(propertyName, $"New {type.Name} Value", oldValueConverted?.ToString(), out var result, allowMultiLine)) + if (Modals.GetString(app, propertyName, $"New {type.Name} Value", oldValueConverted?.ToString(), out var result, allowMultiLine)) { newValue = string.IsNullOrWhiteSpace(result) ? null : Convert.ChangeType(result, type); return true; @@ -175,7 +177,7 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, if (type== typeof(Rune) || type== typeof(Rune?)) { - if (Modals.GetChar(propertyName, "New Single Character", oldValue is null ? null : (char?)oldValue.ToPrimitive() ?? null, out var resultChar)) + if (Modals.GetChar(app, propertyName, "New Single Character", oldValue is null ? null : (char?)oldValue.ToPrimitive() ?? null, out var resultChar)) { newValue = resultChar == null ? null : new Rune(resultChar.Value); return true; @@ -188,7 +190,7 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, fd.AllowsMultipleSelection = false; fd.Layout(); - int answer = ChoicesDialog.Query(propertyName, $"Directory or File?", "Directory", "File", "Cancel"); + int answer = ChoicesDialog.Query(app, propertyName, $"Directory or File?", "Directory", "File", "Cancel"); if (answer < 0 || answer >= 2) { @@ -198,7 +200,7 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, fd.OpenMode = pickDir ? OpenMode.Directory : OpenMode.File; - Application.Run(fd); + app.Run(fd); if (fd.Canceled || string.IsNullOrWhiteSpace(fd.Path)) { return false; @@ -211,7 +213,7 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, } } else - if (Modals.GetString(propertyName, "New String Value", oldValue?.ToString(), out var result, allowMultiLine)) + if (Modals.GetString(app, propertyName, "New String Value", oldValue?.ToString(), out var result, allowMultiLine)) { newValue = result; return true; @@ -221,24 +223,43 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, return false; } - private static bool RunEditor(T editor, out object? result) where T : Dialog, IValueGetterDialog + private static bool IsEnumOrNullableEnum(Type type, object? oldValue, out Enum? enumValue) + { + if(type.IsEnum) + { + enumValue = (Enum?)oldValue; + return true; + } + + if(Nullable.GetUnderlyingType(type) is Type underlying && underlying.IsEnum) + { + enumValue = (Enum?)oldValue; + return true; + } + + enumValue = null; + return false; + } + + private static bool RunEditor(IApplication app, T editor, out object? result) where T : Dialog, IValueGetterDialog { - Application.Run(editor); + app.Run(editor); if (editor.Cancelled) { result = null; return false; } - result = editor.Result; + result = editor.ActualResult; return true; } - internal static bool GetNewValue(Design design, Property property, object? oldValue, out object? newValue) + internal static bool GetNewValue(IApplication app, Design design, Property property, object? oldValue, out object? newValue) { if (property is InstanceOfProperty inst) { if (Modals.Get( + app, property.PropertyInfo.Name, "New Value", typeof(Label).Assembly.GetTypes() @@ -262,7 +283,7 @@ internal static bool GetNewValue(Design design, Property property, object? oldVa } else { - return GetNewValue(property.PropertyInfo.Name, design, property.PropertyInfo.PropertyType, oldValue, out newValue, ValueFactory.AllowMultiLine(property)); + return GetNewValue(app, property.PropertyInfo.Name, design, property.PropertyInfo.PropertyType, oldValue, out newValue, ValueFactory.AllowMultiLine(property)); } } diff --git a/src/UI/Windows/ArrayEditor.Designer.cs b/src/UI/Windows/ArrayEditor.Designer.cs index 9e6dcdec..46f416e0 100644 --- a/src/UI/Windows/ArrayEditor.Designer.cs +++ b/src/UI/Windows/ArrayEditor.Designer.cs @@ -38,7 +38,7 @@ public partial class ArrayEditor : Terminal.Gui.Views.Dialog { private Terminal.Gui.Views.Button btnEdit; - private Terminal.Gui.Views.LineView lineView; + private Terminal.Gui.Views.Line Line; private Terminal.Gui.Views.Button btnOk; @@ -47,7 +47,7 @@ public partial class ArrayEditor : Terminal.Gui.Views.Dialog { private void InitializeComponent() { this.btnCancel = new Terminal.Gui.Views.Button(); this.btnOk = new Terminal.Gui.Views.Button(); - this.lineView = new Terminal.Gui.Views.LineView(); + this.Line = new Terminal.Gui.Views.Line(); this.btnEdit = new Terminal.Gui.Views.Button(); this.btnMoveDown = new Terminal.Gui.Views.Button(); this.btnMoveUp = new Terminal.Gui.Views.Button(); @@ -62,8 +62,7 @@ private void InitializeComponent() { this.Visible = true; this.Arrangement = (Terminal.Gui.ViewBase.ViewArrangement.Movable | Terminal.Gui.ViewBase.ViewArrangement.Overlapped); this.CanFocus = true; - this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Transparent; - this.Modal = true; + this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Transparent; this.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; this.Title = "Array Editor"; this.frameView.Width = Dim.Fill(0); @@ -73,7 +72,7 @@ private void InitializeComponent() { this.frameView.Visible = true; this.frameView.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.frameView.CanFocus = true; - this.frameView.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.frameView.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.frameView.Data = "frameView"; this.frameView.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; this.frameView.Title = "Elements"; @@ -85,15 +84,13 @@ private void InitializeComponent() { this.lvElements.Visible = true; this.lvElements.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.lvElements.CanFocus = true; - this.lvElements.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.lvElements.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.lvElements.Data = "lvElements"; this.lvElements.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; this.lvElements.Source = new Terminal.Gui.Views.ListWrapper(new System.Collections.ObjectModel.ObservableCollection(new string[] { "Item1", "Item2", "Item3"})); - this.lvElements.AllowsMarking = false; - this.lvElements.AllowsMultipleSelection = true; this.frameView.Add(this.lvElements); this.btnAddElement.Width = Dim.Auto(); this.btnAddElement.Height = Dim.Auto(); @@ -102,7 +99,7 @@ private void InitializeComponent() { this.btnAddElement.Visible = true; this.btnAddElement.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnAddElement.CanFocus = true; - this.btnAddElement.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnAddElement.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnAddElement.Data = "btnAddElement"; this.btnAddElement.Text = "Add"; this.btnAddElement.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -115,7 +112,7 @@ private void InitializeComponent() { this.btnDelete.Visible = true; this.btnDelete.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnDelete.CanFocus = true; - this.btnDelete.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnDelete.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnDelete.Data = "btnDelete"; this.btnDelete.Text = "Delete"; this.btnDelete.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -128,7 +125,7 @@ private void InitializeComponent() { this.btnMoveUp.Visible = true; this.btnMoveUp.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnMoveUp.CanFocus = true; - this.btnMoveUp.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnMoveUp.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnMoveUp.Data = "btnMoveUp"; this.btnMoveUp.Text = "Move Up"; this.btnMoveUp.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -141,7 +138,7 @@ private void InitializeComponent() { this.btnMoveDown.Visible = true; this.btnMoveDown.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnMoveDown.CanFocus = true; - this.btnMoveDown.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnMoveDown.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnMoveDown.Data = "btnMoveDown"; this.btnMoveDown.Text = "Move Down"; this.btnMoveDown.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -154,25 +151,24 @@ private void InitializeComponent() { this.btnEdit.Visible = true; this.btnEdit.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnEdit.CanFocus = true; - this.btnEdit.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnEdit.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnEdit.Data = "btnEdit"; this.btnEdit.Text = "Edit"; this.btnEdit.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; this.btnEdit.IsDefault = false; this.Add(this.btnEdit); - this.lineView.Width = Dim.Fill(1); - this.lineView.Height = 1; - this.lineView.X = -1; - this.lineView.Y = Pos.AnchorEnd(3); - this.lineView.Visible = true; - this.lineView.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; - this.lineView.CanFocus = true; - this.lineView.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; - this.lineView.Data = "lineView"; - this.lineView.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; - this.lineView.LineRune = new System.Text.Rune('─'); - this.lineView.Orientation = Terminal.Gui.ViewBase.Orientation.Horizontal; - this.Add(this.lineView); + this.Line.Width = Dim.Fill(1); + this.Line.Height = 1; + this.Line.X = -1; + this.Line.Y = Pos.AnchorEnd(3); + this.Line.Visible = true; + this.Line.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; + this.Line.CanFocus = true; + this.Line.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; + this.Line.Data = "Line"; + this.Line.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; + this.Line.Orientation = Terminal.Gui.ViewBase.Orientation.Horizontal; + this.Add(this.Line); this.btnOk.Width = Dim.Auto(); this.btnOk.Height = Dim.Auto(); this.btnOk.X = 0; @@ -180,7 +176,7 @@ private void InitializeComponent() { this.btnOk.Visible = true; this.btnOk.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnOk.CanFocus = true; - this.btnOk.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnOk.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnOk.Data = "btnOk"; this.btnOk.Text = "Ok"; this.btnOk.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -193,7 +189,7 @@ private void InitializeComponent() { this.btnCancel.Visible = true; this.btnCancel.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnCancel.CanFocus = true; - this.btnCancel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnCancel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnCancel.Data = "btnCancel"; this.btnCancel.Text = "Cancel"; this.btnCancel.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; diff --git a/src/UI/Windows/ArrayEditor.cs b/src/UI/Windows/ArrayEditor.cs index ab7aa885..9c06455f 100644 --- a/src/UI/Windows/ArrayEditor.cs +++ b/src/UI/Windows/ArrayEditor.cs @@ -28,13 +28,14 @@ public partial class ArrayEditor : IValueGetterDialog { /// public bool Cancelled { get; private set; } = true; + private readonly IApplication app; private readonly Design design; private Type elementType; /// /// The new array /// - public object Result => ResultAsList; + public object ActualResult => ResultAsList; /// /// Returns the being designed as an @@ -46,11 +47,13 @@ public partial class ArrayEditor : IValueGetterDialog { /// Creates a new instance of the editor configured to build lists of /// and showing initial values held in (if any). /// + /// /// /// /// - public ArrayEditor(Design design, Type elementType, IList oldValue) { + public ArrayEditor(IApplication app, Design design, Type elementType, IList oldValue) { InitializeComponent(); + this.app = app; this.design = design; this.elementType = elementType; @@ -77,7 +80,7 @@ public ArrayEditor(Design design, Type elementType, IList oldValue) { private void BtnMoveUp_Clicked(object sender, CommandEventArgs e) { // Moving up means reducing the index by 1 - var idx = lvElements.SelectedItem; + var idx = lvElements.SelectedItem ?? -1; if (idx >= 1 && idx < ResultAsList.Count) { @@ -97,7 +100,7 @@ private void BtnMoveUp_Clicked(object sender, CommandEventArgs e) private void BtnMoveDown_Clicked(object sender, CommandEventArgs e) { // Moving up means increasing the index by 1 - var idx = lvElements.SelectedItem; + var idx = lvElements.SelectedItem ?? -1; if (idx >= 0 && idx < ResultAsList.Count-1) { @@ -125,7 +128,7 @@ private void LvElements_KeyDown(object sender, Key e) private void DeleteSelectedItem() { - var idx = lvElements.SelectedItem; + var idx = lvElements.SelectedItem??-1; if (idx >= 0 && idx < ResultAsList.Count) { @@ -139,7 +142,7 @@ private void DeleteSelectedItem() private void BtnAddElement_Clicked(object sender, CommandEventArgs e) { - if(ValueFactory.GetNewValue("Element Value", design, this.elementType,null, out var newValue,true)) + if(ValueFactory.GetNewValue(app, "Element Value", design, this.elementType,null, out var newValue,true)) { ResultAsList.Add(newValue); } @@ -151,13 +154,13 @@ private void BtnAddElement_Clicked(object sender, CommandEventArgs e) } private void BtnEdit_Clicked(object sender, CommandEventArgs e) { - var idx = lvElements.SelectedItem; + var idx = lvElements.SelectedItem ?? -1; if (idx >= 0 && idx < ResultAsList.Count) { var toEdit = ResultAsList[idx]; - if (ValueFactory.GetNewValue("Element Value", design, this.elementType, toEdit, out var newValue, true)) + if (ValueFactory.GetNewValue(app, "Element Value", design, this.elementType, toEdit, out var newValue, true)) { // Replace old with new ResultAsList.RemoveAt(idx); @@ -176,14 +179,14 @@ private void BtnCancel_Clicked(object sender, CommandEventArgs e) { e.Handled = true; Cancelled = true; - Application.RequestStop(); + app.RequestStop(); } private void BtnOk_Clicked(object sender, CommandEventArgs e) { e.Handled = true; Cancelled = false; - Application.RequestStop(); + app.RequestStop(); } } } diff --git a/src/UI/Windows/BigListBox.cs b/src/UI/Windows/BigListBox.cs index 37f1dbf7..e57221bb 100644 --- a/src/UI/Windows/BigListBox.cs +++ b/src/UI/Windows/BigListBox.cs @@ -19,6 +19,7 @@ public class BigListBox private static readonly string[ ] ErrorStringArray = new[] { "Error" }; private readonly object taskCancellationLock = new(); private readonly ConcurrentBag cancelFiltering = new(); + private readonly IApplication app; private readonly Window win; private readonly ListView listView; private readonly bool addNull; @@ -45,6 +46,7 @@ public class BigListBox /// /// Initializes a new instance of the class. /// + /// The application instance. /// User message indicating what they are being asked to select. /// The confirmation text on the 'ok' button. /// True to add search text box. @@ -53,7 +55,8 @@ public class BigListBox /// Creates a selection option "Null" that returns a null selection. /// The optional existing value, if present it should be selected in the list. /// - public BigListBox(string prompt, + public BigListBox(IApplication app, + string prompt, string okText, in bool addSearch, IList collection, @@ -61,6 +64,7 @@ public BigListBox(string prompt, bool addNull, T? currentSelection, bool sort = true) { + this.app = app; this.AspectGetter = displayMember ?? (arg => arg?.ToString() ?? string.Empty); // Sort alphabetically according to display member @@ -80,8 +84,7 @@ public BigListBox(string prompt, // By using Dim.Fill(), it will automatically resize without manual intervention Width = Dim.Fill(), - Height = Dim.Fill(), - Modal = true, + Height = Dim.Fill() }; this.listView = new ListView() @@ -94,7 +97,7 @@ public BigListBox(string prompt, }; listView.SetSource(new ObservableCollection(ErrorStringArray)); - this.listView.MouseClick += this.ListView_MouseClick; + this.listView.MouseEvent += this.ListView_MouseClick; this.collection = this.BuildList(this.GetInitialSource()).ToList(); @@ -123,7 +126,7 @@ public BigListBox(string prompt, btnCancel.Accepting += (s, e) => { e.Handled = true; - Application.RequestStop(); + app.RequestStop(); }; if (addSearch) @@ -176,7 +179,7 @@ public BigListBox(string prompt, this.listView.SelectedItem = 0; } - this.callback = Application.AddTimeout(TimeSpan.FromMilliseconds(100), this.Timer); + this.callback = app.AddTimeout(TimeSpan.FromMilliseconds(100), this.Timer); this.listView.FocusDeepest(NavigationDirection.Forward,TabBehavior.TabStop); } @@ -198,29 +201,29 @@ public BigListBox(string prompt, /// True if selection was made (see ) or false if user cancelled the dialog. public bool ShowDialog() { - Application.Run(this.win); + app.Run(this.win); - Application.RemoveTimeout(this.callback); + app.RemoveTimeout(this.callback); return this.okClicked; } private void Accept() { - var selected = this.listView.SelectedItem; + var selected = this.listView.SelectedItem ?? -1; if (selected < 0 || selected >= this.collection.Count) { return; } this.okClicked = true; - Application.RequestStop(); + app.RequestStop(); this.Selected = this.collection[selected].Object; } - private void ListView_MouseClick(object? sender, MouseEventArgs obj) + private void ListView_MouseClick(object? sender, Mouse obj) { - if (obj.Flags.HasFlag(MouseFlags.Button1DoubleClicked)) + if (obj.Flags.HasFlag(MouseFlags.LeftButtonDoubleClicked)) { obj.Handled = true; this.Accept(); diff --git a/src/UI/Windows/ChoicesDialog.Designer.cs b/src/UI/Windows/ChoicesDialog.Designer.cs index 56fb66ff..3e52dc7b 100644 --- a/src/UI/Windows/ChoicesDialog.Designer.cs +++ b/src/UI/Windows/ChoicesDialog.Designer.cs @@ -51,8 +51,7 @@ private void InitializeComponent() { this.Arrangement = ((Terminal.Gui.ViewBase.ViewArrangement.Movable | Terminal.Gui.ViewBase.ViewArrangement.Resizable) | Terminal.Gui.ViewBase.ViewArrangement.Overlapped); this.CanFocus = true; - this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; - this.Modal = true; + this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; this.Title = ""; this.label1.Width = Dim.Fill(0); @@ -62,7 +61,7 @@ private void InitializeComponent() { this.label1.Visible = true; this.label1.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.label1.CanFocus = true; - this.label1.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.label1.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.label1.Data = "label1"; this.label1.Text = "lblMessage"; this.label1.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -74,7 +73,7 @@ private void InitializeComponent() { this.buttonPanel.Visible = true; this.buttonPanel.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.buttonPanel.CanFocus = true; - this.buttonPanel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.buttonPanel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.buttonPanel.Data = "buttonPanel"; this.buttonPanel.Text = ""; this.buttonPanel.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; @@ -86,7 +85,7 @@ private void InitializeComponent() { this.btn1.Visible = true; this.btn1.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btn1.CanFocus = true; - this.btn1.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btn1.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btn1.Data = "btn1"; this.btn1.Text = "btn1"; this.btn1.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -99,7 +98,7 @@ private void InitializeComponent() { this.btn2.Visible = true; this.btn2.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btn2.CanFocus = true; - this.btn2.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btn2.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btn2.Data = "btn2"; this.btn2.Text = "btn2"; this.btn2.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -112,7 +111,7 @@ private void InitializeComponent() { this.btn3.Visible = true; this.btn3.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btn3.CanFocus = true; - this.btn3.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btn3.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btn3.Data = "btn3"; this.btn3.Text = "btn3"; this.btn3.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -125,7 +124,7 @@ private void InitializeComponent() { this.btn4.Visible = true; this.btn4.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btn4.CanFocus = true; - this.btn4.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btn4.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btn4.Data = "btn4"; this.btn4.Text = "btn4"; this.btn4.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; diff --git a/src/UI/Windows/ChoicesDialog.cs b/src/UI/Windows/ChoicesDialog.cs index 7643d51a..dba68fe3 100644 --- a/src/UI/Windows/ChoicesDialog.cs +++ b/src/UI/Windows/ChoicesDialog.cs @@ -24,24 +24,28 @@ namespace TerminalGuiDesigner.UI.Windows; /// public partial class ChoicesDialog { + private readonly IApplication app; + /// /// The index of the button user clicked (starting at 0). /// - public int Result { get; private set; } + public int ActualResult { get; private set; } private string _title; /// - /// + /// /// + /// The application instance. /// /// /// /// - public ChoicesDialog(string title, string message, params string[] options) { - + public ChoicesDialog(IApplication app, string title, string message, params string[] options) { + const int defaultWidth = 50; + this.app = app; InitializeComponent(); if (options.Length == 0 || options.Length > 4) @@ -62,9 +66,9 @@ public ChoicesDialog(string title, string message, params string[] options) { var i2 = i; buttons[i].Accepting += (s,e) => { - Result = i2; + ActualResult = i2; e.Handled = true; - Application.RequestStop(); + app.RequestStop(); }; } @@ -86,34 +90,34 @@ public ChoicesDialog(string title, string message, params string[] options) { buttonPanel.Width = buttonWidth = buttons.Sum(b=>buttonPanel.SubViews.Contains(b) ? b.Frame.Width : 0) + 1; int maxWidthLine = TextFormatter.GetSumMaxCharWidth(message); - if (maxWidthLine > Application.Driver.Cols) + if (maxWidthLine > app.Driver.Cols) { - maxWidthLine = Application.Driver.Cols; + maxWidthLine = app.Driver.Cols; } - + maxWidthLine = Math.Max(maxWidthLine, defaultWidth); - - int textWidth = Math.Min(TextFormatter.GetSumMaxCharWidth(message, maxWidthLine), Application.Driver.Cols); + + int textWidth = Math.Min(TextFormatter.GetSumMaxCharWidth(message, maxWidthLine), app.Driver.Cols); int textHeight = message.Count (c=>c=='\n') + 4; - int msgboxHeight = Math.Min(Math.Max(1, textHeight) + 4, Application.Driver.Rows); // textHeight + (top + top padding + buttons + bottom) + int msgboxHeight = Math.Min(Math.Max(1, textHeight) + 4, app.Driver.Rows); // textHeight + (top + top padding + buttons + bottom) - Width = Math.Min(Math.Max(maxWidthLine, Math.Max(Title.GetColumns(), Math.Max(textWidth + 2, buttonWidth))), Application.Driver.Cols); + Width = Math.Min(Math.Max(maxWidthLine, Math.Max(Title.GetColumns(), Math.Max(textWidth + 2, buttonWidth))), app.Driver.Cols); Height = msgboxHeight; btn1.FocusDeepest(NavigationDirection.Forward, TabBehavior.TabGroup); } - internal static int Query(string title, string message, params string[] options) + internal static int Query(IApplication app, string title, string message, params string[] options) { - var dlg = new ChoicesDialog(title, message, options); - Application.Run(dlg); - return dlg.Result; + var dlg = new ChoicesDialog(app, title, message, options); + app.Run(dlg); + return dlg.ActualResult; } - internal static bool Confirm(string title, string message, string okText = "Yes", string cancelText = "No") + internal static bool Confirm(IApplication app, string title, string message, string okText = "Yes", string cancelText = "No") { - var dlg = new ChoicesDialog(title, message, okText, cancelText); - Application.Run(dlg); - return dlg.Result == 0; + var dlg = new ChoicesDialog(app, title, message, okText, cancelText); + app.Run(dlg); + return dlg.ActualResult == 0; } } diff --git a/src/UI/Windows/DimEditor.Designer.cs b/src/UI/Windows/DimEditor.Designer.cs index 89a25e50..2a1d5b67 100644 --- a/src/UI/Windows/DimEditor.Designer.cs +++ b/src/UI/Windows/DimEditor.Designer.cs @@ -24,9 +24,9 @@ namespace TerminalGuiDesigner.UI.Windows { public partial class DimEditor : Terminal.Gui.Views.Dialog { - private Terminal.Gui.Views.RadioGroup rgDimType; + private Terminal.Gui.Views.OptionSelector rgDimType; - private Terminal.Gui.Views.LineView lineview1; + private Terminal.Gui.Views.Line Line1; private Terminal.Gui.Views.Label lblValue; @@ -47,8 +47,8 @@ private void InitializeComponent() { this.lblOffset = new Terminal.Gui.Views.Label(); this.tbValue = new Terminal.Gui.Views.TextField(); this.lblValue = new Terminal.Gui.Views.Label(); - this.lineview1 = new Terminal.Gui.Views.LineView(); - this.rgDimType = new Terminal.Gui.Views.RadioGroup(); + this.Line1 = new Terminal.Gui.Views.Line(); + this.rgDimType = new Terminal.Gui.Views.OptionSelector(); this.Width = 40; this.Height = 11; this.X = Pos.Center(); @@ -56,8 +56,7 @@ private void InitializeComponent() { this.Visible = true; this.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Movable; this.CanFocus = true; - this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Transparent; - this.Modal = true; + this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Transparent; this.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; this.Title = ""; this.rgDimType.Width = 11; @@ -67,28 +66,27 @@ private void InitializeComponent() { this.rgDimType.Visible = true; this.rgDimType.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.rgDimType.CanFocus = true; - this.rgDimType.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.rgDimType.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.rgDimType.Data = "rgDimType"; this.rgDimType.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; - this.rgDimType.RadioLabels = new string[] { + this.rgDimType.Labels = new string[] { "Absolute", "Percent", "Fill", "Auto"}; this.Add(this.rgDimType); - this.lineview1.Width = 1; - this.lineview1.Height = 3; - this.lineview1.X = 12; - this.lineview1.Y = 1; - this.lineview1.Visible = true; - this.lineview1.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; - this.lineview1.CanFocus = false; - this.lineview1.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; - this.lineview1.Data = "lineview1"; - this.lineview1.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; - this.lineview1.LineRune = new System.Text.Rune('│'); - this.lineview1.Orientation = Terminal.Gui.ViewBase.Orientation.Vertical; - this.Add(this.lineview1); + this.Line1.Width = 1; + this.Line1.Height = 3; + this.Line1.X = 12; + this.Line1.Y = 1; + this.Line1.Visible = true; + this.Line1.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; + this.Line1.CanFocus = false; + this.Line1.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; + this.Line1.Data = "Line1"; + this.Line1.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; + this.Line1.Orientation = Terminal.Gui.ViewBase.Orientation.Vertical; + this.Add(this.Line1); this.lblValue.Width = 6; this.lblValue.Height = 1; this.lblValue.X = 14; @@ -96,7 +94,7 @@ private void InitializeComponent() { this.lblValue.Visible = true; this.lblValue.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.lblValue.CanFocus = false; - this.lblValue.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.lblValue.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.lblValue.Data = "lblValue"; this.lblValue.Text = "Value:"; this.lblValue.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; @@ -108,7 +106,7 @@ private void InitializeComponent() { this.tbValue.Visible = true; this.tbValue.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.tbValue.CanFocus = true; - this.tbValue.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.tbValue.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.tbValue.Secret = false; this.tbValue.Data = "tbValue"; this.tbValue.Text = ""; @@ -121,7 +119,7 @@ private void InitializeComponent() { this.lblOffset.Visible = true; this.lblOffset.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.lblOffset.CanFocus = false; - this.lblOffset.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.lblOffset.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.lblOffset.Data = "lblOffset"; this.lblOffset.Text = "Offset:"; this.lblOffset.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; @@ -133,7 +131,7 @@ private void InitializeComponent() { this.tbOffset.Visible = true; this.tbOffset.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.tbOffset.CanFocus = true; - this.tbOffset.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.tbOffset.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.tbOffset.Secret = false; this.tbOffset.Data = "tbOffset"; this.tbOffset.Text = ""; @@ -146,7 +144,7 @@ private void InitializeComponent() { this.btnOk.Visible = true; this.btnOk.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnOk.CanFocus = true; - this.btnOk.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnOk.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnOk.Data = "btnOk"; this.btnOk.Text = "Ok"; this.btnOk.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -159,7 +157,7 @@ private void InitializeComponent() { this.btnCancel.Visible = true; this.btnCancel.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnCancel.CanFocus = true; - this.btnCancel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnCancel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnCancel.Data = "btnCancel"; this.btnCancel.Text = "Cancel"; this.btnCancel.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; diff --git a/src/UI/Windows/DimEditor.cs b/src/UI/Windows/DimEditor.cs index 8a625173..676448bb 100644 --- a/src/UI/Windows/DimEditor.cs +++ b/src/UI/Windows/DimEditor.cs @@ -25,6 +25,7 @@ namespace TerminalGuiDesigner.UI.Windows; /// public partial class DimEditor : Dialog, IValueGetterDialog { + private readonly IApplication app; private Design design; /// @@ -32,7 +33,7 @@ public partial class DimEditor : Dialog, IValueGetterDialog /// radio buttons and text box values. /// [CanBeNull] - public object Result { get; private set; } + public object ActualResult { get; private set; } /// /// True if dialog was canceled. @@ -42,21 +43,22 @@ public partial class DimEditor : Dialog, IValueGetterDialog /// /// Creates a new instance of the class. /// + /// The application instance. /// /// Old value (if editing an existing instance) - public DimEditor(Design design, Dim oldValue) { + public DimEditor(IApplication app, Design design, Dim oldValue) { InitializeComponent(); - + + this.app = app; this.design = design; Title = "Dim Designer"; - Border.BorderStyle = LineStyle.Double; + Border.LineStyle = LineStyle.Double; btnOk.Accepting += BtnOk_Clicked; btnCancel.Accepting += BtnCancel_Clicked; Cancelled = true; - Modal = true; rgDimType.KeyDown += RgDimType_KeyPress; if(oldValue.GetDimType(out var type,out var value, out var offset)) @@ -64,16 +66,16 @@ public DimEditor(Design design, Dim oldValue) { switch(type) { case DimType.Absolute: - rgDimType.SelectedItem = 0; + rgDimType.Value = 0; break; case DimType.Percent: - rgDimType.SelectedItem = 1; + rgDimType.Value = 1; break; case DimType.Fill: - rgDimType.SelectedItem = 2; + rgDimType.Value = 2; break; case DimType.Auto: - rgDimType.SelectedItem = 3; + rgDimType.Value = 3; break; } @@ -83,7 +85,7 @@ public DimEditor(Design design, Dim oldValue) { SetupForCurrentDimType(); - rgDimType.SelectedItemChanged += DdType_SelectedItemChanged; + rgDimType.ValueChanged += DdType_SelectedItemChanged; } private void RgDimType_KeyPress(object sender, Key obj) @@ -97,13 +99,13 @@ private void RgDimType_KeyPress(object sender, Key obj) } } - private void DdType_SelectedItemChanged(object sender, SelectedItemChangedArgs obj) + private void DdType_SelectedItemChanged(object sender, ValueChangedEventArgs e) { SetupForCurrentDimType(); } private DimType GetDimType() { - return Enum.Parse(rgDimType.RadioLabels[rgDimType.SelectedItem].ToString()); + return Enum.Parse(rgDimType.Labels[rgDimType.Value??0].ToString()); } private void SetupForCurrentDimType() { @@ -152,15 +154,15 @@ private void BtnCancel_Clicked(object sender, CommandEventArgs e) { e.Handled = true; Cancelled = true; - Application.RequestStop(); + app.RequestStop(); } private void BtnOk_Clicked(object sender, CommandEventArgs e) { e.Handled = true; Cancelled = false; - Result = BuildResult(); - Application.RequestStop(); + ActualResult = BuildResult(); + app.RequestStop(); } private Dim BuildResult() diff --git a/src/UI/Windows/EditDialog.cs b/src/UI/Windows/EditDialog.cs index a03ed09d..6641168c 100644 --- a/src/UI/Windows/EditDialog.cs +++ b/src/UI/Windows/EditDialog.cs @@ -19,6 +19,7 @@ namespace TerminalGuiDesigner.UI.Windows; public class EditDialog : Window { private readonly ListView list; + private readonly IApplication app; private readonly Design design; private readonly List collection = new(); @@ -26,8 +27,9 @@ public class EditDialog : Window /// Initializes a new instance of the class. /// /// The on which you want to set properties. - public EditDialog(Design design) + public EditDialog(IApplication app, Design design) { + this.app = app; this.design = design; this.collection.Clear( ); this.collection.AddRange( this.design.GetDesignableProperties( ) @@ -74,7 +76,7 @@ public EditDialog(Design design) btnClose.Accepting += (s, e) => { e.Handled = true; - Application.RequestStop(); + app.RequestStop(); }; this.list.KeyDown += (s, e) => @@ -93,13 +95,13 @@ public EditDialog(Design design) } - internal static bool SetPropertyToNewValue(Design design, Property p, object? oldValue) + internal static bool SetPropertyToNewValue(IApplication app, Design design, Property p, object? oldValue) { // user wants to give us a new value for this property - if (ValueFactory.GetNewValue(design, p, p.GetValue(), out object? newValue)) + if (ValueFactory.GetNewValue(app, design, p, p.GetValue(), out object? newValue)) { OperationManager.Instance.Do( - new SetPropertyOperation(design, p, oldValue, newValue)); + new SetPropertyOperation(app, design, p, oldValue, newValue)); return true; } @@ -109,22 +111,22 @@ internal static bool SetPropertyToNewValue(Design design, Property p, object? ol private void SetProperty(bool setNull) { - if (this.list.SelectedItem != -1) + if (this.list.SelectedItem.HasValue) { try { - var p = this.collection[this.list.SelectedItem]; + var p = this.collection[this.list.SelectedItem.Value]; var oldValue = p.GetValue(); if (setNull) { // user wants to set this property to null/default OperationManager.Instance.Do( - new SetPropertyOperation(this.design, p, oldValue, null)); + new SetPropertyOperation(app, this.design, p, oldValue, null)); } else { - if (!SetPropertyToNewValue(this.design, p, oldValue)) + if (!SetPropertyToNewValue(app, this.design, p, oldValue)) { // user cancelled editing the value return; @@ -138,7 +140,7 @@ private void SetProperty(bool setNull) } catch (Exception e) { - ExceptionViewer.ShowException("Failed to set Property", e); + ExceptionViewer.ShowException(app, "Failed to set Property", e); } } } @@ -148,7 +150,7 @@ private void List_KeyPress(object? sender, Key obj) // TODO: Should really be using the _keyMap here if (obj == Key.DeleteChar) { - int rly = ChoicesDialog.Query("Clear", "Clear Property Value?", "Yes", "Cancel"); + int rly = ChoicesDialog.Query(app, "Clear", "Clear Property Value?", "Yes", "Cancel"); obj.Handled = true; if (rly == 0) diff --git a/src/UI/Windows/ExceptionViewer.cs b/src/UI/Windows/ExceptionViewer.cs index b2a8aa95..dbae607d 100644 --- a/src/UI/Windows/ExceptionViewer.cs +++ b/src/UI/Windows/ExceptionViewer.cs @@ -14,10 +14,11 @@ public class ExceptionViewer /// /// Launches a new modal instance of showing . /// + /// /// Message that describes what was going on when - /// occurred (e.g. "Could not open file x"). + /// occurred (e.g. "Could not open file x"). /// to show. - public static void ShowException(string errorText, Exception exception) + public static void ShowException(IApplication app, string errorText, Exception exception) { Logging.Critical(errorText + exception); var msg = GetExceptionText(errorText, exception, false); @@ -30,7 +31,7 @@ public static void ShowException(string errorText, Exception exception) Width = Dim.Fill(), Height = Dim.Fill() - 2, ReadOnly = true, - AllowsTab = false, + TabKeyAddsTab = false, }; bool toggleStack = true; @@ -44,7 +45,7 @@ public static void ShowException(string errorText, Exception exception) btnOk.Accepting += (s, e) => { e.Handled = true; - Application.RequestStop(); + app.RequestStop(); }; var btnStack = new Button() { @@ -70,7 +71,7 @@ public static void ShowException(string errorText, Exception exception) }; dlg.Add(textView); - Application.Run(dlg); + app.Run(dlg); } private static string GetExceptionText(string errorText, Exception exception, bool includeStackTrace) diff --git a/src/UI/Windows/GetTextDialog.cs b/src/UI/Windows/GetTextDialog.cs index 1174f1f3..25763dee 100644 --- a/src/UI/Windows/GetTextDialog.cs +++ b/src/UI/Windows/GetTextDialog.cs @@ -11,6 +11,7 @@ namespace TerminalGuiDesigner.UI.Windows; /// internal class GetTextDialog { + private readonly IApplication app; private readonly DialogArgs args; private readonly string? initialValue; private readonly Window win; @@ -19,8 +20,9 @@ internal class GetTextDialog private static CheckState lastKnownEnableNewlines = CheckState.UnChecked; private bool? multiLineChecked; - public GetTextDialog(DialogArgs args, string? initialValue) + public GetTextDialog(IApplication app, DialogArgs args, string? initialValue) { + this.app = app; this.args = args; this.initialValue = initialValue; @@ -29,7 +31,6 @@ public GetTextDialog(DialogArgs args, string? initialValue) Title = this.args.WindowTitle, X = 0, Y = 0, - Modal = true, }; var description = new Label @@ -55,7 +56,7 @@ public GetTextDialog(DialogArgs args, string? initialValue) Height = Dim.Fill(2), Width = Dim.Fill(2), Text = this.initialValue ?? string.Empty, - AllowsTab = false, + TabKeyAddsTab = false, }; this.textView.KeyDown += this.TextViewKeyPress; @@ -72,7 +73,8 @@ public GetTextDialog(DialogArgs args, string? initialValue) SetupMultiLineOptional(); } - this.textView.CursorPosition = new(0, 0); + // No longer supported? + // this.textView.CursorPosition = new(0, 0); // make it easier for user to replace this text with something else // by directly selecting it all so next keypress replaces text @@ -102,7 +104,7 @@ public GetTextDialog(DialogArgs args, string? initialValue) { e.Handled = true; this.okClicked = false; - Application.RequestStop(); + app.RequestStop(); }; var btnClear = new Button() @@ -128,7 +130,7 @@ private void SetupMultiLineForced() { Text = "Enable Newlines", X = Pos.AnchorEnd(), - CheckedState = CheckState.Checked, + Value = CheckState.Checked, Enabled = false }; win.Add(cbMultiLine); @@ -143,11 +145,11 @@ private void SetupMultiLineOptional() { Text = "Enable Newlines", X = Pos.AnchorEnd(), - CheckedState = lastKnownEnableNewlines + Value = lastKnownEnableNewlines }; - cbMultiLine.CheckedStateChanging += (s, e) => + cbMultiLine.ValueChanged += (s, e) => { - SetEnableNewlines(e.Result); + SetEnableNewlines(e.NewValue); }; win.Add(cbMultiLine); } @@ -160,14 +162,14 @@ private void SetEnableNewlines() private void SetEnableNewlines(CheckState newValue) { lastKnownEnableNewlines = newValue; - multiLineChecked = textView.AllowsReturn = newValue == CheckState.Checked; + multiLineChecked = textView.EnterKeyAddsLine = newValue == CheckState.Checked; } public string? ResultText { get; set; } public bool ShowDialog() { - Application.Run(this.win); + app.Run(this.win); return this.okClicked; } @@ -176,7 +178,7 @@ private void Accept() { this.okClicked = true; this.ResultText = this.textView.Text.ToString(); - Application.RequestStop(); + app.RequestStop(); } private void TextViewKeyPress(object? sender, Key key) diff --git a/src/UI/Windows/IValueGetterDialog.cs b/src/UI/Windows/IValueGetterDialog.cs index 7bca25f4..7ec506e8 100644 --- a/src/UI/Windows/IValueGetterDialog.cs +++ b/src/UI/Windows/IValueGetterDialog.cs @@ -3,6 +3,6 @@ namespace TerminalGuiDesigner.UI.Windows; public interface IValueGetterDialog { - public object? Result { get; } + public object? ActualResult { get; } public bool Cancelled { get; } } \ No newline at end of file diff --git a/src/UI/Windows/KeyBindingsUI.Designer.cs b/src/UI/Windows/KeyBindingsUI.Designer.cs index 17bcf452..12066834 100644 --- a/src/UI/Windows/KeyBindingsUI.Designer.cs +++ b/src/UI/Windows/KeyBindingsUI.Designer.cs @@ -47,8 +47,7 @@ private void InitializeComponent() { this.Visible = true; this.Arrangement = (Terminal.Gui.ViewBase.ViewArrangement.Movable | Terminal.Gui.ViewBase.ViewArrangement.Overlapped); this.CanFocus = true; - this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Transparent; - this.Modal = true; + this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Transparent; this.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; this.Title = "Keybindings"; this.tableView.Width = Dim.Fill(0); @@ -58,7 +57,7 @@ private void InitializeComponent() { this.tableView.Visible = true; this.tableView.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.tableView.CanFocus = true; - this.tableView.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.tableView.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.tableView.Data = "tableView"; this.tableView.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; this.tableView.FullRowSelect = true; @@ -96,7 +95,7 @@ private void InitializeComponent() { this.buttonPanel.Visible = true; this.buttonPanel.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.buttonPanel.CanFocus = true; - this.buttonPanel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.buttonPanel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.buttonPanel.Data = "buttonPanel"; this.buttonPanel.Text = ""; this.buttonPanel.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; @@ -108,7 +107,7 @@ private void InitializeComponent() { this.btnSave.Visible = true; this.btnSave.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnSave.CanFocus = true; - this.btnSave.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnSave.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnSave.Data = "btnSave"; this.btnSave.Text = "Save"; this.btnSave.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -121,7 +120,7 @@ private void InitializeComponent() { this.btnCancel.Visible = true; this.btnCancel.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnCancel.CanFocus = true; - this.btnCancel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnCancel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnCancel.Data = "btnCancel"; this.btnCancel.Text = "Cancel"; this.btnCancel.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -134,7 +133,7 @@ private void InitializeComponent() { this.btnReset.Visible = true; this.btnReset.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnReset.CanFocus = true; - this.btnReset.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnReset.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnReset.Data = "btnReset"; this.btnReset.Text = "Reset"; this.btnReset.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; diff --git a/src/UI/Windows/KeyBindingsUI.cs b/src/UI/Windows/KeyBindingsUI.cs index 2d45afa8..693a5954 100644 --- a/src/UI/Windows/KeyBindingsUI.cs +++ b/src/UI/Windows/KeyBindingsUI.cs @@ -18,14 +18,16 @@ namespace TerminalGuiDesigner.UI.Windows { using Terminal.Gui; - + public partial class KeyBindingsUI { + private readonly IApplication app; private readonly KeyMap keyMap; private readonly PropertyInfo[] _props; public bool Save { get; set; } = false; - public KeyBindingsUI(KeyMap keyMap) { + public KeyBindingsUI(IApplication app, KeyMap keyMap) { + this.app = app; this.keyMap = keyMap; InitializeComponent(); @@ -61,7 +63,7 @@ public KeyBindingsUI(KeyMap keyMap) { tableView.CellActivated += (s, e) => { var prop = _props[e.Row]; - var k = Modals.GetShortcut(); + var k = Modals.GetShortcut(app); prop.SetValue(this.keyMap,k.ToString()); this.SetNeedsDraw(); }; @@ -80,12 +82,12 @@ public KeyBindingsUI(KeyMap keyMap) { { Save = true; e.Handled = true; - Application.RequestStop(); + app.RequestStop(); }; btnCancel.Accepting += (s, e) => { e.Handled = true; - Application.RequestStop(); + app.RequestStop(); }; } diff --git a/src/UI/Windows/ListViewExtensions.cs b/src/UI/Windows/ListViewExtensions.cs deleted file mode 100644 index 96ec8324..00000000 --- a/src/UI/Windows/ListViewExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Terminal.Gui; -using Terminal.Gui.Views; - -namespace TerminalGuiDesigner.UI.Windows; - -/// -/// Extension methods for the class. -/// -public static class ListViewExtensions -{ - /// - /// Adjusts so that is - /// within the view bounds (scrolls to selected item if not in current view area). - /// - /// The to scroll. - public static void EnsureSelectedItemVisible(this ListView list) - { - if (list.SelectedItem < list.TopItem) - { - list.TopItem = list.SelectedItem; - } - else if (list.Frame.Height > 0 && list.SelectedItem >= list.TopItem + list.Frame.Height) - { - list.TopItem = Math.Max(list.SelectedItem - list.Frame.Height + 2, 0); - } - } -} diff --git a/src/UI/Windows/LoadingDialog.Designer.cs b/src/UI/Windows/LoadingDialog.Designer.cs index 2a1f7850..6d8541c8 100644 --- a/src/UI/Windows/LoadingDialog.Designer.cs +++ b/src/UI/Windows/LoadingDialog.Designer.cs @@ -35,8 +35,7 @@ private void InitializeComponent() { this.Visible = true; this.Arrangement = (Terminal.Gui.ViewBase.ViewArrangement.Movable | Terminal.Gui.ViewBase.ViewArrangement.Overlapped); this.CanFocus = true; - this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Transparent; - this.Modal = true; + this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Transparent; this.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; this.Title = "Loading..."; this.lblLoading.Width = 36; @@ -46,7 +45,7 @@ private void InitializeComponent() { this.lblLoading.Visible = true; this.lblLoading.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.lblLoading.CanFocus = false; - this.lblLoading.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.lblLoading.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.lblLoading.Data = "lblLoading"; this.lblLoading.Text = "Please wait ..."; this.lblLoading.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; diff --git a/src/UI/Windows/Modals.cs b/src/UI/Windows/Modals.cs index a36ca214..4b7f8834 100644 --- a/src/UI/Windows/Modals.cs +++ b/src/UI/Windows/Modals.cs @@ -17,14 +17,15 @@ public class Modals /// /// Prompts user to enter a number. /// + /// The application instance. /// Title for the pop-up. /// Text to show next to the entry field. /// Initial value to put into the entry field. /// Output value user typed. /// True if user confirmed a choice. - public static bool GetInt(string windowTitle, string entryLabel, int? initialValue, out int? result) + public static bool GetInt(IApplication app, string windowTitle, string entryLabel, int? initialValue, out int? result) { - if (GetString(windowTitle, entryLabel, initialValue.ToString(), out var newValue)) + if (GetString(app, windowTitle, entryLabel, initialValue.ToString(), out var newValue)) { if (string.IsNullOrWhiteSpace(newValue)) { @@ -43,9 +44,10 @@ public static bool GetInt(string windowTitle, string entryLabel, int? initialVal return false; } - internal static bool GetArray(string windowTitle, string entryLabel, Type arrayElement, Array? initialValue, out Array? result) + internal static bool GetArray(IApplication app, string windowTitle, string entryLabel, Type arrayElement, Array? initialValue, out Array? result) { var dlg = new GetTextDialog( + app, new DialogArgs() { WindowTitle = windowTitle, @@ -81,9 +83,10 @@ internal static bool GetArray(string windowTitle, string entryLabel, Type arrayE return false; } - internal static bool TryGetArray(string windowTitle, string entryLabel, Array? initialValue, out Array? result) + internal static bool TryGetArray(IApplication app, string windowTitle, string entryLabel, Array? initialValue, out Array? result) { var dlg = new GetTextDialog( + app, new () { WindowTitle = windowTitle, @@ -119,9 +122,10 @@ internal static bool TryGetArray(string windowTitle, string entryLabel, Array return false; } - internal static bool GetString(string windowTitle, string entryLabel, string? initialValue, out string? result, bool multiLine = false) + internal static bool GetString(IApplication app, string windowTitle, string entryLabel, string? initialValue, out string? result, bool multiLine = false) { var dlg = new GetTextDialog( + app, new DialogArgs() { WindowTitle = windowTitle, @@ -140,27 +144,32 @@ internal static bool GetString(string windowTitle, string entryLabel, string? in return false; } - internal static bool Get(string prompt, string okText, T[] collection, T? currentSelection, out T? selected, bool sort = true) + internal static bool Get(IApplication app, string prompt, string okText, T[] collection, T? currentSelection, out T? selected, bool sort = true) { - return Get(prompt, okText, true, collection, o => o is Type t ? t.Name : o?.ToString() ?? "Null", false, currentSelection, out selected,sort); + return Get(app, prompt, okText, true, collection, o => o is Type t ? t.Name : o?.ToString() ?? "Null", false, currentSelection, out selected,sort); } - internal static bool Get( string prompt, string okText, in bool addSearch, T[] collection, Func displayMember, bool addNull, [NotNullWhen( true )]T? currentSelection, [NotNullWhen( true )] out T? selected, bool sort=true ) + internal static bool Get(IApplication app, string prompt, string okText, in bool addSearch, T[] collection, Func displayMember, bool addNull, [NotNullWhen( true )]T? currentSelection, [NotNullWhen( true )] out T? selected, bool sort=true ) { - var pick = new BigListBox( prompt, okText, in addSearch, collection, displayMember, addNull, currentSelection,sort ); + var pick = new BigListBox(app, prompt, okText, in addSearch, collection, displayMember, addNull, currentSelection,sort ); bool toReturn = pick.ShowDialog( ); selected = pick.Selected; return toReturn; } - internal static bool GetEnum(string prompt, string okText, Type enumType, Enum? currentValue, out Enum? result) + internal static bool GetEnum(IApplication app, string prompt, string okText, Type enumType, Enum? currentValue, out Enum? result) { - return Get(prompt, okText, true, Enum.GetValues(enumType).Cast().ToArray(), o => o?.ToString() ?? "Null", false, currentValue, out result); + if(Nullable.GetUnderlyingType(enumType) is Type underlyingEnumType) + { + enumType = underlyingEnumType; + } + + return Get(app, prompt, okText, true, Enum.GetValues(enumType).Cast().ToArray(), o => o?.ToString() ?? "Null", false, currentValue, out result); } - internal static bool GetChar(string windowTitle, string entryLabel, char? oldValue, out char? resultChar) + internal static bool GetChar(IApplication app, string windowTitle, string entryLabel, char? oldValue, out char? resultChar) { - if (GetString(windowTitle, entryLabel, oldValue?.ToString() ?? string.Empty, out var result)) + if (GetString(app, windowTitle, entryLabel, oldValue?.ToString() ?? string.Empty, out var result)) { if (result == null || result.Length == 0) { @@ -179,7 +188,7 @@ internal static bool GetChar(string windowTitle, string entryLabel, char? oldVal return false; } - internal static Key GetShortcut() + internal static Key GetShortcut(IApplication app) { Key key = KeyCode.Null; var dlg = new LoadingDialog("Press Shortcut or Del"); @@ -189,10 +198,10 @@ internal static Key GetShortcut() { key = e; key.Handled = true; - Application.RequestStop(); + app.RequestStop(); } }; - Application.Run(dlg); + app.Run(dlg); return key == Key.DeleteChar ? KeyCode.Null : key; } diff --git a/src/UI/Windows/PointEditor.Designer.cs b/src/UI/Windows/PointEditor.Designer.cs index 869af219..b09ac1b2 100644 --- a/src/UI/Windows/PointEditor.Designer.cs +++ b/src/UI/Windows/PointEditor.Designer.cs @@ -50,8 +50,7 @@ private void InitializeComponent() { this.Visible = true; this.Arrangement = (Terminal.Gui.ViewBase.ViewArrangement.Movable | Terminal.Gui.ViewBase.ViewArrangement.Overlapped); this.CanFocus = true; - this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Transparent; - this.Modal = true; + this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Transparent; this.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; this.Title = "Point Designer"; this.lblX.Width = 2; @@ -61,7 +60,7 @@ private void InitializeComponent() { this.lblX.Visible = true; this.lblX.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.lblX.CanFocus = false; - this.lblX.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.lblX.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.lblX.Data = "lblX"; this.lblX.Text = "X:"; this.lblX.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; @@ -73,7 +72,7 @@ private void InitializeComponent() { this.tbX.Visible = true; this.tbX.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.tbX.CanFocus = true; - this.tbX.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.tbX.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.tbX.Secret = false; this.tbX.Data = "tbX"; this.tbX.Text = ""; @@ -86,7 +85,7 @@ private void InitializeComponent() { this.lblY.Visible = true; this.lblY.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.lblY.CanFocus = false; - this.lblY.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.lblY.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.lblY.Data = "lblY"; this.lblY.Text = "Y:"; this.lblY.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; @@ -98,7 +97,7 @@ private void InitializeComponent() { this.tbY.Visible = true; this.tbY.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.tbY.CanFocus = true; - this.tbY.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.tbY.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.tbY.Secret = false; this.tbY.Data = "tbY"; this.tbY.Text = ""; @@ -111,7 +110,7 @@ private void InitializeComponent() { this.btnOk.Visible = true; this.btnOk.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnOk.CanFocus = true; - this.btnOk.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnOk.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnOk.Data = "btnOk"; this.btnOk.Text = "Ok"; this.btnOk.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -124,7 +123,7 @@ private void InitializeComponent() { this.btnCancel.Visible = true; this.btnCancel.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnCancel.CanFocus = true; - this.btnCancel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnCancel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnCancel.Data = "btnCancel"; this.btnCancel.Text = "Cancel"; this.btnCancel.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; diff --git a/src/UI/Windows/PointEditor.cs b/src/UI/Windows/PointEditor.cs index 4cf86fe5..24e779e9 100644 --- a/src/UI/Windows/PointEditor.cs +++ b/src/UI/Windows/PointEditor.cs @@ -22,6 +22,8 @@ namespace TerminalGuiDesigner; /// public partial class PointEditor : IValueGetterDialog { + private readonly IApplication app; + /// /// Gets a value indicating whether user cancelled the dialog before /// making a choice. @@ -38,16 +40,18 @@ public partial class PointEditor : IValueGetterDialog { /// public float ResultY {get;private set;} - [CanBeNull] public object Result => new PointF(ResultX, ResultY); + [CanBeNull] public object ActualResult => new PointF(ResultX, ResultY); /// /// Creates a new instance of the class. /// + /// The application instance. /// Initial value to show in dialog for X. /// Initial value to show in dialog for Y. - public PointEditor(float x, float y) { + public PointEditor(IApplication app, float x, float y) { InitializeComponent(); + this.app = app; tbX.Text = x.ToString(); tbY.Text = y.ToString(); @@ -59,7 +63,7 @@ private void Cancel(object sender, CommandEventArgs e) { e.Handled = true; Cancelled = true; - Application.RequestStop(); + app.RequestStop(); } private void Ok(object sender, CommandEventArgs e) @@ -73,16 +77,16 @@ private void Ok(object sender, CommandEventArgs e) ResultY = y; Cancelled = false; - Application.RequestStop(); + app.RequestStop(); } else { - MessageBox.ErrorQuery(20,5,"Could no parse",$"Could not parse '{tbY.Text}'","Ok"); + MessageBox.ErrorQuery(app, "Could no parse",$"Could not parse '{tbY.Text}'","Ok"); } } else { - MessageBox.ErrorQuery(20,5,"Could no parse",$"Could not parse '{tbX.Text}'","Ok"); + MessageBox.ErrorQuery(app,"Could no parse",$"Could not parse '{tbX.Text}'","Ok"); } } } diff --git a/src/UI/Windows/PosEditor.Designer.cs b/src/UI/Windows/PosEditor.Designer.cs index 1a341fac..c4a0112a 100644 --- a/src/UI/Windows/PosEditor.Designer.cs +++ b/src/UI/Windows/PosEditor.Designer.cs @@ -24,9 +24,9 @@ namespace TerminalGuiDesigner.UI.Windows { public partial class PosEditor : Terminal.Gui.Views.Dialog { - private Terminal.Gui.Views.RadioGroup rgPosType; + private Terminal.Gui.Views.OptionSelector rgPosType; - private Terminal.Gui.Views.LineView lineview1; + private Terminal.Gui.Views.Line Line1; private Terminal.Gui.Views.Label lblValue; @@ -38,7 +38,7 @@ public partial class PosEditor : Terminal.Gui.Views.Dialog { private Terminal.Gui.Views.Label lblSide; - private Terminal.Gui.Views.RadioGroup rgSide; + private Terminal.Gui.Views.OptionSelector rgSide; private Terminal.Gui.Views.Label lblOffset; @@ -53,14 +53,14 @@ private void InitializeComponent() { this.btnOk = new Terminal.Gui.Views.Button(); this.tbOffset = new Terminal.Gui.Views.TextField(); this.lblOffset = new Terminal.Gui.Views.Label(); - this.rgSide = new Terminal.Gui.Views.RadioGroup(); + this.rgSide = new Terminal.Gui.Views.OptionSelector(); this.lblSide = new Terminal.Gui.Views.Label(); this.tbRelativeTo = new Terminal.Gui.Views.TextField(); this.lblRelativeTo = new Terminal.Gui.Views.Label(); this.tbValue = new Terminal.Gui.Views.TextField(); this.lblValue = new Terminal.Gui.Views.Label(); - this.lineview1 = new Terminal.Gui.Views.LineView(); - this.rgPosType = new Terminal.Gui.Views.RadioGroup(); + this.Line1 = new Terminal.Gui.Views.Line(); + this.rgPosType = new Terminal.Gui.Views.OptionSelector(); this.Width = 47; this.Height = 16; this.X = Pos.Center(); @@ -68,8 +68,7 @@ private void InitializeComponent() { this.Visible = true; this.Arrangement = (Terminal.Gui.ViewBase.ViewArrangement.Movable | Terminal.Gui.ViewBase.ViewArrangement.Overlapped); this.CanFocus = true; - this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Transparent; - this.Modal = true; + this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Transparent; this.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; this.Title = ""; this.rgPosType.Width = 12; @@ -79,29 +78,28 @@ private void InitializeComponent() { this.rgPosType.Visible = true; this.rgPosType.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.rgPosType.CanFocus = true; - this.rgPosType.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.rgPosType.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.rgPosType.Data = "rgPosType"; this.rgPosType.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; - this.rgPosType.RadioLabels = new string[] { + this.rgPosType.Labels = new string[] { "Absolute", "Percent", "Relative", "Center", "AnchorEnd"}; this.Add(this.rgPosType); - this.lineview1.Width = 1; - this.lineview1.Height = 5; - this.lineview1.X = 14; - this.lineview1.Y = 1; - this.lineview1.Visible = true; - this.lineview1.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; - this.lineview1.CanFocus = false; - this.lineview1.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; - this.lineview1.Data = "lineview1"; - this.lineview1.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; - this.lineview1.LineRune = new System.Text.Rune('│'); - this.lineview1.Orientation = Terminal.Gui.ViewBase.Orientation.Vertical; - this.Add(this.lineview1); + this.Line1.Width = 1; + this.Line1.Height = 5; + this.Line1.X = 14; + this.Line1.Y = 1; + this.Line1.Visible = true; + this.Line1.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; + this.Line1.CanFocus = false; + this.Line1.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; + this.Line1.Data = "Line1"; + this.Line1.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; + this.Line1.Orientation = Terminal.Gui.ViewBase.Orientation.Vertical; + this.Add(this.Line1); this.lblValue.Width = 6; this.lblValue.Height = 1; this.lblValue.X = 22; @@ -109,7 +107,7 @@ private void InitializeComponent() { this.lblValue.Visible = true; this.lblValue.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.lblValue.CanFocus = false; - this.lblValue.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.lblValue.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.lblValue.Data = "lblValue"; this.lblValue.Text = "Value:"; this.lblValue.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; @@ -121,7 +119,7 @@ private void InitializeComponent() { this.tbValue.Visible = true; this.tbValue.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.tbValue.CanFocus = true; - this.tbValue.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.tbValue.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.tbValue.Secret = false; this.tbValue.Data = "tbValue"; this.tbValue.Text = ""; @@ -134,7 +132,7 @@ private void InitializeComponent() { this.lblRelativeTo.Visible = true; this.lblRelativeTo.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.lblRelativeTo.CanFocus = false; - this.lblRelativeTo.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.lblRelativeTo.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.lblRelativeTo.Data = "lblRelativeTo"; this.lblRelativeTo.Text = "Relative To:"; this.lblRelativeTo.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; @@ -146,7 +144,7 @@ private void InitializeComponent() { this.tbRelativeTo.Visible = true; this.tbRelativeTo.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.tbRelativeTo.CanFocus = true; - this.tbRelativeTo.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.tbRelativeTo.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.tbRelativeTo.Secret = false; this.tbRelativeTo.Data = "tbRelativeTo"; this.tbRelativeTo.Text = ""; @@ -159,7 +157,7 @@ private void InitializeComponent() { this.lblSide.Visible = true; this.lblSide.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.lblSide.CanFocus = false; - this.lblSide.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.lblSide.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.lblSide.Data = "lblSide"; this.lblSide.Text = "Side:"; this.lblSide.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; @@ -171,10 +169,10 @@ private void InitializeComponent() { this.rgSide.Visible = true; this.rgSide.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.rgSide.CanFocus = true; - this.rgSide.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.rgSide.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.rgSide.Data = "rgSide"; this.rgSide.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; - this.rgSide.RadioLabels = new string[] { + this.rgSide.Labels = new string[] { "Left", "Top", "Right", @@ -187,7 +185,7 @@ private void InitializeComponent() { this.lblOffset.Visible = true; this.lblOffset.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.lblOffset.CanFocus = false; - this.lblOffset.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.lblOffset.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.lblOffset.Data = "lblOffset"; this.lblOffset.Text = "Offset:"; this.lblOffset.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; @@ -199,7 +197,7 @@ private void InitializeComponent() { this.tbOffset.Visible = true; this.tbOffset.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.tbOffset.CanFocus = true; - this.tbOffset.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.tbOffset.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.tbOffset.Secret = false; this.tbOffset.Data = "tbOffset"; this.tbOffset.Text = ""; @@ -212,7 +210,7 @@ private void InitializeComponent() { this.btnOk.Visible = true; this.btnOk.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnOk.CanFocus = true; - this.btnOk.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnOk.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnOk.Data = "btnOk"; this.btnOk.Text = "Ok"; this.btnOk.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -225,7 +223,7 @@ private void InitializeComponent() { this.btnCancel.Visible = true; this.btnCancel.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnCancel.CanFocus = true; - this.btnCancel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnCancel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnCancel.Data = "btnCancel"; this.btnCancel.Text = "Cancel"; this.btnCancel.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; diff --git a/src/UI/Windows/PosEditor.cs b/src/UI/Windows/PosEditor.cs index 9de07520..68bb5881 100644 --- a/src/UI/Windows/PosEditor.cs +++ b/src/UI/Windows/PosEditor.cs @@ -27,6 +27,7 @@ namespace TerminalGuiDesigner.UI.Windows; /// public partial class PosEditor : Dialog, IValueGetterDialog { + private readonly IApplication app; private Design design; private readonly Dictionary _siblings; @@ -35,7 +36,7 @@ public partial class PosEditor : Dialog, IValueGetterDialog { /// selected and text values entered - offset etc). /// [CanBeNull] - public object Result { get; private set; } + public object ActualResult { get; private set; } /// /// True if user cancelled the dialog instead of hitting Ok. @@ -46,22 +47,23 @@ public partial class PosEditor : Dialog, IValueGetterDialog { /// Prompt user to create a new value to populate /// on with. /// + /// The application instance. /// What to set the value on. /// The current value for the property. - public PosEditor(Design design, Pos oldValue) { + public PosEditor(IApplication app, Design design, Pos oldValue) { InitializeComponent(); - + + this.app = app; this.design = design; Title = "Pos Designer"; - Border.BorderStyle = LineStyle.Double; + Border.LineStyle = LineStyle.Double; rgPosType.KeyDown += RgPosType_KeyPress; btnOk.Accepting += BtnOk_Clicked; btnCancel.Accepting += BtnCancel_Clicked; Cancelled = true; - Modal = true; _siblings = design.GetSiblings().ToDictionary( d=>d.FieldName, @@ -79,22 +81,22 @@ public PosEditor(Design design, Pos oldValue) { switch(type) { case PosType.Absolute: - rgPosType.SelectedItem = 0; + rgPosType.Value = 0; break; case PosType.Percent: - rgPosType.SelectedItem = 1; + rgPosType.Value = 1; break; case PosType.Relative: - rgPosType.SelectedItem = 2; + rgPosType.Value = 2; if(relativeTo != null) tbRelativeTo.Text = relativeTo.FieldName; - rgSide.SelectedItem = (int)side; + rgSide.Value = (int)side; break; case PosType.Center: - rgPosType.SelectedItem = 3; + rgPosType.Value = 3; break; case PosType.AnchorEnd: - rgPosType.SelectedItem = 4; + rgPosType.Value = 4; break; } @@ -104,7 +106,7 @@ public PosEditor(Design design, Pos oldValue) { SetupForCurrentPosType(); - rgPosType.SelectedItemChanged += DdType_SelectedItemChanged; + rgPosType.ValueChanged += DdType_SelectedItemChanged; } @@ -119,7 +121,7 @@ private void RgPosType_KeyPress(object sender, Key key) } } - private void DdType_SelectedItemChanged(object sender, SelectedItemChangedArgs obj) + private void DdType_SelectedItemChanged(object sender, ValueChangedEventArgs e) { SetupForCurrentPosType(); } @@ -210,7 +212,7 @@ private void BtnCancel_Clicked(object sender, CommandEventArgs e) { e.Handled = true; Cancelled = true; - Application.RequestStop(); + app.RequestStop(); } private void BtnOk_Clicked(object sender, CommandEventArgs e) @@ -218,15 +220,15 @@ private void BtnOk_Clicked(object sender, CommandEventArgs e) e.Handled = true; if(GetPosType() == PosType.AnchorEnd && GetValue(out var value) && value <=0) { - if (!ChoicesDialog.Confirm("Anchor Without Margin", "Using AnchorEnd without a margin will result in a point outside of parent bounds.\nAre you sure?")) + if (!ChoicesDialog.Confirm(app, "Anchor Without Margin", "Using AnchorEnd without a margin will result in a point outside of parent bounds.\nAre you sure?")) { return; - } + } } Cancelled = !BuildPos(out var result); - Result = result; - Application.RequestStop(); + ActualResult = result; + app.RequestStop(); } private bool BuildPos(out Pos result) @@ -254,13 +256,13 @@ private bool BuildPos(out Pos result) private PosType GetPosType() { - return Enum.Parse(rgPosType.RadioLabels[rgPosType.SelectedItem].ToString()); + return Enum.Parse(rgPosType.Labels[rgPosType.Value ?? 0].ToString()); } private Side? GetSide() { - return rgSide.SelectedItem == -1 ? null : (Side)rgSide.SelectedItem; + return rgSide.Value == null ? null : (Side)rgSide.Value; } private bool GetOffset(out int offset) diff --git a/src/UI/Windows/SizeEditor.Designer.cs b/src/UI/Windows/SizeEditor.Designer.cs index 6b504626..eea0aa2c 100644 --- a/src/UI/Windows/SizeEditor.Designer.cs +++ b/src/UI/Windows/SizeEditor.Designer.cs @@ -50,8 +50,7 @@ private void InitializeComponent() { this.Visible = true; this.Arrangement = (Terminal.Gui.ViewBase.ViewArrangement.Movable | Terminal.Gui.ViewBase.ViewArrangement.Overlapped); this.CanFocus = true; - this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Transparent; - this.Modal = true; + this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Transparent; this.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; this.Title = "Size"; this.label1.Width = Dim.Auto(); @@ -61,7 +60,7 @@ private void InitializeComponent() { this.label1.Visible = true; this.label1.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.label1.CanFocus = false; - this.label1.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.label1.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.label1.Data = "label1"; this.label1.Text = "Width:"; this.label1.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; @@ -73,7 +72,7 @@ private void InitializeComponent() { this.tfWidth.Visible = true; this.tfWidth.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.tfWidth.CanFocus = true; - this.tfWidth.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.tfWidth.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.tfWidth.Secret = false; this.tfWidth.Data = "tfWidth"; this.tfWidth.Text = ""; @@ -86,7 +85,7 @@ private void InitializeComponent() { this.label12.Visible = true; this.label12.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.label12.CanFocus = false; - this.label12.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.label12.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.label12.Data = "label12"; this.label12.Text = "Height:"; this.label12.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; @@ -98,7 +97,7 @@ private void InitializeComponent() { this.tfHeight.Visible = true; this.tfHeight.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.tfHeight.CanFocus = true; - this.tfHeight.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.tfHeight.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.tfHeight.Secret = false; this.tfHeight.Data = "tfHeight"; this.tfHeight.Text = ""; @@ -111,7 +110,7 @@ private void InitializeComponent() { this.btnOk.Visible = true; this.btnOk.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnOk.CanFocus = true; - this.btnOk.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnOk.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnOk.Data = "btnOk"; this.btnOk.Text = "Ok"; this.btnOk.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -124,7 +123,7 @@ private void InitializeComponent() { this.btnCancel.Visible = true; this.btnCancel.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnCancel.CanFocus = true; - this.btnCancel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnCancel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnCancel.Data = "btnCancel"; this.btnCancel.Text = "Cancel"; this.btnCancel.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; diff --git a/src/UI/Windows/SizeEditor.cs b/src/UI/Windows/SizeEditor.cs index ac574cec..f455bfd8 100644 --- a/src/UI/Windows/SizeEditor.cs +++ b/src/UI/Windows/SizeEditor.cs @@ -13,18 +13,19 @@ namespace TerminalGuiDesigner.UI.Windows; using Terminal.Gui; +using Terminal.Gui.App; /// /// Popup editor for the class. /// public partial class SizeEditor : IValueGetterDialog { - + private readonly IApplication app; /// - /// The users edited + /// The users edited /// - public object? Result { get; private set; } + public object? ActualResult { get; private set; } /// /// True if user cancelled the dialog instead of hitting "Ok". @@ -34,11 +35,13 @@ public partial class SizeEditor : IValueGetterDialog /// /// Creates a new instance of the class. /// + /// The application instance. /// - public SizeEditor(Size s) + public SizeEditor(IApplication app, Size s) { + this.app = app; InitializeComponent(); - Result = s; + ActualResult = s; tfWidth.Text = s.Width.ToString(); tfHeight.Text = s.Height.ToString(); @@ -48,11 +51,11 @@ public SizeEditor(Size s) e.Handled = true; try { - Result = new Size(int.Parse(tfWidth.Text.ToString()), int.Parse(tfHeight.Text.ToString())); + ActualResult = new Size(int.Parse(tfWidth.Text.ToString()), int.Parse(tfHeight.Text.ToString())); } catch (Exception ex) { - MessageBox.ErrorQuery("Bad Value", ex.Message); + MessageBox.ErrorQuery(app, "Bad Value", ex.Message); return; } diff --git a/src/UI/Windows/SliderOptionEditor.Designer.cs b/src/UI/Windows/SliderOptionEditor.Designer.cs index be09432f..cbed3eb0 100644 --- a/src/UI/Windows/SliderOptionEditor.Designer.cs +++ b/src/UI/Windows/SliderOptionEditor.Designer.cs @@ -62,8 +62,7 @@ private void InitializeComponent() { this.Visible = true; this.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Movable; this.CanFocus = true; - this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Transparent; - this.Modal = true; + this.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Transparent; this.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; this.Title = "OptionEditor"; this.label.Width = Dim.Auto(); @@ -73,7 +72,7 @@ private void InitializeComponent() { this.label.Visible = true; this.label.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.label.CanFocus = false; - this.label.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.label.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.label.Data = "label"; this.label.Text = "Legend:"; this.label.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; @@ -85,7 +84,7 @@ private void InitializeComponent() { this.tfLegend.Visible = true; this.tfLegend.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.tfLegend.CanFocus = true; - this.tfLegend.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.tfLegend.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.tfLegend.Secret = false; this.tfLegend.Data = "tfLegend"; this.tfLegend.Text = ""; @@ -98,7 +97,7 @@ private void InitializeComponent() { this.label2.Visible = true; this.label2.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.label2.CanFocus = false; - this.label2.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.label2.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.label2.Data = "label2"; this.label2.Text = "Abbreviation:"; this.label2.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; @@ -110,7 +109,7 @@ private void InitializeComponent() { this.tfLegendAbbr.Visible = true; this.tfLegendAbbr.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.tfLegendAbbr.CanFocus = true; - this.tfLegendAbbr.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.tfLegendAbbr.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.tfLegendAbbr.Secret = false; this.tfLegendAbbr.Data = "tfLegendAbbr"; this.tfLegendAbbr.Text = ""; @@ -123,7 +122,7 @@ private void InitializeComponent() { this.lblOneChar.Visible = true; this.lblOneChar.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.lblOneChar.CanFocus = false; - this.lblOneChar.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.lblOneChar.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.lblOneChar.Data = "lblOneChar"; this.lblOneChar.Text = "(Single Char) "; this.lblOneChar.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -135,7 +134,7 @@ private void InitializeComponent() { this.label3.Visible = true; this.label3.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.label3.CanFocus = false; - this.label3.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.label3.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.label3.Data = "label3"; this.label3.Text = "Data:"; this.label3.TextAlignment = Terminal.Gui.ViewBase.Alignment.Start; @@ -147,7 +146,7 @@ private void InitializeComponent() { this.tfData.Visible = true; this.tfData.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.tfData.CanFocus = true; - this.tfData.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.tfData.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.tfData.Secret = false; this.tfData.Data = "tfData"; this.tfData.Text = ""; @@ -160,7 +159,7 @@ private void InitializeComponent() { this.lblType.Visible = true; this.lblType.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.lblType.CanFocus = true; - this.lblType.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.None; + this.lblType.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.None; this.lblType.Data = "lblType"; this.lblType.Text = "( Type ) "; this.lblType.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -172,7 +171,7 @@ private void InitializeComponent() { this.btnOk.Visible = true; this.btnOk.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnOk.CanFocus = true; - this.btnOk.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnOk.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnOk.Data = "btnOk"; this.btnOk.Text = "Ok"; this.btnOk.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; @@ -185,7 +184,7 @@ private void InitializeComponent() { this.btnCancel.Visible = true; this.btnCancel.Arrangement = Terminal.Gui.ViewBase.ViewArrangement.Fixed; this.btnCancel.CanFocus = true; - this.btnCancel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyle.Opaque; + this.btnCancel.ShadowStyle = Terminal.Gui.ViewBase.ShadowStyles.Opaque; this.btnCancel.Data = "btnCancel"; this.btnCancel.Text = "Cancel"; this.btnCancel.TextAlignment = Terminal.Gui.ViewBase.Alignment.Center; diff --git a/src/UI/Windows/SliderOptionEditor.cs b/src/UI/Windows/SliderOptionEditor.cs index 7528d618..90a75eb9 100644 --- a/src/UI/Windows/SliderOptionEditor.cs +++ b/src/UI/Windows/SliderOptionEditor.cs @@ -14,13 +14,12 @@ using Terminal.Gui.Views; namespace TerminalGuiDesigner.UI.Windows { - using System.Reflection; using System.Text; - using Terminal.Gui; - - + + public partial class SliderOptionEditor : IValueGetterDialog { + private readonly IApplication app; private readonly Type genericTypeArgument; private readonly Type sliderOptionType; @@ -32,7 +31,7 @@ public partial class SliderOptionEditor : IValueGetterDialog /// /// The resulting value as configured by the user /// - public object Result { get; internal set; } + public object ActualResult { get; internal set; } /// /// Creates a new instance of the Designer to create an instance of @@ -40,11 +39,12 @@ public partial class SliderOptionEditor : IValueGetterDialog /// /// The T Type of the you want to design /// Previous value (if editing an existing instance). - public SliderOptionEditor(Type genericTypeArgument, object? oldValue) { + public SliderOptionEditor(IApplication app, Type genericTypeArgument, object? oldValue) { InitializeComponent(); + this.app = app; this.genericTypeArgument = genericTypeArgument; - this.sliderOptionType = typeof(SliderOption<>).MakeGenericType(this.genericTypeArgument); + this.sliderOptionType = typeof(LinearRangeOption<>).MakeGenericType(this.genericTypeArgument); btnOk.Accepting += BtnOk_Clicked; btnCancel.Accepting += BtnCancel_Clicked; @@ -76,7 +76,7 @@ private void BtnCancel_Clicked(object sender, CommandEventArgs e) { e.Handled = true; this.Cancelled = true; - Application.RequestStop(); + app.RequestStop(); } private void BtnOk_Clicked(object sender, CommandEventArgs e) @@ -88,33 +88,33 @@ private void BtnOk_Clicked(object sender, CommandEventArgs e) } catch(Exception ex) { - ExceptionViewer.ShowException("Could not build result", ex); + ExceptionViewer.ShowException(app, "Could not build result", ex); return; } this.Cancelled = false; - Application.RequestStop(); + app.RequestStop(); } private void BuildResult() { - Result = Activator.CreateInstance(sliderOptionType); + ActualResult = Activator.CreateInstance(sliderOptionType); var p = sliderOptionType.GetProperty("Legend"); - p.SetValue(Result, tfLegend.Text); + p.SetValue(ActualResult, tfLegend.Text); p = sliderOptionType.GetProperty("LegendAbbr"); - p.SetValue(Result, new Rune(tfLegendAbbr.Text[0])); + p.SetValue(ActualResult, new Rune(tfLegendAbbr.Text[0])); p = sliderOptionType.GetProperty("Data"); if(this.genericTypeArgument == typeof(string)) { - p.SetValue(Result, tfData.Text); + p.SetValue(ActualResult, tfData.Text); } else { - p.SetValue(Result, Convert.ChangeType(tfData.Text, this.genericTypeArgument)); + p.SetValue(ActualResult, Convert.ChangeType(tfData.Text, this.genericTypeArgument)); } } diff --git a/src/ViewExtensions.cs b/src/ViewExtensions.cs index 29510db0..dfdaf2f4 100644 --- a/src/ViewExtensions.cs +++ b/src/ViewExtensions.cs @@ -13,9 +13,9 @@ namespace TerminalGuiDesigner; /// public static class ViewExtensions { - public static View? FindDeepestView(Point screenPoint) + public static View? FindDeepestView(IApplication app, Point screenPoint) { - return View.GetViewsAtLocation(Application.Top,screenPoint).LastOrDefault(v=> v!= null); + return View.GetViewsAtLocation(app.TopRunnableView, screenPoint).LastOrDefault(v=> v!= null); } /// /// Returns the sub-views of skipping out any @@ -219,7 +219,7 @@ v is Window || /// True if no visible border and . public static bool IsBorderlessContainerView(this View v) { - if (v is Toplevel) + if (v is Runnable) { return false; } @@ -250,30 +250,31 @@ public static bool IsBorderlessContainerView(this View v) /// Screen coordinates. /// True if the click lands on the border of the returned . /// True if the click lands in the lower right of the returned . + /// The application instance. /// One or more to ignore (click through) when performing the hit test. /// The at the given screen location or null if none found. - public static View? HitTest(this View w, MouseEventArgs m, out bool isBorder, out bool isLowerRight, params View[] ignoring) + public static View? HitTest(this View w, IApplication app, Mouse m, out bool isBorder, out bool isLowerRight, params View[] ignoring) { // hide the views while we perform the hit test foreach (View v in ignoring) { v.Visible = false; } - - var hit = ViewExtensions.FindDeepestView(m.Position); + + var hit = ViewExtensions.FindDeepestView(app, m.Position ?? Point.Empty); hit = UnpackHitView(hit); int resizeBoxArea = 2; - if (hit != null) + if (hit != null && m.Position.HasValue) { var screenFrame = hit.FrameToScreen(); if (m.Position != new Point(screenFrame.X, screenFrame.Y)) { - isLowerRight = Math.Abs(screenFrame.X + screenFrame.Width - m.Position.X) <= resizeBoxArea - && Math.Abs(screenFrame.Y + screenFrame.Height - m.Position.Y) <= resizeBoxArea; + isLowerRight = Math.Abs(screenFrame.X + screenFrame.Width - m.Position.Value.X) <= resizeBoxArea + && Math.Abs(screenFrame.Y + screenFrame.Height - m.Position.Value.Y) <= resizeBoxArea; } else { @@ -281,10 +282,10 @@ public static bool IsBorderlessContainerView(this View v) } isBorder = - m.Position.X == screenFrame.X + screenFrame.Width - 1 || - m.Position.X == screenFrame.X || - m.Position.Y == screenFrame.Y + screenFrame.Height - 1 || - m.Position.Y == screenFrame.Y; + m.Position.Value.X == screenFrame.X + screenFrame.Width - 1 || + m.Position.Value.X == screenFrame.X || + m.Position.Value.Y == screenFrame.Y + screenFrame.Height - 1 || + m.Position.Value.Y == screenFrame.Y; } else { @@ -297,8 +298,8 @@ public static bool IsBorderlessContainerView(this View v) { v.Visible = true; } - - return hit is Adornment a ? a.Parent : hit; + + return hit is AdornmentView a ? a.SuperView : hit; } @@ -321,12 +322,6 @@ public static bool IsBorderlessContainerView(this View v) } // Translate clicks in the border as the real View being clicked - if (hit is Border b) - { - hit = b.Parent; - - } - if (hit?.IsAdornment() ?? false) { hit = hit.GetAdornmentParent(); @@ -447,14 +442,13 @@ private static bool HasNoBorderProperty(this View v) /// /// Returns if is part of - /// a (either directly or embedded sub view of - /// one - e.g. ). + /// an Adornment (either directly or embedded sub view of one - e.g. ). /// /// /// public static bool IsAdornment(this View v) { - return v is Adornment || v.AnySuperViewIs(); + return v is AdornmentView || v.AnySuperViewIs(); } /// @@ -481,8 +475,8 @@ public static bool AnySuperViewIs(this View v) where T : View } /// - /// Returns the of - /// if it is an . Or if is not + /// Returns the of + /// if it is an . Or if is not /// directly an adornment but then the method /// will traverse up hierarchy until parent is found. /// @@ -492,9 +486,9 @@ public static bool AnySuperViewIs(this View v) where T : View { while (v != null) { - if (v is Adornment a) + if(v is AdornmentView av) { - return a.Parent; + return av.Adornment?.Parent; } v = v.SuperView; diff --git a/src/ViewFactory.cs b/src/ViewFactory.cs index 0afd09c6..ee121862 100644 --- a/src/ViewFactory.cs +++ b/src/ViewFactory.cs @@ -1,6 +1,7 @@ using System.Collections.ObjectModel; using System.Data; using Terminal.Gui; +using Terminal.Gui.App; using Terminal.Gui.Drawing; using Terminal.Gui.Input; using Terminal.Gui.ViewBase; @@ -33,7 +34,7 @@ public static class ViewFactory internal static readonly Type[] KnownUnsupportedTypes = [ - typeof( Toplevel ), + typeof( Runnable ), typeof( Dialog ), typeof( FileDialog ), typeof( SaveDialog ), @@ -42,9 +43,6 @@ public static class ViewFactory // BUG These seem to cause stack overflows in CreateSubControlDesigns (see TestAddView_RoundTrip) typeof( Wizard ), typeof( WizardStep ), - - // Ignore menu bar v2 for now - typeof(MenuBarv2), // This is unstable when added directly as a view see https://github.com/gui-cs/Terminal.Gui/issues/3664 typeof(Shortcut), @@ -52,14 +50,23 @@ public static class ViewFactory typeof(Tab), typeof(CharMap), typeof(LegendAnnotation), - typeof(Menuv2), typeof(ScrollBar), typeof(ScrollSlider), - typeof(TileView), - // Terminal.Gui combo boxes do not really work properly - typeof(ComboBox), - typeof(FlagSelector<>) + + typeof(FlagSelector<>), + typeof(Dialog<>), + typeof(Prompt<,>), + typeof(FlagSelector<>), + typeof(Runnable<>), + + // Could proably support later on + typeof(OptionSelector<>), + typeof(DropDownList<>), + typeof(Popover<,>), + typeof(ListView<>), + typeof(ToolTipHost<>), + typeof(MarginView) ]; /// @@ -77,8 +84,10 @@ internal static MenuBarItem[] DefaultMenuBarItems { return [ - new( "_File (F9)", - [ new MenuItem( DefaultMenuItemText, string.Empty, static ( ) => { } ) ] ) + new MenuBarItem( "_File", + [ new MenuItem( DefaultMenuItemText, string.Empty, static ( ) => { } )] ){ + Key = Key.F9 + } ]; } } @@ -97,12 +106,10 @@ internal static MenuBarItem[] DefaultMenuBarItems IsValueType: false }) .Where(filteredType => filteredType == typeof(View) || filteredType.IsSubclassOf(typeof(View)) - && filteredType != typeof(Adornment) - && filteredType != typeof(FlagSelector<>) - && filteredType != typeof(FlagSelector<>)) + && filteredType != typeof(AdornmentView)) .Except(KnownUnsupportedTypes) // Slider is an alias of Slider so don't offer that - .Where(vt => vt != typeof(Slider)); + .Where(vt => vt != typeof(LinearRange)); private static bool IsSupportedType( this Type t ) { @@ -148,12 +155,8 @@ public static T Create(int? width = null, int? height = null, string? text = switch ( newView ) { - case TimeField: - SetDefaultDimensions(newView, width ?? 9, height ?? 1); - break; case Button: case CheckBox: - case ComboBox: case Label: newView.SetActualText(text ?? "Heya"); SetDefaultDimensionsDimAuto(newView); @@ -175,9 +178,7 @@ public static T Create(int? width = null, int? height = null, string? text = SetDefaultDimensions(newView, width ?? 10, height ?? 4); break; case Line: - case Slider: - case TileView: - SetDefaultDimensions( newView, width ?? 4, height ?? 1 ); + case LinearRange: break; case TableView tv: var dt = new DataTable( ); @@ -193,15 +194,17 @@ public static T Create(int? width = null, int? height = null, string? text = tv.AddEmptyTab( "Tab2" ); SetDefaultDimensions( newView, width ?? 50, height ?? 5 ); break; + case TimeEditor te: + SetDefaultDimensions(newView, width ?? 10, height ?? 1); + break; + case DateEditor de: + SetDefaultDimensions(newView, width ?? 10, height ?? 1); + break; case TextValidateField tvf: tvf.Provider = new TextRegexProvider( ".*" ); tvf.Text = text ?? "Heya"; SetDefaultDimensions( newView, width ?? 5, height ?? 1 ); break; - case DateField df: - df.Date = DateTime.Today; - SetDefaultDimensions( newView, width ?? 20, height ?? 1 ); - break; case TextField tf: tf.Text = text ?? "Heya"; SetDefaultDimensions( newView, width ?? 5, height ?? 1 ); @@ -216,8 +219,8 @@ public static T Create(int? width = null, int? height = null, string? text = case StatusBar sb: sb.SetShortcuts(new[] { new Shortcut( Key.F1, "F1 - Edit Me", null ) }); break; - case RadioGroup rg: - rg.RadioLabels = new string[] { "Option 1", "Option 2" }; + case OptionSelector rg: + rg.Labels = new string[] { "Option 1", "Option 2" }; SetDefaultDimensions( newView, width ?? 10, height ?? 2 ); break; case GraphView gv: @@ -236,9 +239,6 @@ public static T Create(int? width = null, int? height = null, string? text = case Window: SetDefaultDimensions( newView, width ?? 10, height ?? 5 ); break; - case LineView: - SetDefaultDimensions( newView, width ?? 8, height ?? 1 ); - break; case TreeView: SetDefaultDimensions( newView, width ?? 16, height ?? 5 ); break; @@ -330,20 +330,16 @@ public static View Create( Type requestedType ) return requestedType switch { null => throw new ArgumentNullException( nameof( requestedType ) ), - { } t when t == typeof(TimeField) => Create(), - { } t when t == typeof( DateField ) => Create( ), { } t when t == typeof( Button ) => Create