diff --git a/Examples/CommunityToolkitExample/LoginView.cs b/Examples/CommunityToolkitExample/LoginView.cs index 8a2f0cf388..97aec5da1b 100644 --- a/Examples/CommunityToolkitExample/LoginView.cs +++ b/Examples/CommunityToolkitExample/LoginView.cs @@ -1,6 +1,7 @@ using CommunityToolkit.Mvvm.Messaging; using Terminal.Gui.App; using Terminal.Gui.ViewBase; +using Terminal.Gui.Input; namespace CommunityToolkitExample; @@ -9,7 +10,7 @@ internal partial class LoginView : IRecipient> public LoginView (LoginViewModel viewModel) { WeakReferenceMessenger.Default.Register (this); - Title = $"Community Toolkit MVVM Example - {Application.QuitKey} to Exit"; + Title = $"Community Toolkit MVVM Example - {Application.GetDefaultKey (Command.Quit)} to Exit"; ViewModel = viewModel; InitializeComponent (); usernameInput.TextChanged += (_, _) => diff --git a/Examples/Config/README.md b/Examples/Config/README.md new file mode 100644 index 0000000000..e22877dc3c --- /dev/null +++ b/Examples/Config/README.md @@ -0,0 +1,90 @@ +# Terminal.Gui Key Binding Config Examples + +This folder contains example `config.json` files that override Terminal.Gui's default +key bindings to match platform conventions. + +## How to Use + +Copy the desired file to `~/.tui/config.json` (the global Terminal.Gui config location). + +| OS | Want macOS feel? | Want Windows feel? | +|----|------------------|--------------------| +| **Windows** | Copy `macos.json` → `~/.tui/config.json` | (already default) | +| **macOS** | (already default) | Copy `windows.json` → `~/.tui/config.json` | + +On Windows `~` expands to `C:\Users\`. +On macOS/Linux `~` expands to `/home/` (or `/Users/` on macOS). + +## What Each File Changes + +### `macos.json` — macOS-style bindings (for Windows users) + +Overrides Terminal.Gui's default key bindings to match macOS conventions: + +| What changes | Default (Windows) | With `macos.json` | +|---|---|---| +| Quit app | `Esc` | `Esc` or `Ctrl+Q` | +| Suspend app to background | *(not available)* | `Ctrl+Z` | +| Undo | `Ctrl+Z` | `Ctrl+Z` or `Ctrl+/` | +| Redo | `Ctrl+Y` | `Ctrl+Y` or `Ctrl+Shift+Z` | +| Delete char right | `Delete` | `Delete` or `Ctrl+D` | + +Note: Emacs navigation shortcuts (`Ctrl+B`/`Ctrl+F` for left/right in text fields, +`Ctrl+N`/`Ctrl+P` for up/down in text views and lists) are already available on all +platforms — no override needed. + +### `windows.json` — Windows-style bindings (for macOS users) + +Overrides Terminal.Gui's default key bindings to match Windows conventions: + +| What changes | Default (macOS) | With `windows.json` | +|---|---|---| +| Quit app | `Esc` or `Ctrl+Q` | `Esc` only | +| Suspend app to background | `Ctrl+Z` | *(disabled)* | +| Undo | `Ctrl+Z` or `Ctrl+/` | `Ctrl+Z` only | +| Redo | `Ctrl+Y` or `Ctrl+Shift+Z` | `Ctrl+Y` only | +| Delete char right | `Delete` or `Ctrl+D` | `Delete` only | + +**Limitation:** Emacs navigation shortcuts built into text views (`Ctrl+B`, `Ctrl+F`, +`Ctrl+N`, `Ctrl+P`, `Ctrl+K`, etc.) are set in C# code and cannot be removed via +`config.json`. They remain available alongside the standard keys. + +## How It Works + +Terminal.Gui's `ConfigurationManager` loads `~/.tui/config.json` and uses it to +replace the values of three key binding properties: + +- **`Application.DefaultKeyBindings`** — app-level commands (Quit, Suspend, Tab navigation) +- **`View.DefaultKeyBindings`** — shared commands across all views (navigation, clipboard, editing) +- **`View.ViewKeyBindings`** — per-view overrides (keyed by view type name, e.g. `"TextField"`) + +The JSON format maps command names to `PlatformKeyBinding` objects: + +```json +{ + "Application.DefaultKeyBindings": { + "Quit": { "All": ["Esc", "Ctrl+Q"] } + }, + "View.DefaultKeyBindings": { + "Undo": { "All": ["Ctrl+Z"], "Linux": ["Ctrl+/"], "Macos": ["Ctrl+/"] } + }, + "View.ViewKeyBindings": { + "TextField": { + "WordLeft": { "All": ["Ctrl+CursorLeft"] } + } + } +} +``` + +Each `PlatformKeyBinding` has four optional fields: + +| Field | Applies to | +|-------|-----------| +| `All` | Every platform | +| `Windows` | Windows only (added to `All`) | +| `Linux` | Linux only (added to `All`) | +| `Macos` | macOS only (added to `All`) | + +**Important:** When you override a property (e.g. `View.DefaultKeyBindings`), your +JSON replaces the entire default dictionary. Any command you omit reverts to +having no binding from that layer. Always include all commands you want active. diff --git a/Examples/DemoFiles/example_config.json b/Examples/Config/example_config.json similarity index 88% rename from Examples/DemoFiles/example_config.json rename to Examples/Config/example_config.json index 09c6d4f24d..b47cb04a0d 100644 --- a/Examples/DemoFiles/example_config.json +++ b/Examples/Config/example_config.json @@ -1,27 +1,33 @@ { - "$schema": "https://gui-cs.github.io/Terminal.GuiV2Docs/schemas/tui-config-schema.json", + "$schema": "https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json", "ConfigurationManager.ThrowOnJsonErrors": false, - "Application.ArrangeKey": "Ctrl+F5", - "Application.Force16Colors": false, + "Application.DefaultKeyBindings": { + "Quit": { "All": ["Esc"] }, + "Suspend": { "Linux": ["Ctrl+Z"], "Macos": ["Ctrl+Z"] }, + "Arrange": { "All": ["Ctrl+F5"] }, + "NextTabStop": { "All": ["Tab"] }, + "PreviousTabStop": { "All": ["Shift+Tab"] }, + "NextTabGroup": { "All": ["F6"] }, + "PreviousTabGroup": { "All": ["Shift+F6"] }, + "Refresh": { "All": ["F5"] } + }, + "Driver.Force16Colors": false, "Application.ForceDriver": "", "Application.IsMouseDisabled": false, - "Application.NextTabGroupKey": "F6", - "Application.NextTabKey": "Tab", - "Application.PrevTabGroupKey": "Shift+F6", - "Application.PrevTabKey": "Shift+Tab", - "Application.QuitKey": "Esc", - "ContextMenu.DefaultKey": "Shift+F10", + "Key.Separator": "+", + "MenuBar.DefaultKey": "F10", + "PopoverMenu.DefaultKey": "Shift+F10", "FileDialog.MaxSearchResults": 10000, "FileDialogStyle.DefaultUseColors": false, "FileDialogStyle.DefaultUseUnicodeCharacters": false, - "Glyphs": { - "CheckStateChecked": "✓" - }, "Theme": "Gruntled", "Themes": [ { "Gruntled": { - "ColorSchemes": [ + "Glyphs": { + "CheckStateChecked": "✓" + }, + "Schemes": [ { "TopLevel": { "Normal": { diff --git a/Examples/Config/macos.json b/Examples/Config/macos.json new file mode 100644 index 0000000000..693dea0298 --- /dev/null +++ b/Examples/Config/macos.json @@ -0,0 +1,55 @@ +{ + "$schema": "https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json", + + "Application.DefaultKeyBindings": { + "Quit": { "All": ["Esc", "Ctrl+Q"] }, + "Suspend": { "All": ["Ctrl+Z"] }, + "Arrange": { "All": ["Ctrl+F5"] }, + "NextTabStop": { "All": ["Tab"] }, + "PreviousTabStop": { "All": ["Shift+Tab"] }, + "NextTabGroup": { "All": ["F6"] }, + "PreviousTabGroup":{ "All": ["Shift+F6"] }, + "Refresh": { "All": ["F5"] } + }, + + "View.DefaultKeyBindings": { + "Left": { "All": ["CursorLeft"] }, + "Right": { "All": ["CursorRight"] }, + "Up": { "All": ["CursorUp"] }, + "Down": { "All": ["CursorDown"] }, + "PageUp": { "All": ["PageUp"] }, + "PageDown": { "All": ["PageDown"] }, + "LeftStart": { "All": ["Home"] }, + "RightEnd": { "All": ["End"] }, + "Start": { "All": ["Ctrl+Home"] }, + "End": { "All": ["Ctrl+End"] }, + + "LeftExtend": { "All": ["Shift+CursorLeft"] }, + "RightExtend": { "All": ["Shift+CursorRight"] }, + "UpExtend": { "All": ["Shift+CursorUp"] }, + "DownExtend": { "All": ["Shift+CursorDown"] }, + "PageUpExtend": { "All": ["Shift+PageUp"] }, + "PageDownExtend": { "All": ["Shift+PageDown"] }, + "LeftStartExtend": { "All": ["Shift+Home"] }, + "RightEndExtend": { "All": ["Shift+End"] }, + "StartExtend": { "All": ["Ctrl+Shift+Home"] }, + "EndExtend": { "All": ["Ctrl+Shift+End"] }, + + "Copy": { "All": ["Ctrl+C"] }, + "Cut": { "All": ["Ctrl+X"] }, + "Paste": { "All": ["Ctrl+V"] }, + + "Undo": { "All": ["Ctrl+Z", "Ctrl+/"] }, + "Redo": { "All": ["Ctrl+Y", "Ctrl+Shift+Z"] }, + "SelectAll": { "All": ["Ctrl+A"] }, + "DeleteCharLeft": { "All": ["Backspace"] }, + "DeleteCharRight": { "All": ["Delete", "Ctrl+D"] } + }, + + "View.ViewKeyBindings": { + "TextField": { + "CutToEndOfLine": { "All": ["Ctrl+K"] }, + "KillWordRight": { "All": ["Ctrl+W"] } + } + } +} diff --git a/Examples/Config/windows.json b/Examples/Config/windows.json new file mode 100644 index 0000000000..b4d0085e70 --- /dev/null +++ b/Examples/Config/windows.json @@ -0,0 +1,56 @@ +{ + "$schema": "https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json", + + "Application.DefaultKeyBindings": { + "Quit": { "All": ["Esc"] }, + "Arrange": { "All": ["Ctrl+F5"] }, + "NextTabStop": { "All": ["Tab"] }, + "PreviousTabStop": { "All": ["Shift+Tab"] }, + "NextTabGroup": { "All": ["F6"] }, + "PreviousTabGroup":{ "All": ["Shift+F6"] }, + "Refresh": { "All": ["F5"] } + }, + + "View.DefaultKeyBindings": { + "Left": { "All": ["CursorLeft"] }, + "Right": { "All": ["CursorRight"] }, + "Up": { "All": ["CursorUp"] }, + "Down": { "All": ["CursorDown"] }, + "PageUp": { "All": ["PageUp"] }, + "PageDown": { "All": ["PageDown"] }, + "LeftStart": { "All": ["Home"] }, + "RightEnd": { "All": ["End"] }, + "Start": { "All": ["Ctrl+Home"] }, + "End": { "All": ["Ctrl+End"] }, + + "LeftExtend": { "All": ["Shift+CursorLeft"] }, + "RightExtend": { "All": ["Shift+CursorRight"] }, + "UpExtend": { "All": ["Shift+CursorUp"] }, + "DownExtend": { "All": ["Shift+CursorDown"] }, + "PageUpExtend": { "All": ["Shift+PageUp"] }, + "PageDownExtend": { "All": ["Shift+PageDown"] }, + "LeftStartExtend": { "All": ["Shift+Home"] }, + "RightEndExtend": { "All": ["Shift+End"] }, + "StartExtend": { "All": ["Ctrl+Shift+Home"] }, + "EndExtend": { "All": ["Ctrl+Shift+End"] }, + + "Copy": { "All": ["Ctrl+C"] }, + "Cut": { "All": ["Ctrl+X"] }, + "Paste": { "All": ["Ctrl+V"] }, + + "Undo": { "All": ["Ctrl+Z"] }, + "Redo": { "All": ["Ctrl+Y"] }, + "SelectAll": { "All": ["Ctrl+A"] }, + "DeleteCharLeft": { "All": ["Backspace"] }, + "DeleteCharRight": { "All": ["Delete"] } + }, + + "View.ViewKeyBindings": { + "TextField": { + "WordLeft": { "All": ["Ctrl+CursorLeft"] }, + "WordRight": { "All": ["Ctrl+CursorRight"] }, + "WordLeftExtend": { "All": ["Ctrl+Shift+CursorLeft"] }, + "WordRightExtend":{ "All": ["Ctrl+Shift+CursorRight"] } + } + } +} diff --git a/Examples/Example/Example.cs b/Examples/Example/Example.cs index 763557b59a..e7ea27671c 100644 --- a/Examples/Example/Example.cs +++ b/Examples/Example/Example.cs @@ -7,6 +7,7 @@ using Terminal.Gui.Configuration; using Terminal.Gui.ViewBase; using Terminal.Gui.Views; +using Terminal.Gui.Input; // Override the default configuration for the application to use the Amber Phosphor theme ConfigurationManager.RuntimeConfig = """{ "Theme": "Amber Phosphor" }"""; @@ -32,7 +33,7 @@ public sealed class ExampleWindow : Runnable { public ExampleWindow () { - Title = $"Example App ({Application.QuitKey} to quit)"; + Title = $"Example App ({Application.GetDefaultKey (Command.Quit)} to quit)"; // Create input components and labels var usernameLabel = new Label { Text = "Username:" }; diff --git a/Examples/FSharpExample/Program.fs b/Examples/FSharpExample/Program.fs index 9859ce16fd..716f444faa 100644 --- a/Examples/FSharpExample/Program.fs +++ b/Examples/FSharpExample/Program.fs @@ -4,7 +4,7 @@ type ExampleWindow() as this = inherit Window() do - this.Title <- sprintf "Example App (%O to quit)" Application.QuitKey + this.Title <- sprintf "Example App (%O to quit)" (Application.GetDefaultKey (Command.Quit)) // Create input components and labels let usernameLabel = new Label(Text = "Username:") diff --git a/Examples/NativeAot/Program.cs b/Examples/NativeAot/Program.cs index 2f203aceea..bd1deee28e 100644 --- a/Examples/NativeAot/Program.cs +++ b/Examples/NativeAot/Program.cs @@ -7,6 +7,7 @@ using Terminal.Gui.Views; using Terminal.Gui.App; using Terminal.Gui.ViewBase; +using Terminal.Gui.Input; namespace NativeAot; @@ -63,7 +64,7 @@ public class ExampleWindow : Window public ExampleWindow () { - Title = $"Example App ({Application.QuitKey} to quit)"; + Title = $"Example App ({Application.GetDefaultKey (Command.Quit)} to quit)"; // Create input components and labels var usernameLabel = new Label { Text = "Username:" }; diff --git a/Examples/ReactiveExample/LoginView.cs b/Examples/ReactiveExample/LoginView.cs index 4090d2694d..9cceecde66 100644 --- a/Examples/ReactiveExample/LoginView.cs +++ b/Examples/ReactiveExample/LoginView.cs @@ -1,4 +1,4 @@ -using System.Reactive.Disposables; +using System.Reactive.Disposables; using System.Reactive.Disposables.Fluent; using System.Reactive.Linq; using ReactiveMarbles.ObservableEvents; @@ -7,6 +7,7 @@ using Terminal.Gui.Views; using Terminal.Gui.App; using Terminal.Gui.ViewBase; +using Terminal.Gui.Input; namespace ReactiveExample; @@ -21,7 +22,7 @@ public class LoginView : Window, IViewFor public LoginView (LoginViewModel viewModel) { - Title = $"Reactive Extensions Example - {Application.QuitKey} to Exit"; + Title = $"Reactive Extensions Example - {Application.GetDefaultKey (Command.Quit)} to Exit"; ViewModel = viewModel; var title = this.AddControl