diff --git a/Documentation/Reference/README.md b/Documentation/Reference/README.md index 3bf0af7..b54e66b 100644 --- a/Documentation/Reference/README.md +++ b/Documentation/Reference/README.md @@ -3,6 +3,8 @@ ## Protocols - [App](protocols/App.md) +- [MenuItem](protocols/MenuItem.md) +- [MenuItemGroup](protocols/MenuItemGroup.md) - [StateProtocol](protocols/StateProtocol.md) - [View](protocols/View.md) - [Widget](protocols/Widget.md) @@ -11,7 +13,6 @@ ## Structs -- [ApplicationWindow](structs/ApplicationWindow.md) - [Binding](structs/Binding.md) - [Button](structs/Button.md) - [Clamp](structs/Clamp.md) @@ -20,11 +21,15 @@ - [HeaderBar](structs/HeaderBar.md) - [InspectorWrapper](structs/InspectorWrapper.md) - [List](structs/List.md) +- [Menu](structs/Menu.md) +- [MenuButton](structs/MenuButton.md) +- [MenuSection](structs/MenuSection.md) - [NavigationSplitView](structs/NavigationSplitView.md) - [ScrollView](structs/ScrollView.md) - [State](structs/State.md) - [StateWrapper](structs/StateWrapper.md) - [StatusPage](structs/StatusPage.md) +- [Submenu](structs/Submenu.md) - [Text](structs/Text.md) - [ToolbarView](structs/ToolbarView.md) - [UpdateObserver](structs/UpdateObserver.md) @@ -49,6 +54,8 @@ - [App](extensions/App.md) - [Array](extensions/Array.md) +- [MenuItem](extensions/MenuItem.md) +- [MenuItemGroup](extensions/MenuItemGroup.md) - [NativeWidgetPeer](extensions/NativeWidgetPeer.md) - [String](extensions/String.md) - [View](extensions/View.md) @@ -61,6 +68,8 @@ - [Body](typealiases/Body.md) - [GTUIApplicationWindow](typealiases/GTUIApplicationWindow.md) - [GTUIWindow](typealiases/GTUIWindow.md) +- [MenuBuilder](typealiases/MenuBuilder.md) +- [MenuContent](typealiases/MenuContent.md) - [Scene](typealiases/Scene.md) - [SceneBuilder](typealiases/SceneBuilder.md) diff --git a/Documentation/Reference/extensions/MenuItem.md b/Documentation/Reference/extensions/MenuItem.md new file mode 100644 index 0000000..81dfbdb --- /dev/null +++ b/Documentation/Reference/extensions/MenuItem.md @@ -0,0 +1,8 @@ +**EXTENSION** + +# `MenuItem` + +## Properties +### `content` + +The menu item's content is itself. diff --git a/Documentation/Reference/extensions/MenuItemGroup.md b/Documentation/Reference/extensions/MenuItemGroup.md new file mode 100644 index 0000000..435e04e --- /dev/null +++ b/Documentation/Reference/extensions/MenuItemGroup.md @@ -0,0 +1,9 @@ +**EXTENSION** + +# `MenuItemGroup` + +## Methods +### `addMenuItems(menu:app:window:)` + +Add the menu items described by the group to a menu. +- Parameter menu: The menu. diff --git a/Documentation/Reference/protocols/MenuItem.md b/Documentation/Reference/protocols/MenuItem.md new file mode 100644 index 0000000..c58eefa --- /dev/null +++ b/Documentation/Reference/protocols/MenuItem.md @@ -0,0 +1,14 @@ +**PROTOCOL** + +# `MenuItem` + +A structure representing the content for a certain menu item type. + +## Methods +### `addMenuItem(menu:app:window:)` + +Add the menu item to a certain menu. +- Parameters: + - menu: The menu. + - app: The application containing the menu. + - window: The application window containing the menu. diff --git a/Documentation/Reference/protocols/MenuItemGroup.md b/Documentation/Reference/protocols/MenuItemGroup.md new file mode 100644 index 0000000..eda9fe4 --- /dev/null +++ b/Documentation/Reference/protocols/MenuItemGroup.md @@ -0,0 +1,10 @@ +**PROTOCOL** + +# `MenuItemGroup` + +A structure conforming to `MenuItemGroup` can be added to the content accepting a menu. + +## Properties +### `content` + +The menu's content. diff --git a/Documentation/Reference/structs/Menu.md b/Documentation/Reference/structs/Menu.md new file mode 100644 index 0000000..6fddd0b --- /dev/null +++ b/Documentation/Reference/structs/Menu.md @@ -0,0 +1,56 @@ +**STRUCT** + +# `Menu` + +A menu button widget. + +## Properties +### `label` + +The button's label. + +### `icon` + +The button's icon. + +### `content` + +The menu's content. + +### `app` + +The application. + +### `window` + +The window. + +## Methods +### `init(_:icon:app:window:content:)` + +Initialize a menu button. +- Parameters: + - label: The button's label. + - icon: The button's icon. + - app: The application. + - window: The application window. + - content: The menu's content. + +### `init(_:app:window:content:)` + +Initialize a menu button. +- Parameters: + - label: The buttons label. + - app: The application. + - window: The application window. + - content: The menu's content. + +### `update(_:)` + +Update a button's view storage. +- Parameter storage: The view storage. + +### `container()` + +Get a button's view storage. +- Returns: The button's view storage. diff --git a/Documentation/Reference/structs/MenuButton.md b/Documentation/Reference/structs/MenuButton.md new file mode 100644 index 0000000..7cbe9db --- /dev/null +++ b/Documentation/Reference/structs/MenuButton.md @@ -0,0 +1,48 @@ +**STRUCT** + +# `MenuButton` + +A button widget for menus. + +## Properties +### `label` + +The button's label. + +### `handler` + +The button's action handler. + +### `shortcut` + +The keyboard shortcut. + +### `preferApplicationWindow` + +Whether to prefer adding the action to the application window. + +## Methods +### `init(_:window:handler:)` + +Initialize a menu button. +- Parameters: + - label: The buttons label. + - window: Whether to prefer adding the action to the application window. + - handler: The button's action handler. + +### `addMenuItem(menu:app:window:)` + +Add the button to a menu. +- Parameters: + - menu: The menu. + - app: The application containing the menu. + - window: The application window containing the menu. + +### `keyboardShortcut(_:)` + +Create a keyboard shortcut for an application from a button. + +Note that the keyboard shortcut is available after the view has been visible for the first time. +- Parameters: + - shortcut: The keyboard shortcut. +- Returns: The button. diff --git a/Documentation/Reference/structs/MenuSection.md b/Documentation/Reference/structs/MenuSection.md new file mode 100644 index 0000000..73e143b --- /dev/null +++ b/Documentation/Reference/structs/MenuSection.md @@ -0,0 +1,24 @@ +**STRUCT** + +# `MenuSection` + +A section for menus. + +## Properties +### `sectionContent` + +The content of the section. + +## Methods +### `init(content:)` + +Initialize a section for menus. +- Parameter content: The content of the section. + +### `addMenuItem(menu:app:window:)` + +Add the section to a menu. +- Parameters: + - menu: The menu. + - app: The application containing the menu. + - window: The application window containing the menu. diff --git a/Documentation/Reference/structs/Submenu.md b/Documentation/Reference/structs/Submenu.md new file mode 100644 index 0000000..19e302a --- /dev/null +++ b/Documentation/Reference/structs/Submenu.md @@ -0,0 +1,30 @@ +**STRUCT** + +# `Submenu` + +A submenu widget. + +## Properties +### `label` + +The submenu's label. + +### `submenuContent` + +The content of the submenu. + +## Methods +### `init(_:content:)` + +Initialize a submenu. +- Parameters: + - label: The submenu's label. + - content: The content of the submenu. + +### `addMenuItem(menu:app:window:)` + +Add the submenu to a menu. +- Parameters: + - menu: The menu. + - app: The application containing the menu. + - window: The application window containing the menu. diff --git a/Documentation/Reference/structs/Window.md b/Documentation/Reference/structs/Window.md index 8003790..0a56a26 100644 --- a/Documentation/Reference/structs/Window.md +++ b/Documentation/Reference/structs/Window.md @@ -2,7 +2,7 @@ # `Window` -A structure representing a simple window type. +A structure representing an application window type. Note that multiple instances of a window can be opened at the same time. @@ -19,6 +19,10 @@ The window's content. Whether an instance of the window type should be opened when the app is starting up. +### `shortcuts` + +The keyboard shortcuts. + ### `appShortcuts` The keyboard shortcuts on the app level. @@ -53,6 +57,22 @@ Get the storage of the content view. ### `update(_:app:)` Update a window storage's content. +- Parameter storage: The storage to update. + +### `keyboardShortcut(_:action:)` + +Add a keyboard shortcut. - Parameters: - - storage: The storage to update. - - app: The application. + - shortcut: The keyboard shortcut. + - action: The closure to execute when the keyboard shortcut is pressed. +- Returns: The window. + +### `updateShortcuts(window:)` + +Update the keyboard shortcuts. +- Parameter window: The application window. + +### `closeShortcut()` + +Add the shortcut "w" which closes the window. +- Returns: The window. diff --git a/Documentation/Reference/typealiases/MenuBuilder.md b/Documentation/Reference/typealiases/MenuBuilder.md new file mode 100644 index 0000000..566c55e --- /dev/null +++ b/Documentation/Reference/typealiases/MenuBuilder.md @@ -0,0 +1,5 @@ +**TYPEALIAS** + +# `MenuBuilder` + +A builder for the `MenuContent` \ No newline at end of file diff --git a/Documentation/Reference/typealiases/MenuContent.md b/Documentation/Reference/typealiases/MenuContent.md new file mode 100644 index 0000000..98f7574 --- /dev/null +++ b/Documentation/Reference/typealiases/MenuContent.md @@ -0,0 +1,5 @@ +**TYPEALIAS** + +# `MenuContent` + +`MenuContent` is an array of menu item groups. \ No newline at end of file diff --git a/Icons/Demo.png b/Icons/Demo.png index eb77fb1..32f8e1c 100644 Binary files a/Icons/Demo.png and b/Icons/Demo.png differ diff --git a/README.md b/README.md index f68b89a..430953e 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ If you want to use _Adwaita_ in a project, but there are widgets missing, open a | VStack | A widget which arranges child widgets into a single column. | GtkBox | | HStack | A widget which arranges child widgets into a single row. | GtkBox | | List | A widget which arranges child widgets vertically into rows. | GtkListBox | +| Menu | A widget showing a button that toggles the appearance of a menu. | GtkMenuButton | | NavigationSplitView | A widget presenting sidebar and content side by side. | AdwNavigationSplitView | | ScrollView | A container that makes its child scrollable. | GtkScrolledWindow | | StatusPage | A page with an icon, title, and optionally description and widget.| AdwStatusPage | @@ -134,7 +135,19 @@ If you want to use _Adwaita_ in a project, but there are widgets missing, open a | Syntax | Description | | ------------------------------- | --------------------------------------------------------------------------------------- | | `keyboardShortcut(_:action:)` | Create a keyboard shortcut available in one window. | -| `closeShortcut()` | Create a keyboard shortcut for closing the window with "Ctrl + w". | +| `closeShortcut()` | Create a keyboard shortcut for closing the window with "Ctrl + w". + +### Menu Widgets +| Name | Description | Widget | +| -------------------- | ----------------------------------------------------------------- | ---------------------- | +| MenuButton | A button in a menu. | GMenuItem | +| MenuSection | A collection of menu widgets grouped with lines. | GMenuItem | +| Submenu | A collection of menu widgets grouped by navigation. | GMenuItem | + +### `MenuButton` Modifiers +| Syntax | Description | +| ------------------------------- | --------------------------------------------------------------------------------------- | +| `keyboardShortcut(_:)` | Assign a keyboard shortcut to the button's action. | ## Installation ### Dependencies diff --git a/Sources/Adwaita/Menu/MenuButton.swift b/Sources/Adwaita/Menu/MenuButton.swift new file mode 100644 index 0000000..4be9920 --- /dev/null +++ b/Sources/Adwaita/Menu/MenuButton.swift @@ -0,0 +1,58 @@ +// +// MenuButton.swift +// Adwaita +// +// Created by david-swift on 22.10.23. +// + +import GTUI + +/// A button widget for menus. +public struct MenuButton: MenuItem { + + /// The button's label. + var label: String + /// The button's action handler. + var handler: () -> Void + /// The keyboard shortcut. + var shortcut = "" + /// Whether to prefer adding the action to the application window. + var preferApplicationWindow: Bool + + /// Initialize a menu button. + /// - Parameters: + /// - label: The buttons label. + /// - window: Whether to prefer adding the action to the application window. + /// - handler: The button's action handler. + public init(_ label: String, window: Bool = true, handler: @escaping () -> Void) { + self.label = label + preferApplicationWindow = window + self.handler = handler + } + + /// Add the button to a menu. + /// - Parameters: + /// - menu: The menu. + /// - app: The application containing the menu. + /// - window: The application window containing the menu. + public func addMenuItem(menu: GTUI.Menu, app: GTUIApp, window: GTUIApplicationWindow?) { + if let window, preferApplicationWindow { + _ = menu.append(label, window: window, shortcut: shortcut, handler: handler) + } else { + _ = menu.append(label, app: app, shortcut: shortcut, handler: handler) + } + } + + /// Create a keyboard shortcut for an application from a button. + /// + /// Note that the keyboard shortcut is available after the view has been visible for the first time. + /// - Parameters: + /// - shortcut: The keyboard shortcut. + /// - Returns: The button. + public func keyboardShortcut(_ shortcut: String) -> Self { + var newSelf = self + newSelf.shortcut = shortcut + return newSelf + } + +} diff --git a/Sources/Adwaita/Menu/MenuSection.swift b/Sources/Adwaita/Menu/MenuSection.swift new file mode 100644 index 0000000..9f0309f --- /dev/null +++ b/Sources/Adwaita/Menu/MenuSection.swift @@ -0,0 +1,35 @@ +// +// Submenu.swift +// Adwaita +// +// Created by david-swift on 22.10.23. +// + +import GTUI + +/// A section for menus. +public struct MenuSection: MenuItem { + + /// The content of the section. + var sectionContent: MenuContent + + /// Initialize a section for menus. + /// - Parameter content: The content of the section. + public init(@MenuBuilder content: () -> MenuContent) { + self.sectionContent = content() + } + + /// Add the section to a menu. + /// - Parameters: + /// - menu: The menu. + /// - app: The application containing the menu. + /// - window: The application window containing the menu. + public func addMenuItem(menu: GTUI.Menu, app: GTUIApp, window: GTUIApplicationWindow?) { + let section = GTUI.Menu() + _ = menu.append("", section: section) + for element in sectionContent { + element.addMenuItems(menu: section, app: app, window: window) + } + } + +} diff --git a/Sources/Adwaita/Menu/Submenu.swift b/Sources/Adwaita/Menu/Submenu.swift new file mode 100644 index 0000000..c125995 --- /dev/null +++ b/Sources/Adwaita/Menu/Submenu.swift @@ -0,0 +1,40 @@ +// +// Submenu.swift +// Adwaita +// +// Created by david-swift on 22.10.23. +// + +import GTUI + +/// A submenu widget. +public struct Submenu: MenuItem { + + /// The submenu's label. + var label: String + /// The content of the submenu. + var submenuContent: MenuContent + + /// Initialize a submenu. + /// - Parameters: + /// - label: The submenu's label. + /// - content: The content of the submenu. + public init(_ label: String, @MenuBuilder content: () -> MenuContent) { + self.label = label + self.submenuContent = content() + } + + /// Add the submenu to a menu. + /// - Parameters: + /// - menu: The menu. + /// - app: The application containing the menu. + /// - window: The application window containing the menu. + public func addMenuItem(menu: GTUI.Menu, app: GTUIApp, window: GTUIApplicationWindow?) { + let submenu = GTUI.Menu() + _ = menu.append(label, submenu: submenu) + for element in submenuContent { + element.addMenuItems(menu: submenu, app: app, window: window) + } + } + +} diff --git a/Sources/Adwaita/Model/User Interface/App.swift b/Sources/Adwaita/Model/User Interface/App/App.swift similarity index 100% rename from Sources/Adwaita/Model/User Interface/App.swift rename to Sources/Adwaita/Model/User Interface/App/App.swift diff --git a/Sources/Adwaita/Model/User Interface/GTUIApp.swift b/Sources/Adwaita/Model/User Interface/App/GTUIApp.swift similarity index 100% rename from Sources/Adwaita/Model/User Interface/GTUIApp.swift rename to Sources/Adwaita/Model/User Interface/App/GTUIApp.swift diff --git a/Sources/Adwaita/Model/User Interface/Menu/MenuItem.swift b/Sources/Adwaita/Model/User Interface/Menu/MenuItem.swift new file mode 100644 index 0000000..f5a112d --- /dev/null +++ b/Sources/Adwaita/Model/User Interface/Menu/MenuItem.swift @@ -0,0 +1,27 @@ +// +// MenuItem.swift +// Adwaita +// +// Created by david-swift on 22.10.23. +// + +import GTUI + +/// A structure representing the content for a certain menu item type. +public protocol MenuItem: MenuItemGroup { + + /// Add the menu item to a certain menu. + /// - Parameters: + /// - menu: The menu. + /// - app: The application containing the menu. + /// - window: The application window containing the menu. + func addMenuItem(menu: GTUI.Menu, app: GTUIApp, window: GTUIApplicationWindow?) + +} + +extension MenuItem { + + /// The menu item's content is itself. + @MenuBuilder public var content: MenuContent { self } + +} diff --git a/Sources/Adwaita/Model/User Interface/Menu/MenuItemGroup.swift b/Sources/Adwaita/Model/User Interface/Menu/MenuItemGroup.swift new file mode 100644 index 0000000..0bc47ad --- /dev/null +++ b/Sources/Adwaita/Model/User Interface/Menu/MenuItemGroup.swift @@ -0,0 +1,37 @@ +// +// MenuItemGroup.swift +// Adwaita +// +// Created by david-swift on 22.10.23. +// + +import GTUI + +/// A structure conforming to `MenuItemGroup` can be added to the content accepting a menu. +public protocol MenuItemGroup { + + /// The menu's content. + @MenuBuilder var content: MenuContent { get } + +} + +extension MenuItemGroup { + + /// Add the menu items described by the group to a menu. + /// - Parameter menu: The menu. + func addMenuItems(menu: GTUI.Menu, app: GTUIApp, window: GTUIApplicationWindow?) { + for element in content { + if let item = element as? MenuItem { + item.addMenuItem(menu: menu, app: app, window: window) + } else { + element.addMenuItems(menu: menu, app: app, window: window) + } + } + } + +} + +/// `MenuContent` is an array of menu item groups. +public typealias MenuContent = [MenuItemGroup] +/// A builder for the `MenuContent` +public typealias MenuBuilder = ArrayBuilder diff --git a/Sources/Adwaita/Model/User Interface/View.swift b/Sources/Adwaita/Model/User Interface/View/View.swift similarity index 100% rename from Sources/Adwaita/Model/User Interface/View.swift rename to Sources/Adwaita/Model/User Interface/View/View.swift diff --git a/Sources/Adwaita/Model/User Interface/ViewBuilder.swift b/Sources/Adwaita/Model/User Interface/View/ViewBuilder.swift similarity index 100% rename from Sources/Adwaita/Model/User Interface/ViewBuilder.swift rename to Sources/Adwaita/Model/User Interface/View/ViewBuilder.swift diff --git a/Sources/Adwaita/Model/User Interface/ViewStorage.swift b/Sources/Adwaita/Model/User Interface/View/ViewStorage.swift similarity index 100% rename from Sources/Adwaita/Model/User Interface/ViewStorage.swift rename to Sources/Adwaita/Model/User Interface/View/ViewStorage.swift diff --git a/Sources/Adwaita/Model/User Interface/Widget.swift b/Sources/Adwaita/Model/User Interface/View/Widget.swift similarity index 100% rename from Sources/Adwaita/Model/User Interface/Widget.swift rename to Sources/Adwaita/Model/User Interface/View/Widget.swift diff --git a/Sources/Adwaita/Model/User Interface/GTUIApplicationWindow.swift b/Sources/Adwaita/Model/User Interface/Window/GTUIApplicationWindow.swift similarity index 100% rename from Sources/Adwaita/Model/User Interface/GTUIApplicationWindow.swift rename to Sources/Adwaita/Model/User Interface/Window/GTUIApplicationWindow.swift diff --git a/Sources/Adwaita/Model/User Interface/GTUIWindow.swift b/Sources/Adwaita/Model/User Interface/Window/GTUIWindow.swift similarity index 100% rename from Sources/Adwaita/Model/User Interface/GTUIWindow.swift rename to Sources/Adwaita/Model/User Interface/Window/GTUIWindow.swift diff --git a/Sources/Adwaita/Model/User Interface/WindowScene.swift b/Sources/Adwaita/Model/User Interface/Window/WindowScene.swift similarity index 100% rename from Sources/Adwaita/Model/User Interface/WindowScene.swift rename to Sources/Adwaita/Model/User Interface/Window/WindowScene.swift diff --git a/Sources/Adwaita/Model/User Interface/WindowSceneGroup.swift b/Sources/Adwaita/Model/User Interface/Window/WindowSceneGroup.swift similarity index 100% rename from Sources/Adwaita/Model/User Interface/WindowSceneGroup.swift rename to Sources/Adwaita/Model/User Interface/Window/WindowSceneGroup.swift diff --git a/Sources/Adwaita/Model/User Interface/WindowStorage.swift b/Sources/Adwaita/Model/User Interface/Window/WindowStorage.swift similarity index 100% rename from Sources/Adwaita/Model/User Interface/WindowStorage.swift rename to Sources/Adwaita/Model/User Interface/Window/WindowStorage.swift diff --git a/Sources/Adwaita/View/Menu.swift b/Sources/Adwaita/View/Menu.swift new file mode 100644 index 0000000..6fbb504 --- /dev/null +++ b/Sources/Adwaita/View/Menu.swift @@ -0,0 +1,98 @@ +// +// Menu.swift +// Adwaita +// +// Created by david-swift on 21.10.23. +// + +import GTUI + +/// A menu button widget. +public struct Menu: Widget { + + /// The button's label. + var label: String? + /// The button's icon. + var icon: Icon? + /// The menu's content. + var content: MenuContent + /// The application. + var app: GTUIApp + /// The window. + var window: GTUIApplicationWindow? + + // swiftlint:disable function_default_parameter_at_end + /// Initialize a menu button. + /// - Parameters: + /// - label: The button's label. + /// - icon: The button's icon. + /// - app: The application. + /// - window: The application window. + /// - content: The menu's content. + public init( + _ label: String? = nil, + icon: Icon, + app: GTUIApp, + window: GTUIApplicationWindow?, + @MenuBuilder content: () -> MenuContent + ) { + self.label = label + self.icon = icon + self.app = app + self.window = window + self.content = content() + } + // swiftlint:enable function_default_parameter_at_end + + /// Initialize a menu button. + /// - Parameters: + /// - label: The buttons label. + /// - app: The application. + /// - window: The application window. + /// - content: The menu's content. + public init( + _ label: String, + app: GTUIApp, + window: GTUIApplicationWindow?, + @MenuBuilder content: () -> MenuContent + ) { + self.label = label + self.app = app + self.window = window + self.content = content() + } + + /// Update a button's view storage. + /// - Parameter storage: The view storage. + public func update(_ storage: ViewStorage) { + if let button = storage.view as? GTUI.MenuButton { + let content = button.getContent() + if let label { + if icon == nil { + button.setLabel(label) + } else { + content?.setLabel(label) + } + } + if let icon { + content?.setIcon(icon) + } + } + } + + /// Get a button's view storage. + /// - Returns: The button's view storage. + public func container() -> ViewStorage { + let button: GTUI.MenuButton + if let icon { + button = .init(label, icon: icon) + } else { + button = .init(label ?? .init()) + } + for element in content { + element.addMenuItems(menu: button.getMenu(), app: app, window: window) + } + return .init(button) + } + +} diff --git a/Tests/Demo.swift b/Tests/Demo.swift index 1847d30..b0c9deb 100644 --- a/Tests/Demo.swift +++ b/Tests/Demo.swift @@ -20,9 +20,6 @@ struct Demo: App { Window(id: "main") { window in DemoContent(window: window, app: app) } - .appKeyboardShortcut("n".ctrl()) { $0.addWindow("main") } - .closeShortcut() - .quitShortcut() HelperWindows() } @@ -44,7 +41,7 @@ struct Demo: App { struct DemoContent: View { @State private var selection: Page = .welcome - var window: GTUIWindow + var window: GTUIApplicationWindow var app: GTUIApp! var view: Body { @@ -58,7 +55,24 @@ struct Demo: App { .sidebarStyle() } .topToolbar { - HeaderBar.empty() + HeaderBar.end { + Menu(icon: .default(icon: .openMenu), app: app, window: window) { + MenuButton("New Window", window: false) { + app.addWindow("main") + } + .keyboardShortcut("n".ctrl()) + MenuButton("Close Window") { + window.close() + } + .keyboardShortcut("w".ctrl()) + MenuSection { + MenuButton("Quit", window: false) { + app.quit() + } + .keyboardShortcut("q".ctrl()) + } + } + } } .navigationTitle("Demo") } content: { diff --git a/user-manual/Basics/KeyboardShortcuts.md b/user-manual/Basics/KeyboardShortcuts.md index 99c6069..c92bfca 100644 --- a/user-manual/Basics/KeyboardShortcuts.md +++ b/user-manual/Basics/KeyboardShortcuts.md @@ -70,6 +70,29 @@ struct HelloWorld: App { } ``` +## Create Shortcuts from a Menu +The most elegant way for adding keyboard shortcuts is in many cases adding them via menus. +Here is an example using a menu button: +```swift +struct TestView: View { + + var app: GTUIApp + + var view: Body { + Menu(icon: .default(icon: .openMenu), app: app) { + MenuButton("New Window", window: false) { + app.addWindow("main") + } + // Add a keyboard shortcut to the app. + .keyboardShortcut("n".ctrl()) + } + } + +} +``` +Add the keyboard shortcut to a single window by specifying the `window` parameter in the initializer of `Menu`, +and removing `window: false` in the initializer of `MenuButton`. + ## Create Shortcuts from a Button It's possible to easily create a keyboard shortcut from a button. Use `appKeyboardShortcut` instead of `keyboardShortcut` for shortcuts on an application level.