From d8de6115109748b428585e8000136930178b0a22 Mon Sep 17 00:00:00 2001
From: david-swift
Date: Mon, 22 Jan 2024 21:41:21 +0100
Subject: [PATCH] Add support for auto-generated widget bindings
Additionally fix an update problem occurring with custom views
---
.github/ISSUE_TEMPLATE/component_request.yml | 8 -
.swiftlint.yml | 3 +
CONTRIBUTING.md | 2 +
LICENSE.md | 696 +---------------
Package.swift | 22 +-
README.md | 10 +-
SUMMARY.md | 1 +
Sources/Adwaita/Menu/MenuButton.swift | 13 +-
Sources/Adwaita/Menu/MenuSection.swift | 8 +-
Sources/Adwaita/Menu/Submenu.swift | 8 +-
Sources/Adwaita/Model/ArrayBuilder.swift | 2 -
Sources/Adwaita/Model/Data Flow/State.swift | 18 +-
.../Model/Enumerations/Alignment.swift | 44 +
Sources/Adwaita/Model/Enumerations/Edge.swift | 20 +
Sources/Adwaita/Model/Enumerations/Icon.swift | 767 ++++++++++++++++++
.../Model/Enumerations/Transition.swift | 75 ++
Sources/Adwaita/Model/Extensions/Array.swift | 26 +-
Sources/Adwaita/Model/Extensions/Bool.swift | 15 +
Sources/Adwaita/Model/Extensions/Int.swift | 17 +
.../Extensions/Libadwaita.FileDialog.swift | 74 --
.../Model/Extensions/NativeWidgetPeer.swift | 37 -
.../Model/Extensions/OpaquePointer.swift | 16 +
Sources/Adwaita/Model/Extensions/Set.swift | 35 +
Sources/Adwaita/Model/Extensions/String.swift | 24 +
Sources/Adwaita/Model/Extensions/UInt.swift | 15 +
.../Extensions/UnsafeMutablePointer.swift | 23 +
.../Extensions/UnsafeMutableRawPointer.swift | 17 +
.../Model/User Interface/App/App.swift | 2 -
.../Model/User Interface/App/GTUIApp.swift | 66 +-
.../Model/User Interface/Menu/MenuItem.swift | 4 +-
.../User Interface/Menu/MenuItemGroup.swift | 4 +-
.../Model/User Interface/View/View.swift | 22 +-
.../User Interface/View/ViewStorage.swift | 175 +++-
.../Model/User Interface/View/Widget.swift | 2 -
.../Window/GTUIAboutWindow.swift | 49 ++
.../Window/GTUIApplicationWindow.swift | 32 +-
.../Window/GTUIFileDialog.swift | 138 ++++
.../User Interface/Window/GTUIWindow.swift | 82 +-
.../User Interface/Window/WindowScene.swift | 2 -
.../User Interface/Window/WindowStorage.swift | 2 -
.../User Interface/Window/WindowType.swift | 2 -
Sources/Adwaita/View/Banner+.swift | 33 +
Sources/Adwaita/View/Banner.swift | 76 --
Sources/Adwaita/View/Button+.swift | 64 ++
Sources/Adwaita/View/Button.swift | 98 ---
Sources/Adwaita/View/Carousel+.swift | 17 +
Sources/Adwaita/View/Carousel.swift | 49 --
Sources/Adwaita/View/Container.swift | 101 ---
Sources/Adwaita/View/Forms/ActionRow+.swift | 17 +
Sources/Adwaita/View/Forms/ActionRow.swift | 99 ---
Sources/Adwaita/View/Forms/ComboRow+.swift | 61 ++
Sources/Adwaita/View/Forms/ComboRow.swift | 132 ---
Sources/Adwaita/View/Forms/EntryRow+.swift | 59 ++
Sources/Adwaita/View/Forms/EntryRow.swift | 136 ----
Sources/Adwaita/View/Forms/Form.swift | 39 +-
Sources/Adwaita/View/Forms/FormSection+.swift | 30 +
Sources/Adwaita/View/Forms/FormSection.swift | 89 --
.../View/Forms/PasswordEntryRow+.swift | 43 +
Sources/Adwaita/View/Forms/SpinRow+.swift | 62 ++
Sources/Adwaita/View/Forms/SpinRow.swift | 149 ----
Sources/Adwaita/View/Forms/SwitchRow+.swift | 21 +
Sources/Adwaita/View/Forms/SwitchRow.swift | 113 ---
.../Adwaita/View/Generated/ActionRow.swift | 334 ++++++++
Sources/Adwaita/View/Generated/Avatar.swift | 139 ++++
Sources/Adwaita/View/Generated/Banner.swift | 161 ++++
Sources/Adwaita/View/Generated/Bin.swift | 81 ++
Sources/Adwaita/View/Generated/Box.swift | 173 ++++
Sources/Adwaita/View/Generated/Button.swift | 221 +++++
.../View/Generated/ButtonContent.swift | 162 ++++
Sources/Adwaita/View/Generated/Carousel.swift | 229 ++++++
.../Adwaita/View/Generated/CenterBox.swift | 183 +++++
.../Adwaita/View/Generated/CheckButton.swift | 246 ++++++
Sources/Adwaita/View/Generated/Clamp.swift | 146 ++++
Sources/Adwaita/View/Generated/ComboRow.swift | 380 +++++++++
Sources/Adwaita/View/Generated/EntryRow.swift | 293 +++++++
.../Adwaita/View/Generated/ExpanderRow.swift | 332 ++++++++
.../Adwaita/View/Generated/HeaderBar.swift | 331 ++++++++
Sources/Adwaita/View/Generated/Label.swift | 502 ++++++++++++
Sources/Adwaita/View/Generated/LevelBar.swift | 238 ++++++
.../Adwaita/View/Generated/LinkButton.swift | 246 ++++++
Sources/Adwaita/View/Generated/ListBox.swift | 319 ++++++++
Sources/Adwaita/View/Generated/Menu.swift | 289 +++++++
Sources/Adwaita/View/Generated/Overlay.swift | 167 ++++
.../View/Generated/OverlaySplitView.swift | 361 +++++++++
.../View/Generated/PasswordEntryRow.swift | 248 ++++++
.../View/Generated/PreferencesGroup.swift | 159 ++++
.../View/Generated/PreferencesPage.swift | 157 ++++
.../View/Generated/PreferencesRow.swift | 134 +++
.../Adwaita/View/Generated/ProgressBar.swift | 184 +++++
.../View/Generated/ScrolledWindow.swift | 403 +++++++++
Sources/Adwaita/View/Generated/SpinRow.swift | 432 ++++++++++
Sources/Adwaita/View/Generated/Spinner.swift | 83 ++
.../Adwaita/View/Generated/SplitButton.swift | 274 +++++++
.../Adwaita/View/Generated/StatusPage.swift | 133 +++
.../Adwaita/View/Generated/SwitchRow.swift | 309 +++++++
.../Adwaita/View/Generated/ToastOverlay.swift | 108 +++
.../Adwaita/View/Generated/ToggleButton.swift | 296 +++++++
.../Adwaita/View/Generated/ToolbarView.swift | 332 ++++++++
.../Adwaita/View/Generated/WindowTitle.swift | 99 +++
Sources/Adwaita/View/HStack.swift | 31 +-
Sources/Adwaita/View/HeaderBar+.swift | 52 ++
Sources/Adwaita/View/HeaderBar.swift | 113 ---
Sources/Adwaita/View/List.swift | 121 +--
Sources/Adwaita/View/Menu+.swift | 53 ++
Sources/Adwaita/View/Menu.swift | 101 ---
.../View/Modifiers/AppearObserver.swift | 15 +-
Sources/Adwaita/View/Modifiers/Clamp+.swift | 52 ++
Sources/Adwaita/View/Modifiers/Clamp.swift | 52 --
.../View/Modifiers/ContentModifier.swift | 2 -
.../View/Modifiers/InspectorWrapper.swift | 44 +-
.../View/Modifiers/ModifierStopper.swift | 2 -
Sources/Adwaita/View/Modifiers/Overlay.swift | 56 --
.../View/Modifiers/ToastOverlay+.swift | 78 ++
.../Adwaita/View/Modifiers/ToastOverlay.swift | 83 --
.../Adwaita/View/Modifiers/ToolbarView.swift | 93 ---
Sources/Adwaita/View/Modifiers/View+.swift | 34 +
.../Adwaita/View/NavigationSplitView.swift | 16 +-
Sources/Adwaita/View/OverlaySplitView+.swift | 39 +
Sources/Adwaita/View/OverlaySplitView.swift | 98 ---
Sources/Adwaita/View/ProgressBar+.swift | 23 +
Sources/Adwaita/View/ProgressBar.swift | 46 --
Sources/Adwaita/View/ScrollView.swift | 28 +-
Sources/Adwaita/View/StateWrapper.swift | 4 +-
Sources/Adwaita/View/StatusPage+.swift | 29 +
Sources/Adwaita/View/StatusPage.swift | 64 --
Sources/Adwaita/View/Text.swift | 37 +-
Sources/Adwaita/View/Toggle.swift | 92 +--
Sources/Adwaita/View/VStack.swift | 35 +-
Sources/Adwaita/View/ViewStack.swift | 29 +-
Sources/Adwaita/View/ViewSwitcher.swift | 39 +-
Sources/Adwaita/Window/AboutWindow.swift | 36 +-
Sources/Adwaita/Window/FileDialog.swift | 13 +-
Sources/Adwaita/Window/Window.swift | 6 +-
Sources/CAdw/module.modulemap | 6 +
Sources/CAdw/shim.h | 37 +
Sources/Generation/Extensions/String.swift | 80 ++
Sources/Generation/GIR/Class+.swift | 304 +++++++
Sources/Generation/GIR/Class.swift | 227 ++++++
Sources/Generation/GIR/Constructor.swift | 16 +
Sources/Generation/GIR/GIR.swift | 60 ++
Sources/Generation/GIR/GIRType.swift | 38 +
Sources/Generation/GIR/Namespace.swift | 22 +
Sources/Generation/GIR/Parameter.swift | 9 +
Sources/Generation/GIR/Parameters.swift | 22 +
Sources/Generation/GIR/Property.swift | 268 ++++++
Sources/Generation/GIR/Signal.swift | 66 ++
Sources/Generation/Generation.swift | 103 +++
.../Generation/GenerationConfiguration.swift | 266 ++++++
Sources/Generation/WidgetConfiguration.swift | 68 ++
Tests/CounterDemo.swift | 1 -
Tests/Demo.swift | 12 +-
Tests/FormDemo.swift | 44 +-
Tests/Page.swift | 3 +-
Tests/ToolbarDemo.swift | 6 -
Tests/ViewSwitcherDemo.swift | 1 -
user-manual/Advanced/CreatingWidgets.md | 17 +-
.../Information/AutoGeneratedWidgets.md | 89 ++
user-manual/Information/Widgets.md | 12 +-
158 files changed, 13296 insertions(+), 3047 deletions(-)
create mode 100644 Sources/Adwaita/Model/Enumerations/Alignment.swift
create mode 100644 Sources/Adwaita/Model/Enumerations/Edge.swift
create mode 100644 Sources/Adwaita/Model/Enumerations/Icon.swift
create mode 100644 Sources/Adwaita/Model/Enumerations/Transition.swift
create mode 100644 Sources/Adwaita/Model/Extensions/Bool.swift
create mode 100644 Sources/Adwaita/Model/Extensions/Int.swift
delete mode 100644 Sources/Adwaita/Model/Extensions/Libadwaita.FileDialog.swift
delete mode 100644 Sources/Adwaita/Model/Extensions/NativeWidgetPeer.swift
create mode 100644 Sources/Adwaita/Model/Extensions/OpaquePointer.swift
create mode 100644 Sources/Adwaita/Model/Extensions/Set.swift
create mode 100644 Sources/Adwaita/Model/Extensions/UInt.swift
create mode 100644 Sources/Adwaita/Model/Extensions/UnsafeMutablePointer.swift
create mode 100644 Sources/Adwaita/Model/Extensions/UnsafeMutableRawPointer.swift
create mode 100644 Sources/Adwaita/Model/User Interface/Window/GTUIAboutWindow.swift
create mode 100644 Sources/Adwaita/Model/User Interface/Window/GTUIFileDialog.swift
create mode 100644 Sources/Adwaita/View/Banner+.swift
delete mode 100644 Sources/Adwaita/View/Banner.swift
create mode 100644 Sources/Adwaita/View/Button+.swift
delete mode 100644 Sources/Adwaita/View/Button.swift
create mode 100644 Sources/Adwaita/View/Carousel+.swift
delete mode 100644 Sources/Adwaita/View/Carousel.swift
delete mode 100644 Sources/Adwaita/View/Container.swift
create mode 100644 Sources/Adwaita/View/Forms/ActionRow+.swift
delete mode 100644 Sources/Adwaita/View/Forms/ActionRow.swift
create mode 100644 Sources/Adwaita/View/Forms/ComboRow+.swift
delete mode 100644 Sources/Adwaita/View/Forms/ComboRow.swift
create mode 100644 Sources/Adwaita/View/Forms/EntryRow+.swift
delete mode 100644 Sources/Adwaita/View/Forms/EntryRow.swift
create mode 100644 Sources/Adwaita/View/Forms/FormSection+.swift
delete mode 100644 Sources/Adwaita/View/Forms/FormSection.swift
create mode 100644 Sources/Adwaita/View/Forms/PasswordEntryRow+.swift
create mode 100644 Sources/Adwaita/View/Forms/SpinRow+.swift
delete mode 100644 Sources/Adwaita/View/Forms/SpinRow.swift
create mode 100644 Sources/Adwaita/View/Forms/SwitchRow+.swift
delete mode 100644 Sources/Adwaita/View/Forms/SwitchRow.swift
create mode 100644 Sources/Adwaita/View/Generated/ActionRow.swift
create mode 100644 Sources/Adwaita/View/Generated/Avatar.swift
create mode 100644 Sources/Adwaita/View/Generated/Banner.swift
create mode 100644 Sources/Adwaita/View/Generated/Bin.swift
create mode 100644 Sources/Adwaita/View/Generated/Box.swift
create mode 100644 Sources/Adwaita/View/Generated/Button.swift
create mode 100644 Sources/Adwaita/View/Generated/ButtonContent.swift
create mode 100644 Sources/Adwaita/View/Generated/Carousel.swift
create mode 100644 Sources/Adwaita/View/Generated/CenterBox.swift
create mode 100644 Sources/Adwaita/View/Generated/CheckButton.swift
create mode 100644 Sources/Adwaita/View/Generated/Clamp.swift
create mode 100644 Sources/Adwaita/View/Generated/ComboRow.swift
create mode 100644 Sources/Adwaita/View/Generated/EntryRow.swift
create mode 100644 Sources/Adwaita/View/Generated/ExpanderRow.swift
create mode 100644 Sources/Adwaita/View/Generated/HeaderBar.swift
create mode 100644 Sources/Adwaita/View/Generated/Label.swift
create mode 100644 Sources/Adwaita/View/Generated/LevelBar.swift
create mode 100644 Sources/Adwaita/View/Generated/LinkButton.swift
create mode 100644 Sources/Adwaita/View/Generated/ListBox.swift
create mode 100644 Sources/Adwaita/View/Generated/Menu.swift
create mode 100644 Sources/Adwaita/View/Generated/Overlay.swift
create mode 100644 Sources/Adwaita/View/Generated/OverlaySplitView.swift
create mode 100644 Sources/Adwaita/View/Generated/PasswordEntryRow.swift
create mode 100644 Sources/Adwaita/View/Generated/PreferencesGroup.swift
create mode 100644 Sources/Adwaita/View/Generated/PreferencesPage.swift
create mode 100644 Sources/Adwaita/View/Generated/PreferencesRow.swift
create mode 100644 Sources/Adwaita/View/Generated/ProgressBar.swift
create mode 100644 Sources/Adwaita/View/Generated/ScrolledWindow.swift
create mode 100644 Sources/Adwaita/View/Generated/SpinRow.swift
create mode 100644 Sources/Adwaita/View/Generated/Spinner.swift
create mode 100644 Sources/Adwaita/View/Generated/SplitButton.swift
create mode 100644 Sources/Adwaita/View/Generated/StatusPage.swift
create mode 100644 Sources/Adwaita/View/Generated/SwitchRow.swift
create mode 100644 Sources/Adwaita/View/Generated/ToastOverlay.swift
create mode 100644 Sources/Adwaita/View/Generated/ToggleButton.swift
create mode 100644 Sources/Adwaita/View/Generated/ToolbarView.swift
create mode 100644 Sources/Adwaita/View/Generated/WindowTitle.swift
create mode 100644 Sources/Adwaita/View/HeaderBar+.swift
delete mode 100644 Sources/Adwaita/View/HeaderBar.swift
create mode 100644 Sources/Adwaita/View/Menu+.swift
delete mode 100644 Sources/Adwaita/View/Menu.swift
create mode 100644 Sources/Adwaita/View/Modifiers/Clamp+.swift
delete mode 100644 Sources/Adwaita/View/Modifiers/Clamp.swift
delete mode 100644 Sources/Adwaita/View/Modifiers/Overlay.swift
create mode 100644 Sources/Adwaita/View/Modifiers/ToastOverlay+.swift
delete mode 100644 Sources/Adwaita/View/Modifiers/ToastOverlay.swift
delete mode 100644 Sources/Adwaita/View/Modifiers/ToolbarView.swift
create mode 100644 Sources/Adwaita/View/OverlaySplitView+.swift
delete mode 100644 Sources/Adwaita/View/OverlaySplitView.swift
create mode 100644 Sources/Adwaita/View/ProgressBar+.swift
delete mode 100644 Sources/Adwaita/View/ProgressBar.swift
create mode 100644 Sources/Adwaita/View/StatusPage+.swift
delete mode 100644 Sources/Adwaita/View/StatusPage.swift
create mode 100644 Sources/CAdw/module.modulemap
create mode 100644 Sources/CAdw/shim.h
create mode 100644 Sources/Generation/Extensions/String.swift
create mode 100644 Sources/Generation/GIR/Class+.swift
create mode 100644 Sources/Generation/GIR/Class.swift
create mode 100644 Sources/Generation/GIR/Constructor.swift
create mode 100644 Sources/Generation/GIR/GIR.swift
create mode 100644 Sources/Generation/GIR/GIRType.swift
create mode 100644 Sources/Generation/GIR/Namespace.swift
create mode 100644 Sources/Generation/GIR/Parameter.swift
create mode 100644 Sources/Generation/GIR/Parameters.swift
create mode 100644 Sources/Generation/GIR/Property.swift
create mode 100644 Sources/Generation/GIR/Signal.swift
create mode 100644 Sources/Generation/Generation.swift
create mode 100644 Sources/Generation/GenerationConfiguration.swift
create mode 100644 Sources/Generation/WidgetConfiguration.swift
create mode 100644 user-manual/Information/AutoGeneratedWidgets.md
diff --git a/.github/ISSUE_TEMPLATE/component_request.yml b/.github/ISSUE_TEMPLATE/component_request.yml
index a1f3e32..63a6c7b 100644
--- a/.github/ISSUE_TEMPLATE/component_request.yml
+++ b/.github/ISSUE_TEMPLATE/component_request.yml
@@ -12,14 +12,6 @@ body:
validations:
required: false
- - type: textarea
- attributes:
- label: Name the equivalents in Libadwaita.
- placeholder: >-
- A list of the equivalents in the Libadwaita library (see dependencies).
- validations:
- required: true
-
- type: textarea
attributes:
label: Describe your idea for the implementation.
diff --git a/.swiftlint.yml b/.swiftlint.yml
index 350656d..062b47f 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -160,3 +160,6 @@ type_contents_order:
- view_life_cycle_method
- ib_action
- other_method
+
+excluded:
+ - Sources/Adwaita/View/Generated/
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4be32e2..a4b3e66 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -36,6 +36,8 @@ Open the project folder in GNOME Builder, Xcode or another IDE.
- `User Interface` contains protocols and structures that are the basis of presenting content to the user.
- `View` contains structures that conform to the `View` protocol and provide an easier-to-use wrapper around a GTUI `NativeWidgetPeer` type.
- `Window` contains structures that conform to the `Window` protocol and simplify the creation of different types of windows.
+ - `CAdw` contains the reference to the C library.
+ - `Generation` contains the code for the auto-generation of Adwaita and Gtk widgets.
- `Tests` contains an example application for testing `Adwaita`.
### 4. Edit the Code
diff --git a/LICENSE.md b/LICENSE.md
index 10926e8..626e124 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,675 +1,21 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C)
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
-
+MIT License
+
+Copyright (c) 2024 david-swift
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Package.swift b/Package.swift
index 16a12ae..a180a8a 100644
--- a/Package.swift
+++ b/Package.swift
@@ -15,25 +15,39 @@ let package = Package(
.library(
name: "Adwaita",
targets: ["Adwaita"]
+ ),
+ .library(
+ name: "CAdw",
+ targets: ["CAdw"]
)
],
dependencies: [
- .package(url: "https://github.com/AparokshaUI/Libadwaita", from: "0.1.6"),
.package(
url: "https://github.com/david-swift/LevenshteinTransformations",
from: "0.1.1"
- )
+ ),
+ .package(url: "https://github.com/CoreOffice/XMLCoder", from: "0.17.1")
],
targets: [
+ .systemLibrary(
+ name: "CAdw",
+ pkgConfig: "libadwaita-1"
+ ),
.target(
name: "Adwaita",
dependencies: [
- .product(name: "Libadwaita", package: "Libadwaita"),
+ "CAdw",
.product(name: "LevenshteinTransformations", package: "LevenshteinTransformations")
]
),
.executableTarget(
- name: "Swift Adwaita Demo",
+ name: "Generation",
+ dependencies: [
+ .product(name: "XMLCoder", package: "XMLCoder")
+ ]
+ ),
+ .executableTarget(
+ name: "Demo",
dependencies: ["Adwaita"],
path: "Tests"
)
diff --git a/README.md b/README.md
index 62c8954..3636660 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
Adwaita
+
Adwaita for Swift
@@ -110,15 +110,17 @@ I recommend using the [template repository](https://github.com/AparokshaUI/Adwai
### Information
* [Widgets](user-manual/Information/Widgets.md)
+* [Auto-Generated Widgets](user-manual/Information/AutoGeneratedWidgets.md)
## Thanks
### Dependencies
-- [Libadwaita][18] licensed under the [GPL-3.0 license][19]
+- [XMLCoder][18] licensed under the [MIT license][19]
- [Levenshtein Transformations](https://github.com/david-swift/LevenshteinTransformations) licensed under the [MIT license](https://github.com/david-swift/LevenshteinTransformations/blob/main/LICENSE.md)
### Other Thanks
- The [contributors][20]
+- The auto-generation of widgets is based on [Swift Cross UI](https://github.com/stackotter/swift-cross-ui)
- [SwiftLint][21] for checking whether code style conventions are violated
- The programming language [Swift][22]
- [SourceDocs][23] used for generating the [docs][24]
@@ -140,8 +142,8 @@ I recommend using the [template repository](https://github.com/AparokshaUI/Adwai
[15]: user-manual/Basics/Windows.md
[16]: user-manual/Basics/KeyboardShortcuts.md
[17]: user-manual/Advanced/CreatingWidgets.md
-[18]: https://github.com/AparokshaUI/Libadwaita
-[19]: https://github.com/AparokshaUI/Libadwaita/blob/main/LICENSE.md
+[18]: https://github.com/CoreOffice/XMLCoder
+[19]: https://github.com/CoreOffice/XMLCoder/blob/main/LICENSE
[20]: Contributors.md
[21]: https://github.com/realm/SwiftLint
[22]: https://github.com/apple/swift
diff --git a/SUMMARY.md b/SUMMARY.md
index 9e747b8..b203483 100644
--- a/SUMMARY.md
+++ b/SUMMARY.md
@@ -17,6 +17,7 @@
## Information
* [Widgets](user-manual/Information/Widgets.md)
+* [Auto-Generated Widgets](user-manual/Information/AutoGeneratedWidgets.md)
[1]: README.md
[2]: user-manual/GettingStarted.md
diff --git a/Sources/Adwaita/Menu/MenuButton.swift b/Sources/Adwaita/Menu/MenuButton.swift
index 70b59c7..c3d4351 100644
--- a/Sources/Adwaita/Menu/MenuButton.swift
+++ b/Sources/Adwaita/Menu/MenuButton.swift
@@ -5,7 +5,7 @@
// Created by david-swift on 22.10.23.
//
-import Libadwaita
+import CAdw
/// A button widget for menus.
public struct MenuButton: MenuItem {
@@ -19,6 +19,9 @@ public struct MenuButton: MenuItem {
/// Whether to prefer adding the action to the application window.
var preferApplicationWindow: Bool
+ /// The action label.
+ var filteredLabel: String { label.filter { $0.isLetter || $0.isNumber || $0 == "-" || $0 == "." } }
+
/// Initialize a menu button.
/// - Parameters:
/// - label: The buttons label.
@@ -35,11 +38,13 @@ public struct MenuButton: MenuItem {
/// - menu: The menu.
/// - app: The application containing the menu.
/// - window: The application window containing the menu.
- public func addMenuItem(menu: Libadwaita.Menu, app: GTUIApp, window: GTUIApplicationWindow?) {
+ public func addMenuItem(menu: OpaquePointer?, app: GTUIApp, window: GTUIApplicationWindow?) {
if let window, preferApplicationWindow {
- _ = menu.append(label, window: window, shortcut: shortcut, handler: handler)
+ window.addKeyboardShortcut(shortcut, id: filteredLabel, handler: handler)
+ g_menu_append(menu, label, "win." + filteredLabel)
} else {
- _ = menu.append(label, app: app, shortcut: shortcut, handler: handler)
+ app.addKeyboardShortcut(shortcut, id: filteredLabel, handler: handler)
+ g_menu_append(menu, label, "app." + filteredLabel)
}
}
diff --git a/Sources/Adwaita/Menu/MenuSection.swift b/Sources/Adwaita/Menu/MenuSection.swift
index 6f67f6e..e4d48b1 100644
--- a/Sources/Adwaita/Menu/MenuSection.swift
+++ b/Sources/Adwaita/Menu/MenuSection.swift
@@ -5,7 +5,7 @@
// Created by david-swift on 22.10.23.
//
-import Libadwaita
+import CAdw
/// A section for menus.
public struct MenuSection: MenuItem {
@@ -24,9 +24,9 @@ public struct MenuSection: MenuItem {
/// - menu: The menu.
/// - app: The application containing the menu.
/// - window: The application window containing the menu.
- public func addMenuItem(menu: Libadwaita.Menu, app: GTUIApp, window: GTUIApplicationWindow?) {
- let section = Libadwaita.Menu()
- _ = menu.append("", section: section)
+ public func addMenuItem(menu: OpaquePointer?, app: GTUIApp, window: GTUIApplicationWindow?) {
+ let section = g_menu_new()
+ g_menu_append_section(menu, nil, section?.cast())
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
index d1137b5..5cede15 100644
--- a/Sources/Adwaita/Menu/Submenu.swift
+++ b/Sources/Adwaita/Menu/Submenu.swift
@@ -5,7 +5,7 @@
// Created by david-swift on 22.10.23.
//
-import Libadwaita
+import CAdw
/// A submenu widget.
public struct Submenu: MenuItem {
@@ -29,9 +29,9 @@ public struct Submenu: MenuItem {
/// - menu: The menu.
/// - app: The application containing the menu.
/// - window: The application window containing the menu.
- public func addMenuItem(menu: Libadwaita.Menu, app: GTUIApp, window: GTUIApplicationWindow?) {
- let submenu = Libadwaita.Menu()
- _ = menu.append(label, submenu: submenu)
+ public func addMenuItem(menu: OpaquePointer?, app: GTUIApp, window: GTUIApplicationWindow?) {
+ let submenu = g_menu_new()
+ g_menu_append_submenu(menu, label, submenu?.cast())
for element in submenuContent {
element.addMenuItems(menu: submenu, app: app, window: window)
}
diff --git a/Sources/Adwaita/Model/ArrayBuilder.swift b/Sources/Adwaita/Model/ArrayBuilder.swift
index 13fafa3..2adf197 100644
--- a/Sources/Adwaita/Model/ArrayBuilder.swift
+++ b/Sources/Adwaita/Model/ArrayBuilder.swift
@@ -9,8 +9,6 @@
// https://gist.github.com/JadenGeller/c375fc15ad5900a0ddac4ed8ba8307a9
//
-import Foundation
-
/// The ``ArrayBuilder`` is a simple result builder that outputs an array of any type.
///
/// You can define any array using Swift's DSL:
diff --git a/Sources/Adwaita/Model/Data Flow/State.swift b/Sources/Adwaita/Model/Data Flow/State.swift
index 9b4738c..38a45fb 100644
--- a/Sources/Adwaita/Model/Data Flow/State.swift
+++ b/Sources/Adwaita/Model/Data Flow/State.swift
@@ -5,8 +5,8 @@
// Created by david-swift on 06.08.23.
//
+import CAdw
import Foundation
-import Libadwaita
/// A property wrapper for properties in a view that should be stored throughout view updates.
@propertyWrapper
@@ -110,11 +110,23 @@ public struct State: StateProtocol {
}
}
+ /// The directory used for storing user data.
+ /// - Returns: The URL.
+ public static func userDataDir() -> URL {
+ .init(fileURLWithPath: .init(cString: g_get_user_data_dir()))
+ }
+
+ /// Copy a text to the clipboard.
+ /// - Parameter text: The text.
+ public static func copy(_ text: String) {
+ let clipboard = gdk_display_get_clipboard(gdk_display_get_default())
+ gdk_clipboard_set_text(clipboard, text)
+ }
+
/// Get the settings directory path.
/// - Returns: The path.
private func dirPath() -> URL {
- NativePeer
- .getUserDataDirectory()
+ Self.userDataDir()
.appendingPathComponent(content.storage.folder ?? GTUIApp.appID, isDirectory: true)
}
diff --git a/Sources/Adwaita/Model/Enumerations/Alignment.swift b/Sources/Adwaita/Model/Enumerations/Alignment.swift
new file mode 100644
index 0000000..ee76cc5
--- /dev/null
+++ b/Sources/Adwaita/Model/Enumerations/Alignment.swift
@@ -0,0 +1,44 @@
+//
+// Alignment.swift
+// Adwaita
+//
+// Created by david-swift on 21.01.24.
+//
+
+import CAdw
+
+/// The alignment for a widget.
+public enum Alignment: Int {
+
+ /// The widget will fill the available space.
+ case fill
+ /// The widget will start at the beginning of the available space.
+ case start
+ /// The widget will end at the end of the available space.
+ case end
+ /// The widget will be centered in the available space.
+ case center
+ /// The widget will be baseline aligned in the available space.
+ case baselineFill
+ /// The widget will be baseline aligned at the start of the available space.
+ case baselineCenter
+
+ /// Get the GtkAlign alignment.
+ public var cAlign: GtkAlign {
+ switch self {
+ case .fill:
+ return GTK_ALIGN_FILL
+ case .start:
+ return GTK_ALIGN_START
+ case .end:
+ return GTK_ALIGN_END
+ case .center:
+ return GTK_ALIGN_CENTER
+ case .baselineFill:
+ return GTK_ALIGN_BASELINE_FILL
+ case .baselineCenter:
+ return GTK_ALIGN_BASELINE_CENTER
+ }
+ }
+
+}
diff --git a/Sources/Adwaita/Model/Enumerations/Edge.swift b/Sources/Adwaita/Model/Enumerations/Edge.swift
new file mode 100644
index 0000000..bb1896b
--- /dev/null
+++ b/Sources/Adwaita/Model/Enumerations/Edge.swift
@@ -0,0 +1,20 @@
+//
+// Edge.swift
+// Adwaita
+//
+// Created by david-swift on 21.01.24.
+//
+
+/// The edges for a widget.
+public enum Edge {
+
+ /// The leading (start) edge.
+ case leading
+ /// The trailing (end) edge.
+ case trailing
+ /// The top edge.
+ case top
+ /// The bottom edge.
+ case bottom
+
+}
diff --git a/Sources/Adwaita/Model/Enumerations/Icon.swift b/Sources/Adwaita/Model/Enumerations/Icon.swift
new file mode 100644
index 0000000..348751c
--- /dev/null
+++ b/Sources/Adwaita/Model/Enumerations/Icon.swift
@@ -0,0 +1,767 @@
+//
+// Icon.swift
+// Adwaita
+//
+// Created by david-swift on 21.01.24.
+//
+
+import Foundation
+
+// swiftlint:disable type_body_length file_length
+/// An icon.
+public enum Icon {
+
+ /// A preinstalled icon.
+ /// - Parameter icon: The default icon.
+ case `default`(icon: DefaultIcon)
+ /// A custom icon.
+ /// - Parameter name: The icon's name.
+ case custom(name: String)
+
+ /// A string representation of the icon.
+ public var string: String {
+ switch self {
+ case let .default(icon):
+ return icon.string
+ case let .custom(name):
+ return name
+ }
+ }
+
+ /// A preinstalled icon.
+ public enum DefaultIcon: String {
+ // swiftlint:disable missing_docs identifier_name
+ case acAdapter
+ case accessoriesCalculator
+ case accessoriesCharacterMap
+ case accessoriesDictionary
+ case accessoriesTextEditor
+ case actionUnavailable
+ case addressBookNew
+ case airplaneMode
+ case alarm
+ case appRemove
+ case appletsScreenshooter
+ case applicationCertificate
+ case applicationExitRtl
+ case applicationExit
+ case applicationRss_plus_xml
+ case applicationXAddon
+ case applicationXAppliance
+ case applicationXExecutable
+ case applicationXFirmware
+ case applicationXSharedlib
+ case applicationsEngineering
+ case applicationsGames
+ case applicationsGraphics
+ case applicationsMultimedia
+ case applicationsScience
+ case applicationsSystem
+ case applicationsUtilities
+ case appointmentMissed
+ case appointmentNew
+ case appointmentSoon
+ case audioCard
+ case audioHeadphones
+ case audioHeadset
+ case audioInputMicrophone
+ case audioSpeakersRtl
+ case audioSpeakers
+ case audioVolumeHighRtl
+ case audioVolumeHigh
+ case audioVolumeLowRtl
+ case audioVolumeLow
+ case audioVolumeMediumRtl
+ case audioVolumeMedium
+ case audioVolumeMutedRtl
+ case audioVolumeMuted
+ case audioVolumeOveramplifiedRtl
+ case audioVolumeOveramplified
+ case audioXGeneric
+ case authFace
+ case authFingerprint
+ case authSimLocked
+ case authSimMissing
+ case authSim
+ case authSmartcard
+ case avatarDefault
+ case batteryAction
+ case batteryCautionCharging
+ case batteryCaution
+ case batteryEmptyCharging
+ case batteryEmpty
+ case batteryFullCharged
+ case batteryFullCharging
+ case batteryFull
+ case batteryGoodCharging
+ case batteryGood
+ case batteryLevel_0Charging
+ case batteryLevel_0
+ case batteryLevel_10Charging
+ case batteryLevel_10
+ case batteryLevel_100Charged
+ case batteryLevel_100
+ case batteryLevel_20Charging
+ case batteryLevel_20
+ case batteryLevel_30Charging
+ case batteryLevel_30
+ case batteryLevel_40Charging
+ case batteryLevel_40
+ case batteryLevel_50Charging
+ case batteryLevel_50
+ case batteryLevel_60Charging
+ case batteryLevel_60
+ case batteryLevel_70Charging
+ case batteryLevel_70
+ case batteryLevel_80Charging
+ case batteryLevel_80
+ case batteryLevel_90Charging
+ case batteryLevel_90
+ case batteryLowCharging
+ case batteryLow
+ case batteryMissing
+ case battery
+ case bluetoothAcquiring
+ case bluetoothActive
+ case bluetoothDisabled
+ case bluetoothDisconnected
+ case bluetoothHardwareDisabled
+ case bluetooth
+ case bookmarkNew
+ case callIncoming
+ case callMissed
+ case callOutgoing
+ case callStart
+ case callStop
+ case cameraDisabled
+ case cameraHardwareDisabled
+ case cameraPhoto
+ case cameraSwitch
+ case cameraVideo
+ case cameraWeb
+ case capsLock
+ case changesAllow
+ case changesPrevent
+ case channelInsecure
+ case channelSecure
+ case chatMessageNew
+ case checkboxChecked
+ case checkboxMixed
+ case checkbox
+ case colorSelect
+ case colorimeterColorhug
+ case completionSnippet
+ case completionWord
+ case computerAppleIpad
+ case computerFail
+ case computer
+ case contactNew
+ case contentLoading
+ case daytimeSunrise
+ case daytimeSunset
+ case dialogError
+ case dialogInformation
+ case dialogPassword
+ case dialogQuestion
+ case dialogWarning
+ case displayBrightness
+ case displayProjector
+ case documentEdit
+ case documentNew
+ case documentOpenRecent
+ case documentOpen
+ case documentPageSetup
+ case documentPrintPreview
+ case documentPrint
+ case documentProperties
+ case documentRevertRtl
+ case documentRevert
+ case documentSaveAs
+ case documentSave
+ case documentSend
+ case driveHarddiskIeee1394
+ case driveHarddiskSolidstate
+ case driveHarddisk
+ case driveHarddiskSystem
+ case driveHarddiskUsb
+ case driveMultidisk
+ case driveOptical
+ case driveRemovableMedia
+ case editClearAll
+ case editClearRtl
+ case editClear
+ case editCopy
+ case editCut
+ case editDelete
+ case editFindReplace
+ case editFind
+ case editPaste
+ case editRedo
+ case editSelectAll
+ case editSelect
+ case editUndo
+ case emblemDefault
+ case emblemDocuments
+ case emblemFavorite
+ case emblemImportant
+ case emblemMusic
+ case emblemOk
+ case emblemPhotos
+ case emblemShared
+ case emblemSynchronizing
+ case emblemSystem
+ case emblemVideos
+ case emojiActivities
+ case emojiBody
+ case emojiFlags
+ case emojiFood
+ case emojiNature
+ case emojiObjects
+ case emojiPeople
+ case emojiRecent
+ case emojiSymbols
+ case emojiTravel
+ case emoteLove
+ case errorCorrect
+ case faceAngel
+ case faceAngry
+ case faceConfused
+ case faceCool
+ case faceCrying
+ case faceDevilish
+ case faceEmbarrassed
+ case faceGlasses
+ case faceKiss
+ case faceLaugh
+ case faceMonkey
+ case facePlain
+ case faceRaspberry
+ case faceSad
+ case faceShutmouth
+ case faceSick
+ case faceSmileBig
+ case faceSmile
+ case faceSmirk
+ case faceSurprise
+ case faceTired
+ case faceUncertain
+ case faceWink
+ case faceWorried
+ case faceYawn
+ case findLocation
+ case focusLegacySystray
+ case focusTopBar
+ case focusWindows
+ case folderDocuments
+ case folderDownload
+ case folderDragAccept
+ case folderMusic
+ case folderNew
+ case folderOpen
+ case folderPictures
+ case folderPublicshare
+ case folderRemote
+ case folderSavedSearch
+ case folder
+ case folderTemplates
+ case folderVideos
+ case folderVisiting
+ case fontSelect
+ case fontXGeneric
+ case formatIndentLessRtl
+ case formatIndentLess
+ case formatIndentMoreRtl
+ case formatIndentMore
+ case formatJustifyCenter
+ case formatJustifyFill
+ case formatJustifyLeft
+ case formatJustifyRight
+ case formatTextBold
+ case formatTextDirectionLtr
+ case formatTextDirectionRtl
+ case formatTextDirection
+ case formatTextItalic
+ case formatTextPlaintext
+ case formatTextRich
+ case formatTextStrikethrough
+ case formatTextUnderline
+ case functionLinear
+ case gestureSwipeLeft
+ case gestureSwipeRight
+ case gnomeDisksStateStandby
+ case gnomePowerManager
+ case goBottom
+ case goDown
+ case goFirst
+ case goHome
+ case goJumpRtl
+ case goJump
+ case goLast
+ case goNext
+ case goPrevious
+ case goTop
+ case goUp
+ case goaAccountExchange
+ case goaAccountGoogle
+ case goaAccountLastfm
+ case goaAccountMsn
+ case goaAccountOwncloud
+ case goaAccount
+ case goaPanel
+ case gtk3Demo
+ case gtk3WidgetFactory
+ case helpAbout
+ case helpBrowser
+ case helpContents
+ case helpFaq
+ case imageLoading
+ case imageMissing
+ case imageXGeneric
+ case info
+ case inodeDirectory
+ case inputDialpad
+ case inputGaming
+ case inputKeyboard
+ case inputMouse
+ case inputTablet
+ case inputTouchpad
+ case insertImage
+ case insertLink
+ case insertObject
+ case insertText
+ case keyboardBrightness
+ case langClass
+ case langDefine
+ case langEnum
+ case langEnumValue
+ case langFunction
+ case langInclude
+ case langMethod
+ case langNamespace
+ case langStructField
+ case langStruct
+ case langTypedef
+ case langUnion
+ case langVariable
+ case libreofficeBase
+ case libreofficeCalc
+ case libreofficeDraw
+ case libreofficeImpress
+ case libreofficeMain
+ case libreofficeMath
+ case libreofficeWriter
+ case listAdd
+ case listDragHandle
+ case listRemoveAll
+ case listRemove
+ case locationServicesActive
+ case locationServicesDisabled
+ case mailAttachment
+ case mailForward
+ case mailMarkImportant
+ case mailMarkJunk
+ case mailMarkNotjunk
+ case mailMessageNew
+ case mailRead
+ case mailRepliedRtl
+ case mailReplied
+ case mailReplyAllRtl
+ case mailReplyAll
+ case mailReplySender
+ case mailSendReceive
+ case mailSend
+ case mailUnread
+ case markLocation
+ case mediaEject
+ case mediaFlash
+ case mediaFloppy
+ case mediaOpticalBd
+ case mediaOpticalCdAudio
+ case mediaOpticalCd
+ case mediaOpticalDvd
+ case mediaOptical
+ case mediaPlaybackPause
+ case mediaPlaybackStart
+ case mediaPlaybackStop
+ case mediaPlaylistConsecutive
+ case mediaPlaylistRepeatSong
+ case mediaPlaylistRepeat
+ case mediaPlaylistShuffle
+ case mediaRecord
+ case mediaRemovable
+ case mediaSeekBackward
+ case mediaSeekForward
+ case mediaSkipBackward
+ case mediaSkipForward
+ case mediaTape
+ case mediaViewSubtitles
+ case mediaZip
+ case microphoneDisabled
+ case microphoneHardwareDisabled
+ case microphoneSensitivityHigh
+ case microphoneSensitivityLow
+ case microphoneSensitivityMedium
+ case microphoneSensitivityMuted
+ case modem
+ case multimediaPlayerAppleIpodTouch
+ case multimediaPlayer
+ case multimediaVolumeControl
+ case networkCellular_2g
+ case networkCellular_3g
+ case networkCellular_4g
+ case networkCellular_5g
+ case networkCellularAcquiringRtl
+ case networkCellularAcquiring
+ case networkCellularConnected
+ case networkCellularDisabledRtl
+ case networkCellularDisabled
+ case networkCellularEdge
+ case networkCellularGprs
+ case networkCellularHardwareDisabledRtl
+ case networkCellularHardwareDisabled
+ case networkCellularHspa
+ case networkCellularNoRouteRtl
+ case networkCellularNoRoute
+ case networkCellularOfflineRtl
+ case networkCellularOffline
+ case networkCellularSignalExcellentRtl
+ case networkCellularSignalExcellent
+ case networkCellularSignalGoodRtl
+ case networkCellularSignalGood
+ case networkCellularSignalNoneRtl
+ case networkCellularSignalNone
+ case networkCellularSignalOkRtl
+ case networkCellularSignalOk
+ case networkCellularSignalWeakRtl
+ case networkCellularSignalWeak
+ case networkCellular
+ case networkError
+ case networkIdle
+ case networkNoRoute
+ case networkOffline
+ case networkReceive
+ case networkServer
+ case networkTransmitReceive
+ case networkTransmit
+ case networkVpnAcquiring
+ case networkVpnDisabled
+ case networkVpnDisconnected
+ case networkVpnNoRoute
+ case networkVpn
+ case networkWiredAcquiring
+ case networkWiredDisconnected
+ case networkWiredNoRoute
+ case networkWired
+ case networkWirelessAcquiring
+ case networkWirelessConnected
+ case networkWirelessDisabled
+ case networkWirelessEncrypted
+ case networkWirelessHardwareDisabled
+ case networkWirelessHotspot
+ case networkWirelessNoRoute
+ case networkWirelessOffline
+ case networkWirelessSignalExcellent
+ case networkWirelessSignalGood
+ case networkWirelessSignalNone
+ case networkWirelessSignalOk
+ case networkWirelessSignalWeak
+ case networkWireless
+ case networkWorkgroup
+ case nightLightDisabled
+ case nightLight
+ case nmDeviceWiredSecure
+ case nmDeviceWired
+ case nmDeviceWwan
+ case nonStarred
+ case notificationsDisabled
+ case objectFlipHorizontal
+ case objectFlipVertical
+ case objectRotateLeft
+ case objectRotateRight
+ case objectSelect
+ case openMenu
+ case orca
+ case fedoraprojectAnacondaInstaller = "org.fedoraproject.AnacondaInstaller"
+ case freedesktopMalcontentControl = "org.freedesktop.MalcontentControl"
+ case gnomeAdwaita1Demo = "org.gnome.Adwaita1.Demo"
+ case gnomeBoxes = "org.gnome.Boxes"
+ case gnomeBuilder = "org.gnome.Builder"
+ case gnomeCalculator = "org.gnome.Calculator"
+ case gnomeCharacters = "org.gnome.Characters"
+ case gnomeCheese = "org.gnome.Cheese"
+ case gnomeConsole = "org.gnome.Console"
+ case gnomeDiskUtility = "org.gnome.DiskUtility"
+ case gnomeEpiphany = "org.gnome.Epiphany"
+ case gnomeEvince = "org.gnome.Evince"
+ case gnomeLogs = "org.gnome.Logs"
+ case gnomeMaps = "org.gnome.Maps"
+ case gnomeNautilus = "org.gnome.Nautilus"
+ case gnomePhotos = "org.gnome.Photos"
+ case gnomeRhythmbox3 = "org.gnome.Rhythmbox3"
+ case gnomeSettingsAbout = "org.gnome.Settings-about"
+ case gnomeSettingsAccessibility = "org.gnome.Settings-accessibility"
+ case gnomeSettingsAppearance = "org.gnome.Settings-appearance"
+ case gnomeSettingsApplications = "org.gnome.Settings-applications"
+ case gnomeSettingsBluetooth = "org.gnome.Settings-bluetooth"
+ case gnomeSettingsCamera = "org.gnome.Settings-camera"
+ case gnomeSettingsColor = "org.gnome.Settings-color"
+ case gnomeSettingsDefaultApps = "org.gnome.Settings-default-apps"
+ case gnomeSettingsDiagnostics = "org.gnome.Settings-diagnostics"
+ case gnomeSettingsDisplay = "org.gnome.Settings-display"
+ case gnomeSettingsFileHistory = "org.gnome.Settings-file-history"
+ case gnomeSettingsKeyboard = "org.gnome.Settings-keyboard"
+ case gnomeSettingsLocation = "org.gnome.Settings-location"
+ case gnomeSettingsMicrophone = "org.gnome.Settings-microphone"
+ case gnomeSettingsMobileNetwork = "org.gnome.Settings-mobile-network"
+ case gnomeSettingsMouse = "org.gnome.Settings-mouse"
+ case gnomeSettingsMultitasking = "org.gnome.Settings-multitasking"
+ case gnomeSettingsNetwork = "org.gnome.Settings-network"
+ case gnomeSettingsNotifications = "org.gnome.Settings-notifications"
+ case gnomeSettingsOnlineAccounts = "org.gnome.Settings-online-accounts"
+ case gnomeSettingsPower = "org.gnome.Settings-power"
+ case gnomeSettingsPinters = "org.gnome.Setting-printers"
+ case gnomeSettingsRegion = "org.gnome.Settings-region"
+ case gnomeSettingsRemovableMedia = "org.gnome.Settings-removable-media"
+ case gnomeSettingsSearch = "org.gnome.Settings-search"
+ case gnomeSettingsSharing = "org.gnome.Settings-sharing"
+ case gnomeSettingsSound = "org.gnome.Settings-sound"
+ case gnomeSettings = "org.gnome.Settings"
+ case gnomeSettingsSystemLockScreen = "org.gnome.Settings-system-lock-screen"
+ case gnomeSettingsThunderbolt = "org.gnome.Settings-thunderbolt"
+ case gnomeSettingsTime = "org.gnome.Settings-time"
+ case gnomeSettingsUsers = "org.gnome.Settings-users"
+ case gnomeSettingsWacom = "org.gnome.Settings-wacom"
+ case gnomeShellExtensions = "org.gnome.Shell.Extensions"
+ case gnomeSoftware = "org.gnome.Software"
+ case gnomeSystemMonitor = "org.gnome.SystemMonitor"
+ case gnomeTextEditor = "org.gnome.TextEditor"
+ case gnomeTotem = "org.gnome.Totem"
+ case gnomeWeather = "org.gnome.Weather"
+ case gnomeYelp = "org.gnome.Yelp"
+ case gnomeBaobab = "org.gnome.baobab"
+ case gnomeClocks = "org.gnome.clocks"
+ case gnomeDesignIconLibrary = "org.gnome.design.IconLibrary"
+ case gnomeEog = "org.gnome.eog"
+ case gnomeFontViewer = "org.gnome.font-viewer"
+ case gnomeTweaks = "org.gnome.tweaks"
+ case gtkDemo4 = "org.gtk.Demo4"
+ case gtkIconBrowser4 = "org.gtk.IconBrowser4"
+ case gtkPrintEditor4 = "org.gtk.PrintEditor4"
+ case gtkWidgetFactory4 = "org.gtk.WidgetFactory4"
+ case gtkGtk4NodeEditor = "org.gtk.gtk4.NodeEditor"
+ case orientationLandscapeInverse
+ case orientationLandscape
+ case orientationPortraitInverse
+ case orientationPortraitLeft
+ case orientationPortraitRight
+ case orientationProtrait
+ case packageXGeneric
+ case panDown
+ case panEnd
+ case panStart
+ case panUp
+ case panelBottom
+ case panelCenter
+ case panelLeft
+ case panelModified
+ case panelRight
+ case panelTop
+ case pda
+ case phoneAppleIphone
+ case phoneOld
+ case phone
+ case powerProfileBalancedRtl
+ case powerProfileBalanced
+ case powerProfilePerformanceRtl
+ case powerProfilePerformance
+ case powerProfilePowerSaverRtl
+ case powerProfilePowerSaver
+ case preferencesColor
+ case preferencesDesktopAccessibility
+ case preferencesDesktopAppearance
+ case preferencesDesktopApps
+ case preferencesDesktopDisplay
+ case preferencesDesktopFont
+ case preferencesDesktopKeyboardShortcuts
+ case preferencesDesktopKeyboard
+ case preferencesDesktopLocale
+ case preferencesDesktopMultitasking
+ case preferencesDesktopRemoteDesktop
+ case preferencesDesktopScreensaver
+ case preferencesDesktopWallpaper
+ case preferencesOther
+ case preferencesSystemDetails
+ case preferencesSystemDevices
+ case preferencesSystemNetworkProxy
+ case preferencesSystemNetwork
+ case preferencesSystemNotifications
+ case preferencesSystemParentalControls
+ case preferencesSystemPrivacy
+ case preferencesSystemSearch
+ case preferencesSystemSharing
+ case preferencesSystem
+ case preferencesSystemTime
+ case printerError
+ case printerNetwork
+ case printerPrinting
+ case printer
+ case printerWarning
+ case processStop
+ case processWorking
+ case radioChecked
+ case radioMixed
+ case radio
+ case rotationAllowed
+ case rotationLocked
+ case scanner
+ case screenShared
+ case securityHigh
+ case securityLow
+ case securityMediumRtl
+ case securityMedium
+ case selectionEnd
+ case selectionMode
+ case selectionStart
+ case semiStarredRtl
+ case semiStarred
+ case sendTo
+ case sidebarShowRight
+ case sidebarShow
+ case softwareUpdateAvailable
+ case softwareUpdateUrgent
+ case speedometer
+ case starNew
+ case starred
+ case startHere
+ case switchOff
+ case switchOn
+ case systemFileManager
+ case systemHelp
+ case systemLockScreen
+ case systemLogOutRtl
+ case systemLogOut
+ case systemReboot
+ case systemRun
+ case systemSearch
+ case systemShutdown
+ case systemSoftwareInstall
+ case systemSwitchUserRtl
+ case systemSwitchUser
+ case systemUsers
+ case tabNew
+ case tablet
+ case taskDue
+ case taskPastDue
+ case temperature
+ case textEditor
+ case textXGeneric
+ case thunderboltAcquiring
+ case thunderbolt
+ case toolsCheckSpelling
+ case totemTv
+ case touchDisabled
+ case touchpadDisabled
+ case tv
+ case uninterruptiblePowerSupply
+ case userAvailable
+ case userAway
+ case userBookmarks
+ case userBusy
+ case userDesktop
+ case userHome
+ case userIdle
+ case userInfo
+ case userInvisible
+ case userNotTracked
+ case userOffline
+ case userStatusPending
+ case userTrashFull
+ case userTrash
+ case utilitiesTerminal
+ case valueDecrease
+ case valueIncrease
+ case videoDisplay
+ case videoJoineDisplays
+ case videoSingleDisplay
+ case videoXGeneric
+ case viewAppGrid
+ case viewConceal
+ case viewContinuous
+ case viewDual
+ case viewFullscreen
+ case viewGrid
+ case viewListBulletRtl
+ case viewListBullet
+ case viewListOrderedRtl
+ case viewListOrdered
+ case viewListRtl
+ case viewList
+ case viewMirror
+ case viewMoreHorizontal
+ case viewMore
+ case viewPagedRtl
+ case viewPaged
+ case viewPin
+ case viewRefresh
+ case viewRestore
+ case viewReveal
+ case viewSortAscendingRtl
+ case viewSortAscending
+ case viewSortDescendingRtl
+ case viewSortDescending
+ case viewWrappedRtl
+ case viewWrapped
+ case weatherClearNight
+ case weatherClear
+ case weatherFewCloudsNight
+ case weatherFewClouds
+ case weatherFog
+ case weatherHourly
+ case weatherOvercast
+ case weatherSevereAlert
+ case weatherShowersScattered
+ case weatherShowers
+ case weatherSnow
+ case weatherStorm
+ case weatherTornado
+ case weatherWindy
+ case webBrowser
+ case windowClose
+ case windowMaximize
+ case windowMinimize
+ case windowNew
+ case windowRestore
+ case xOfficeAddressBook
+ case xOfficeCalendar
+ case xOfficeDocument
+ case xOfficeDrawing
+ case xOfficePresentation
+ case xOfficeSpreadsheet
+ case zoomFitBest
+ case zoomIn
+ case zoomOriginal
+ case zoomOut
+ // swiftlint:enable missing_docs identifier_name
+
+ /// A string representation of the icon.
+ public var string: String {
+ var string = rawValue
+ if !string.hasPrefix("org.") {
+ let result = string
+ .map { letter in
+ if letter.isUppercase {
+ return "-\(letter)"
+ } else {
+ return "\(letter)"
+ }
+ }
+ .joined()
+ string = result.lowercased()
+ }
+ string = string.replacingOccurrences(of: "_plus_", with: "+")
+ string = string.replacingOccurrences(of: "_", with: "-")
+ return string + "-symbolic"
+ }
+ }
+}
+// swiftlint:enable type_body_length file_length
diff --git a/Sources/Adwaita/Model/Enumerations/Transition.swift b/Sources/Adwaita/Model/Enumerations/Transition.swift
new file mode 100644
index 0000000..911eb9f
--- /dev/null
+++ b/Sources/Adwaita/Model/Enumerations/Transition.swift
@@ -0,0 +1,75 @@
+//
+// Transition.swift
+// Adwaita
+//
+// Created by david-swift on 21.01.24.
+//
+
+import CAdw
+
+/// A transition for a stack.
+public enum Transition: Int {
+
+ // swiftlint:disable missing_docs discouraged_none_name
+ case none
+ case crossfade
+ case slideRight, slideLeft, slideUp, slideDown, slideLeftRight, slideUpDown
+ case coverUp, coverDown, coverLeft, coverRight
+ case uncoverUp, uncoverDown, uncoverLeft, uncoverRight
+ case coverUpDown, coverDownUp, coverLeftRight, coverRightLeft
+ case rotateLeft, rotateRight, rotateLeftRight
+ // swiftlint:enable missing_docs discouraged_none_name
+
+ /// Get the GtkStackTransitionType transition.
+ public var cTransition: GtkStackTransitionType {
+ switch self {
+ case .none:
+ return GTK_STACK_TRANSITION_TYPE_NONE
+ case .crossfade:
+ return GTK_STACK_TRANSITION_TYPE_CROSSFADE
+ case .slideRight:
+ return GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT
+ case .slideLeft:
+ return GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT
+ case .slideUp:
+ return GTK_STACK_TRANSITION_TYPE_SLIDE_UP
+ case .slideDown:
+ return GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN
+ case .slideLeftRight:
+ return GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT
+ case .slideUpDown:
+ return GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN
+ case .coverUp:
+ return GTK_STACK_TRANSITION_TYPE_OVER_UP
+ case .coverDown:
+ return GTK_STACK_TRANSITION_TYPE_OVER_DOWN
+ case .coverLeft:
+ return GTK_STACK_TRANSITION_TYPE_OVER_LEFT
+ case .coverRight:
+ return GTK_STACK_TRANSITION_TYPE_OVER_RIGHT
+ case .uncoverUp:
+ return GTK_STACK_TRANSITION_TYPE_UNDER_UP
+ case .uncoverDown:
+ return GTK_STACK_TRANSITION_TYPE_UNDER_DOWN
+ case .uncoverLeft:
+ return GTK_STACK_TRANSITION_TYPE_UNDER_LEFT
+ case .uncoverRight:
+ return GTK_STACK_TRANSITION_TYPE_UNDER_RIGHT
+ case .coverUpDown:
+ return GTK_STACK_TRANSITION_TYPE_OVER_UP_DOWN
+ case .coverDownUp:
+ return GTK_STACK_TRANSITION_TYPE_OVER_DOWN_UP
+ case .coverLeftRight:
+ return GTK_STACK_TRANSITION_TYPE_OVER_LEFT_RIGHT
+ case .coverRightLeft:
+ return GTK_STACK_TRANSITION_TYPE_OVER_RIGHT_LEFT
+ case .rotateLeft:
+ return GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT
+ case .rotateRight:
+ return GTK_STACK_TRANSITION_TYPE_ROTATE_RIGHT
+ case .rotateLeftRight:
+ return GTK_STACK_TRANSITION_TYPE_ROTATE_LEFT_RIGHT
+ }
+ }
+
+}
diff --git a/Sources/Adwaita/Model/Extensions/Array.swift b/Sources/Adwaita/Model/Extensions/Array.swift
index bdedc60..778fba7 100644
--- a/Sources/Adwaita/Model/Extensions/Array.swift
+++ b/Sources/Adwaita/Model/Extensions/Array.swift
@@ -5,7 +5,7 @@
// Created by david-swift on 06.08.23.
//
-import Libadwaita
+import Foundation
extension Array: View where Element == View {
@@ -22,7 +22,7 @@ extension Array: View where Element == View {
var modified = self
for (index, view) in modified.enumerated() {
for modifier in modifiers {
- modified[index] = modifier(view)
+ modified[safe: index] = modifier(view)
}
}
return VStack { modified }
@@ -53,6 +53,28 @@ extension Array where Element == WindowSceneGroup {
}
+extension Array where Element == String {
+
+ /// Get the C version of the array.
+ var cArray: UnsafePointer?>? {
+ let cStrings = self.map { $0.utf8CString }
+ let cStringPointers = cStrings.map { $0.withUnsafeBufferPointer { $0.baseAddress } }
+ let optionalCStringPointers = cStringPointers + [nil]
+ var optionalCStringPointersCopy = optionalCStringPointers
+ optionalCStringPointersCopy.withUnsafeMutableBufferPointer { bufferPointer in
+ bufferPointer.baseAddress?.advanced(by: cStrings.count).pointee = nil
+ }
+ let flatArray = optionalCStringPointersCopy.compactMap { $0 }
+ let pointer = UnsafeMutablePointer?>.allocate(capacity: flatArray.count + 1)
+ for (index, element) in flatArray.enumerated() {
+ pointer.advanced(by: index).pointee = element
+ }
+ pointer.advanced(by: flatArray.count).pointee = nil
+ return UnsafePointer(pointer)
+ }
+
+}
+
extension Array {
/// Accesses the element at the specified position safely.
diff --git a/Sources/Adwaita/Model/Extensions/Bool.swift b/Sources/Adwaita/Model/Extensions/Bool.swift
new file mode 100644
index 0000000..ea35d6f
--- /dev/null
+++ b/Sources/Adwaita/Model/Extensions/Bool.swift
@@ -0,0 +1,15 @@
+//
+// Bool.swift
+// Adwaita
+//
+// Created by david-swift on 15.01.24.
+//
+
+extension Bool {
+
+ /// Get the gboolean for C.
+ public var cBool: Int32 {
+ self ? 1 : 0
+ }
+
+}
diff --git a/Sources/Adwaita/Model/Extensions/Int.swift b/Sources/Adwaita/Model/Extensions/Int.swift
new file mode 100644
index 0000000..580036b
--- /dev/null
+++ b/Sources/Adwaita/Model/Extensions/Int.swift
@@ -0,0 +1,17 @@
+//
+// Int.swift
+// Adwaita
+//
+// Created by david-swift on 15.01.24.
+//
+
+extension Int: Identifiable {
+
+ /// Get the integer itself as the identifier.
+ public var id: Int { self }
+ /// The C integer.
+ public var cInt: Int32 {
+ .init(self)
+ }
+
+}
diff --git a/Sources/Adwaita/Model/Extensions/Libadwaita.FileDialog.swift b/Sources/Adwaita/Model/Extensions/Libadwaita.FileDialog.swift
deleted file mode 100644
index ac8c008..0000000
--- a/Sources/Adwaita/Model/Extensions/Libadwaita.FileDialog.swift
+++ /dev/null
@@ -1,74 +0,0 @@
-//
-// Libadwaita.FileDialog.swift
-// Adwaita
-//
-// Created by david-swift on 09.12.23.
-//
-
-import Foundation
-import Libadwaita
-
-extension Libadwaita.FileDialog: WindowType {
-
- /// An ID for the importer field.
- static var importer: String { "importer" }
- /// An ID for the folder field.
- static var folder: String { "folder" }
- /// An ID for the result field.
- static var result: String { "result" }
- /// An ID for the cancel field.
- static var cancel: String { "cancel" }
-
- /// Whether the file dialog is an importer.
- var isImporter: Bool {
- get {
- fields[Self.importer] as? Bool ?? true
- }
- set {
- fields[Self.importer] = newValue
- }
- }
- /// The selected folder in the file dialog.
- var folder: URL? {
- get {
- fields[Self.folder] as? URL
- }
- set {
- fields[Self.folder] = newValue
- }
- }
- /// A closure triggered on selecting a file in the dialog.
- var onResult: ((URL) -> Void) {
- get {
- fields[Self.result] as? (URL) -> Void ?? { _ in }
- }
- set {
- fields[Self.result] = newValue
- }
- }
- /// A closure triggered when the dialog is canceled.
- var onCancel: (() -> Void) {
- get {
- fields[Self.cancel] as? () -> Void ?? { }
- }
- set {
- fields[Self.cancel] = newValue
- }
- }
-
- /// Set the window's parent window.
- /// - Parameter parent: The parent window.
- ///
- /// Currently not implemented.
- public func setParentWindow(_ parent: WindowType) { }
-
- /// Display the file dialog.
- public func show() {
- if isImporter {
- self.open(folder: folder, onResult, onClose: onCancel)
- } else {
- self.save(folder: folder, onResult, onClose: onCancel)
- }
- }
-
-}
diff --git a/Sources/Adwaita/Model/Extensions/NativeWidgetPeer.swift b/Sources/Adwaita/Model/Extensions/NativeWidgetPeer.swift
deleted file mode 100644
index 6178c2e..0000000
--- a/Sources/Adwaita/Model/Extensions/NativeWidgetPeer.swift
+++ /dev/null
@@ -1,37 +0,0 @@
-//
-// NativeWidgetPeer.swift
-// Adwaita
-//
-// Created by david-swift on 05.08.23.
-//
-
-import Libadwaita
-
-extension NativeWidgetPeer: Widget {
-
- /// A `Libadwaita.NativeWidgetPeer` is static.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) { }
-
- /// A `Libadwaita.NativeWidgetPeer`'s container is itself.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let bold = "\(modifier(code: 1))"
- let yellow = 33
- let warning = "\(modifier(code: yellow))\(bold)"
- let reset = modifier(code: 0)
- print("\(warning)warning: \(reset)discouraged use of GTUI widgets (here: \(bold)\(Self.self)\(reset)) in views")
- return .init(self)
- }
-
- /// Get a modifier stirng.
- /// - Parameter code: The modifier.
- /// - Returns: The string.
- private func modifier(code: Int) -> String {
- "\u{001B}[\(code)m"
- }
-
-}
diff --git a/Sources/Adwaita/Model/Extensions/OpaquePointer.swift b/Sources/Adwaita/Model/Extensions/OpaquePointer.swift
new file mode 100644
index 0000000..b594bfd
--- /dev/null
+++ b/Sources/Adwaita/Model/Extensions/OpaquePointer.swift
@@ -0,0 +1,16 @@
+//
+// OpaquePointer.swift
+// Adwaita
+//
+// Created by david-swift on 15.01.23.
+//
+
+extension OpaquePointer {
+
+ /// Convert an opaque pointer into an unsafe mutable pointer with a defined type.
+ /// - Returns: The unsafe mutable pointer.
+ public func cast() -> UnsafeMutablePointer {
+ .init(self)
+ }
+
+}
diff --git a/Sources/Adwaita/Model/Extensions/Set.swift b/Sources/Adwaita/Model/Extensions/Set.swift
new file mode 100644
index 0000000..7df5c9c
--- /dev/null
+++ b/Sources/Adwaita/Model/Extensions/Set.swift
@@ -0,0 +1,35 @@
+//
+// Set.swift
+// Adwaita
+//
+// Created by david-swift on 21.01.24.
+//
+
+extension Set where Element == Edge {
+
+ /// Horizontal and vertical edges.
+ public static var all: Self { vertical.union(horizontal) }
+
+ /// Top and bottom edges.
+ public static var vertical: Self { top.union(bottom) }
+
+ /// Leading and trailing edges.
+ public static var horizontal: Self { leading.union(trailing) }
+
+ /// Top edge.
+ public static var top: Self { [.top] }
+
+ /// Bottom edge.
+ public static var bottom: Self { [.bottom] }
+
+ /// Leading edge.
+ public static var leading: Self { [.leading] }
+
+ /// Trailing edge.
+ public static var trailing: Self { [.trailing] }
+
+ /// Add a collection of edges to a collection of edges.
+ /// - Parameter edges: The collection of edges.
+ /// - Returns: Both collections combined.
+ public func add(_ edges: Self) -> Self { union(edges) }
+}
diff --git a/Sources/Adwaita/Model/Extensions/String.swift b/Sources/Adwaita/Model/Extensions/String.swift
index d922699..1f76efe 100644
--- a/Sources/Adwaita/Model/Extensions/String.swift
+++ b/Sources/Adwaita/Model/Extensions/String.swift
@@ -14,4 +14,28 @@ extension String {
/// A label for the navigation label in a GTUI widget's fields.
static var navigationLabel: Self { "navigation-label" }
+ /// Add the Ctrl key to a shortcut.
+ /// - Returns: The shortcut.
+ public func ctrl() -> String { "\(self)" }
+
+ /// Add the Shift key to a shortcut.
+ /// - Returns: The shortcut.
+ public func shift() -> String { "\(self)" }
+
+ /// Add the Alt key to a shortcut.
+ /// - Returns: The shortcut.
+ public func alt() -> String { "\(self)" }
+
+ /// Add the Meta key to a shortcut.
+ /// - Returns: The shortcut.
+ public func meta() -> String { "\(self)" }
+
+ /// Add the Super key to a shortcut.
+ /// - Returns: The shortcut.
+ public func `super`() -> String { "\(self)" }
+
+ /// Add the Hyper key to a shortcut.
+ /// - Returns: The shortcut.
+ public func hyper() -> String { "\(self)" }
+
}
diff --git a/Sources/Adwaita/Model/Extensions/UInt.swift b/Sources/Adwaita/Model/Extensions/UInt.swift
new file mode 100644
index 0000000..e417c3d
--- /dev/null
+++ b/Sources/Adwaita/Model/Extensions/UInt.swift
@@ -0,0 +1,15 @@
+//
+// UInt.swift
+// Adwaita
+//
+// Created by david-swift on 19.01.24.
+//
+
+extension UInt {
+
+ /// Convert an unsigned integer into the C form.
+ public var cInt: UInt32 {
+ .init(self)
+ }
+
+}
diff --git a/Sources/Adwaita/Model/Extensions/UnsafeMutablePointer.swift b/Sources/Adwaita/Model/Extensions/UnsafeMutablePointer.swift
new file mode 100644
index 0000000..52475a2
--- /dev/null
+++ b/Sources/Adwaita/Model/Extensions/UnsafeMutablePointer.swift
@@ -0,0 +1,23 @@
+//
+// UnsafeMutablePointer.swift
+// Adwaita
+//
+// Created by david-swift on 15.01.24.
+//
+
+extension UnsafeMutablePointer {
+
+ /// Convert into an opaque pointer.
+ /// - Returns: The opaque pointer.
+ public func opaque() -> OpaquePointer {
+ .init(self)
+ }
+
+ /// Convert into an unsafe mutable pointer of another type.
+ /// - Returns: The unsafe mutable pointer.
+ public func cast() -> UnsafeMutablePointer {
+ let pointer = UnsafeMutableRawPointer(self).bindMemory(to: T.self, capacity: 1)
+ return UnsafeMutablePointer(mutating: pointer)
+ }
+
+}
diff --git a/Sources/Adwaita/Model/Extensions/UnsafeMutableRawPointer.swift b/Sources/Adwaita/Model/Extensions/UnsafeMutableRawPointer.swift
new file mode 100644
index 0000000..2ab2fdb
--- /dev/null
+++ b/Sources/Adwaita/Model/Extensions/UnsafeMutableRawPointer.swift
@@ -0,0 +1,17 @@
+//
+// UnsafeMutableRawPointer.swift
+// Adwaita
+//
+// Created by david-swift on 15.01.24.
+//
+
+extension UnsafeMutableRawPointer {
+
+ /// Convert into an unsafe mutable pointer of a certain type.
+ /// - Returns: The unsafe mutable pointer.
+ public func cast() -> UnsafeMutablePointer {
+ let pointer = UnsafeMutableRawPointer(self).bindMemory(to: T.self, capacity: 1)
+ return UnsafeMutablePointer(mutating: pointer)
+ }
+
+}
diff --git a/Sources/Adwaita/Model/User Interface/App/App.swift b/Sources/Adwaita/Model/User Interface/App/App.swift
index 95a5040..74dab3b 100644
--- a/Sources/Adwaita/Model/User Interface/App/App.swift
+++ b/Sources/Adwaita/Model/User Interface/App/App.swift
@@ -5,8 +5,6 @@
// Created by david-swift on 05.08.23.
//
-import Libadwaita
-
/// A structure conforming to `App` is the entry point of your app.
///
/// ```swift
diff --git a/Sources/Adwaita/Model/User Interface/App/GTUIApp.swift b/Sources/Adwaita/Model/User Interface/App/GTUIApp.swift
index 687d814..1828443 100644
--- a/Sources/Adwaita/Model/User Interface/App/GTUIApp.swift
+++ b/Sources/Adwaita/Model/User Interface/App/GTUIApp.swift
@@ -5,16 +5,21 @@
// Created by david-swift on 05.08.23.
//
-import Libadwaita
+import CAdw
/// The GTUI application.
-public class GTUIApp: Application {
+public class GTUIApp {
/// The handlers which are called when a state changes.
static var updateHandlers: [() -> Void] = []
/// The app's id for the file name for storing the data.
static var appID = "temporary"
+ /// The pointer to the application.
+ public var pointer: UnsafeMutablePointer?
+ /// Fields for additional information.
+ public var fields: [String: Any] = [:]
+
/// The app's content.
var body: () -> App
/// The scenes that are displayed.
@@ -28,11 +33,11 @@ public class GTUIApp: Application {
/// - body: The application's content.
init(_ id: String, body: @escaping () -> App) {
self.body = body
- super.init(name: id)
+ self.pointer = adw_application_new(id, G_APPLICATION_DEFAULT_FLAGS)?.cast()
}
/// The entry point of the application.
- override public func onActivate() {
+ public func onActivate() {
let body = body()
for windowScene in body.scene.windows() {
for _ in 0.. Void
+ ) {
+ let action = g_simple_action_new(id, nil)
+ let data = ViewStorage.SignalData(closure: handler)
+ g_signal_connect_data(
+ action?.cast(),
+ "activate",
+ unsafeBitCast(data.threeParamsHandler, to: GCallback.self),
+ Unmanaged.passUnretained(data).toOpaque().cast(),
+ nil,
+ G_CONNECT_AFTER
+ )
+ if let window {
+ g_action_map_add_action(.init(window.pointer), action)
+ window.fields[id] = data
+ } else {
+ g_action_map_add_action(.init(pointer), action)
+ fields[id] = data
+ }
+ gtk_application_set_accels_for_action(pointer, (window == nil ? "app." : "win.") + id, [shortcut].cArray)
+ }
+
/// Focus the window with a certain id. Create the window if it doesn't already exist.
/// - Parameters:
/// - id: The window's id.
@@ -75,4 +127,10 @@ public class GTUIApp: Application {
}
}
}
+
+ /// Terminate the application.
+ public func quit() {
+ g_application_quit(pointer?.cast())
+ }
+
}
diff --git a/Sources/Adwaita/Model/User Interface/Menu/MenuItem.swift b/Sources/Adwaita/Model/User Interface/Menu/MenuItem.swift
index 02edf5c..06e44ee 100644
--- a/Sources/Adwaita/Model/User Interface/Menu/MenuItem.swift
+++ b/Sources/Adwaita/Model/User Interface/Menu/MenuItem.swift
@@ -5,7 +5,7 @@
// Created by david-swift on 22.10.23.
//
-import Libadwaita
+import CAdw
/// A structure representing the content for a certain menu item type.
public protocol MenuItem: MenuItemGroup {
@@ -15,7 +15,7 @@ public protocol MenuItem: MenuItemGroup {
/// - menu: The menu.
/// - app: The application containing the menu.
/// - window: The application window containing the menu.
- func addMenuItem(menu: Libadwaita.Menu, app: GTUIApp, window: GTUIApplicationWindow?)
+ func addMenuItem(menu: OpaquePointer?, app: GTUIApp, window: GTUIApplicationWindow?)
}
diff --git a/Sources/Adwaita/Model/User Interface/Menu/MenuItemGroup.swift b/Sources/Adwaita/Model/User Interface/Menu/MenuItemGroup.swift
index 3d2ce38..8125e1d 100644
--- a/Sources/Adwaita/Model/User Interface/Menu/MenuItemGroup.swift
+++ b/Sources/Adwaita/Model/User Interface/Menu/MenuItemGroup.swift
@@ -5,7 +5,7 @@
// Created by david-swift on 22.10.23.
//
-import Libadwaita
+import CAdw
/// A structure conforming to `MenuItemGroup` can be added to the content accepting a menu.
public protocol MenuItemGroup {
@@ -19,7 +19,7 @@ extension MenuItemGroup {
/// Add the menu items described by the group to a menu.
/// - Parameter menu: The menu.
- func addMenuItems(menu: Libadwaita.Menu, app: GTUIApp, window: GTUIApplicationWindow?) {
+ func addMenuItems(menu: OpaquePointer?, app: GTUIApp, window: GTUIApplicationWindow?) {
for element in content {
if let item = element as? MenuItem {
item.addMenuItem(menu: menu, app: app, window: window)
diff --git a/Sources/Adwaita/Model/User Interface/View/View.swift b/Sources/Adwaita/Model/User Interface/View/View.swift
index 7870d99..223aa7a 100644
--- a/Sources/Adwaita/Model/User Interface/View/View.swift
+++ b/Sources/Adwaita/Model/User Interface/View/View.swift
@@ -5,8 +5,6 @@
// Created by david-swift on 05.08.23.
//
-import Libadwaita
-
/// A structure conforming to `View` is referred to as a view.
/// It can be part of a body.
///
@@ -37,13 +35,7 @@ extension View {
if let peer = modified as? Widget {
return peer
} else {
- var state: [String: StateProtocol] = [:]
- for property in Mirror(reflecting: self).children {
- if let label = property.label, let value = property.value as? StateProtocol {
- state[label] = value
- }
- }
- return StateWrapper(content: { view }, state: state)
+ return StateWrapper(content: { view }, state: getState())
}
}
@@ -56,8 +48,18 @@ extension View {
if let widget = modified as? Widget {
widget.update(storage, modifiers: modifiers)
} else {
- StateWrapper { self }.update(storage, modifiers: modifiers)
+ StateWrapper(content: { view }, state: getState()).update(storage, modifiers: modifiers)
+ }
+ }
+
+ func getState() -> [String: StateProtocol] {
+ var state: [String: StateProtocol] = [:]
+ for property in Mirror(reflecting: self).children {
+ if let label = property.label, let value = property.value as? StateProtocol {
+ state[label] = value
+ }
}
+ return state
}
/// Get a storage.
diff --git a/Sources/Adwaita/Model/User Interface/View/ViewStorage.swift b/Sources/Adwaita/Model/User Interface/View/ViewStorage.swift
index 281ad8b..ebce24e 100644
--- a/Sources/Adwaita/Model/User Interface/View/ViewStorage.swift
+++ b/Sources/Adwaita/Model/User Interface/View/ViewStorage.swift
@@ -5,31 +5,194 @@
// Created by david-swift on 31.08.23.
//
-import Libadwaita
+import CAdw
/// Store a rendered view in a view storage.
public class ViewStorage {
- /// The GTUI widget.
- public var view: NativeWidgetPeer
+ /// The pointer.
+ public var pointer: OpaquePointer?
/// The view's content.
public var content: [String: [ViewStorage]]
/// The view's state (used in `StateWrapper`).
public var state: [String: StateProtocol]
+ /// The signal handlers.
+ public var handlers: [String: SignalData] = [:]
+ /// Other properties.
+ public var fields: [String: Any] = [:]
/// Initialize a view storage.
/// - Parameters:
- /// - view: The GTUI widget.
+ /// - pointer: The pointer to the Gtk widget.
/// - content: The view's content.
/// - state: The view's state.
public init(
- _ view: NativeWidgetPeer,
+ _ pointer: OpaquePointer?,
content: [String: [ViewStorage]] = [:],
state: [String: StateProtocol] = [:]
) {
- self.view = view
+ self.pointer = pointer
self.content = content
self.state = state
}
+ /// Data to pass to signal handlers.
+ public class SignalData {
+
+ /// The closure.
+ public var closure: ([UnsafeMutableRawPointer]) -> Void
+
+ /// The closure as a C handler.
+ var handler: @convention(c) (UnsafeMutableRawPointer, UnsafeMutableRawPointer) -> Void {
+ { _, data in
+ let data = unsafeBitCast(data, to: SignalData.self)
+ data.closure([])
+ }
+ }
+
+ /// The closure as a C handler with three parameters.
+ var threeParamsHandler: @convention(c) (
+ UnsafeMutableRawPointer,
+ UnsafeMutableRawPointer,
+ UnsafeMutableRawPointer
+ ) -> Void {
+ { _, arg1, data in
+ let data = unsafeBitCast(data, to: SignalData.self)
+ data.closure([arg1])
+ }
+ }
+
+ /// The closure as a C handler with four parameters.
+ var fourParamsHandler: @convention(c) (
+ UnsafeMutableRawPointer,
+ UnsafeMutableRawPointer,
+ UnsafeMutableRawPointer,
+ UnsafeMutableRawPointer
+ ) -> Void {
+ { _, arg1, arg2, data in
+ let data = unsafeBitCast(data, to: SignalData.self)
+ data.closure([arg1, arg2])
+ }
+ }
+
+ /// The closure as a C handler with five parameters.
+ var fiveParamsHandler: @convention(c) (
+ UnsafeMutableRawPointer,
+ UnsafeMutableRawPointer,
+ UnsafeMutableRawPointer,
+ UnsafeMutableRawPointer,
+ UnsafeMutableRawPointer
+ ) -> Void {
+ { _, arg1, arg2, arg3, data in
+ let data = unsafeBitCast(data, to: SignalData.self)
+ data.closure([arg1, arg2, arg3])
+ print("Hi")
+ }
+ }
+
+ /// Initialize the signal data.
+ /// - Parameter closure: The signal's closure.
+ public convenience init(closure: @escaping () -> Void) {
+ self.init { _ in closure() }
+ }
+
+ /// Initialize the signal data.
+ /// - Parameter closure: The signal's closure.
+ public init(closure: @escaping ([UnsafeMutableRawPointer]) -> Void) {
+ self.closure = closure
+ }
+
+ }
+
+ /// Connect a handler to the observer of a property.
+ /// - Parameters:
+ /// - name: The property's name.
+ /// - id: The handlers id to separate form others connecting to the signal.
+ /// - connectFlags: The GConnectFlags.
+ /// - handler: The signal's handler.
+ public func notify(
+ name: String,
+ id: String = "",
+ connectFlags: GConnectFlags = G_CONNECT_AFTER,
+ handler: @escaping () -> Void
+ ) {
+ let name = "notify::" + name
+ connectSignal(name: name, id: id, connectFlags: connectFlags, argCount: 1, handler: handler)
+ }
+
+ /// Connect a handler to a signal.
+ /// - Parameters:
+ /// - name: The signal's name.
+ /// - id: The handlers id to separate form others connecting to the signal.
+ /// - connectFlags: The GConnectFlags.
+ /// - argCount: The number of additional arguments (without the first and the last one).
+ /// - handler: The signal's handler.
+ public func connectSignal(
+ name: String,
+ id: String = "",
+ connectFlags: GConnectFlags = G_CONNECT_AFTER,
+ argCount: Int = 0,
+ handler: @escaping () -> Void
+ ) {
+ connectSignal(name: name, id: id, connectFlags: connectFlags, argCount: argCount) { _ in
+ handler()
+ }
+ }
+
+ /// Connect a handler to a signal.
+ /// - Parameters:
+ /// - name: The signal's name.
+ /// - id: The handlers id to separate form others connecting to the signal.
+ /// - connectFlags: The GConnectFlags.
+ /// - argCount: The number of additional arguments (without the first and the last one).
+ /// - handler: The signal's handler.
+ public func connectSignal(
+ name: String,
+ id: String = "",
+ connectFlags: GConnectFlags = G_CONNECT_AFTER,
+ argCount: Int = 0,
+ handler: @escaping ([UnsafeMutableRawPointer]) -> Void
+ ) {
+ if let data = handlers[name + id] {
+ data.closure = handler
+ } else {
+ let data = SignalData(closure: handler)
+ handlers[name + id] = data
+ let callback: GCallback
+ let three = 3
+ let two = 2
+ if argCount >= three {
+ callback = unsafeBitCast(data.fiveParamsHandler, to: GCallback.self)
+ } else if argCount == two {
+ callback = unsafeBitCast(data.fourParamsHandler, to: GCallback.self)
+ } else if argCount == 1 {
+ callback = unsafeBitCast(data.threeParamsHandler, to: GCallback.self)
+ } else {
+ callback = unsafeBitCast(data.handler, to: GCallback.self)
+ }
+ g_signal_connect_data(
+ pointer?.cast(),
+ name,
+ callback,
+ Unmanaged.passUnretained(data).toOpaque().cast(),
+ nil,
+ connectFlags
+ )
+ }
+ }
+
+ /// Modify the view.
+ /// - Parameter modify: The modification function.
+ public func modify(_ modify: (OpaquePointer?) -> Void) {
+ modify(pointer)
+ }
+
+ /// Convert the pointer to a pointer of a certain type and modify the view.
+ /// - Parameters:
+ /// - type: The pointer's type.
+ /// - modify: The modification function.
+ public func modify(_ type: T.Type, _ modify: (UnsafeMutablePointer?) -> Void) {
+ modify(pointer?.cast())
+ }
+
}
diff --git a/Sources/Adwaita/Model/User Interface/View/Widget.swift b/Sources/Adwaita/Model/User Interface/View/Widget.swift
index 11372b5..f12a2bb 100644
--- a/Sources/Adwaita/Model/User Interface/View/Widget.swift
+++ b/Sources/Adwaita/Model/User Interface/View/Widget.swift
@@ -5,8 +5,6 @@
// Created by david-swift on 16.08.23.
//
-import Libadwaita
-
/// A widget is a view that know about its GTUI widget.
public protocol Widget: View {
diff --git a/Sources/Adwaita/Model/User Interface/Window/GTUIAboutWindow.swift b/Sources/Adwaita/Model/User Interface/Window/GTUIAboutWindow.swift
new file mode 100644
index 0000000..a3ac51b
--- /dev/null
+++ b/Sources/Adwaita/Model/User Interface/Window/GTUIAboutWindow.swift
@@ -0,0 +1,49 @@
+//
+// GTUIAboutWindow.swift
+// Adwaita
+//
+// Created by david-swift on 21.01.24.
+//
+
+import CAdw
+
+/// A GTUI about window.
+public class GTUIAboutWindow: GTUIWindow {
+
+ /// Initialize an about window using the AppStream metadata.
+ /// - Parameter filePath: The path.
+ public init(filePath: String? = nil) {
+ super.init(fields: [:])
+ if let filePath {
+ pointer = adw_about_window_new_from_appdata(filePath, nil)?.cast()
+ } else {
+ pointer = adw_about_window_new()?.cast()
+ }
+ }
+
+ /// Set the general data.
+ /// - Parameters:
+ /// - title: The app name.
+ /// - icon: The app icon.
+ /// - developer: The app's developer.
+ /// - version: The app's version.
+ public func generalData(title: String, icon: Icon, developer: String, version: String) {
+ adw_about_window_set_application_name(.init(pointer), title)
+ adw_about_window_set_application_icon(.init(pointer), icon.string)
+ adw_about_window_set_developer_name(.init(pointer), developer)
+ adw_about_window_set_version(.init(pointer), version)
+ }
+
+ /// Set the website.
+ /// - Parameter url: The website.
+ public func website(url: String) {
+ adw_about_window_set_website(.init(pointer), url)
+ }
+
+ /// Set the URL for issues.
+ /// - Parameter issues: The issues website.
+ public func issues(url: String) {
+ adw_about_window_set_issue_url(.init(pointer), url)
+ }
+
+}
diff --git a/Sources/Adwaita/Model/User Interface/Window/GTUIApplicationWindow.swift b/Sources/Adwaita/Model/User Interface/Window/GTUIApplicationWindow.swift
index 4a997a2..ec1863b 100644
--- a/Sources/Adwaita/Model/User Interface/Window/GTUIApplicationWindow.swift
+++ b/Sources/Adwaita/Model/User Interface/Window/GTUIApplicationWindow.swift
@@ -5,7 +5,35 @@
// Created by david-swift on 19.10.23.
//
-import Libadwaita
+import CAdw
/// A GTUI application window.
-public typealias GTUIApplicationWindow = Libadwaita.ApplicationWindow
+public class GTUIApplicationWindow: GTUIWindow {
+
+ /// The window's parent app.
+ public var app: GTUIApp
+
+ /// Initialize the application window.
+ /// - Parameter app: The application.
+ public init(app: GTUIApp) {
+ self.app = app
+ super.init(fields: [:])
+ pointer = adw_application_window_new(app.pointer)?.cast()
+ }
+
+ /// Add a keyboard shortcut.
+ /// - Parameters:
+ /// - shortcut: The keyboard shortcut.
+ /// - id: The action's id.
+ /// - handler: The action's handler.
+ public func addKeyboardShortcut(_ shortcut: String, id: String, handler: @escaping () -> Void) {
+ app.addKeyboardShortcut(shortcut, id: id, window: self, handler: handler)
+ }
+
+ /// Set the window's child.
+ /// - Parameter child: The child.
+ override public func setChild(_ child: OpaquePointer?) {
+ adw_application_window_set_content(pointer?.cast(), child?.cast())
+ }
+
+}
diff --git a/Sources/Adwaita/Model/User Interface/Window/GTUIFileDialog.swift b/Sources/Adwaita/Model/User Interface/Window/GTUIFileDialog.swift
new file mode 100644
index 0000000..b17a813
--- /dev/null
+++ b/Sources/Adwaita/Model/User Interface/Window/GTUIFileDialog.swift
@@ -0,0 +1,138 @@
+//
+// GTUIFileDialog.swift
+// Adwaita
+//
+// Created by david-swift on 09.12.23.
+//
+
+import CAdw
+import Foundation
+
+/// A GTUI file dialog window.
+public class GTUIFileDialog: WindowType {
+
+ /// The file dialog's pointer.
+ public var pointer: OpaquePointer?
+ /// Fields for additional data.
+ public var fields: [String: Any] = [:]
+ /// A link to the file dialog.
+ var selfAddr: UInt64 {
+ unsafeBitCast(self, to: UInt64.self)
+ }
+ /// The parent window.
+ var parent: OpaquePointer?
+ /// Whether the file dialog is an importer.
+ var isImporter = false
+ /// The selected folder in the file dialog.
+ var folder: URL?
+ /// A closure triggered on selecting a file in the dialog.
+ var onResult: (URL) -> Void = { _ in }
+ /// A closure triggered when the dialog is canceled.
+ var onCancel: () -> Void = { }
+
+ /// Initialize the window.
+ public init() {
+ pointer = gtk_file_dialog_new()
+ }
+
+ /// Set the window's parent window.
+ /// - Parameter parent: The parent window.
+ public func setParentWindow(_ parent: WindowType) {
+ if let window = parent as? GTUIWindow {
+ self.parent = .init(window.pointer)
+ }
+ }
+
+ /// Set the initial name.
+ /// - Parameter name: The parent window.
+ public func setInitialName(_ name: String) {
+ gtk_file_dialog_set_initial_name(pointer, name)
+ }
+
+ // swiftlint:disable discouraged_optional_collection
+ /// Set the allowed file extensions.
+ /// - Parameters:
+ /// - extensions: The file extensions.
+ public func setExtensions(_ extensions: [String]?) {
+ if let extensions {
+ let filter = gtk_file_filter_new()
+ for name in extensions {
+ gtk_file_filter_add_suffix(filter, name)
+ }
+ gtk_file_dialog_set_default_filter(pointer, filter)
+ } else {
+ gtk_file_dialog_set_default_filter(pointer, nil)
+ }
+ }
+ // swiftlint:enable discouraged_optional_collection
+
+ /// Display the file dialog.
+ public func show() {
+ if let folder {
+ gtk_file_dialog_set_initial_folder(pointer, g_file_new_for_path(folder.absoluteString))
+ }
+ if isImporter {
+ gtui_filedialog_open(UInt64(Int(bitPattern: pointer)), selfAddr, UInt64(Int(bitPattern: parent)))
+ } else {
+ gtui_filedialog_save(UInt64(Int(bitPattern: pointer)), selfAddr, UInt64(Int(bitPattern: parent)))
+ }
+ }
+
+ /// Run this when a file gets opened.
+ /// - Parameter path: The file path.
+ func onOpen(_ path: String) {
+ let url = URL(fileURLWithPath: path)
+ onResult(url)
+ }
+
+ /// Run this when a file gets saved.
+ /// - Parameter path: The file path.
+ func onSave(_ path: String) {
+ let url = URL(fileURLWithPath: path)
+ onResult(url)
+ }
+
+ /// Run this when the user cancels the action.
+ func onClose() {
+ onCancel()
+ }
+
+}
+
+/// Run when a file should be opened.
+/// - Parameters:
+/// - ptr: The pointer.
+/// - file: The path to the file.
+/// - userData: The file dialog data.
+@_cdecl("filedialog_on_open_cb")
+func filedialog_on_open_cb(
+ ptr: UnsafeMutableRawPointer,
+ file: UnsafePointer?,
+ userData: UnsafeMutableRawPointer
+) {
+ let dialog = Unmanaged.fromOpaque(userData).takeUnretainedValue()
+ if let file {
+ dialog.onOpen(.init(cString: file))
+ } else {
+ dialog.onClose()
+ }
+}
+
+/// Run when a file should be saved.
+/// - Parameters:
+/// - ptr: The pointer.
+/// - file: The path to the file.
+/// - userData: The file dialog data.
+@_cdecl("filedialog_on_save_cb")
+func filedialog_on_save_cb(
+ ptr: UnsafeMutableRawPointer,
+ file: UnsafePointer?,
+ userData: UnsafeMutableRawPointer
+) {
+ let dialog = Unmanaged.fromOpaque(userData).takeUnretainedValue()
+ if let file {
+ dialog.onSave(.init(cString: file))
+ } else {
+ dialog.onClose()
+ }
+}
diff --git a/Sources/Adwaita/Model/User Interface/Window/GTUIWindow.swift b/Sources/Adwaita/Model/User Interface/Window/GTUIWindow.swift
index 4fe9e92..9ad8715 100644
--- a/Sources/Adwaita/Model/User Interface/Window/GTUIWindow.swift
+++ b/Sources/Adwaita/Model/User Interface/Window/GTUIWindow.swift
@@ -5,19 +5,93 @@
// Created by david-swift on 12.10.23.
//
-import Libadwaita
+import CAdw
/// A GTUI window.
-public typealias GTUIWindow = Libadwaita.Window
+public class GTUIWindow: WindowType {
-extension GTUIWindow: WindowType {
+ /// The window's pointer.
+ public var pointer: UnsafeMutablePointer?
+ /// Fields for additional information.
+ public var fields: [String: Any] = [:]
+
+ /// Initialize the window.
+ public init() {
+ pointer = adw_window_new()?.cast()
+ }
+
+ /// Initialize the window, but not the pointer.
+ /// - Parameter fields: The fields.
+ init(fields: [String: Any]) {
+ self.fields = fields
+ }
+
+ /// Set the default window size.
+ /// - Parameters:
+ /// - width: The width.
+ /// - height: The height.
+ public func setDefaultSize(width: Int?, height: Int?) {
+ gtk_window_set_default_size(pointer, width?.cInt ?? -1, height?.cInt ?? -1)
+ }
+
+ /// Set the resizability.
+ /// - Parameter resizable: Whether the window is resizable.
+ public func setResizability(_ resizable: Bool) {
+ gtk_window_set_resizable(pointer, resizable.cBool)
+ }
+
+ /// Set the deletability.
+ /// - Parameter deletable: Whether the window is deletable.
+ public func setDeletability(_ deletable: Bool) {
+ gtk_window_set_deletable(pointer, deletable.cBool)
+ }
+
+ /// Set the window title.
+ /// - Parameter title: The window's title.
+ public func setTitle(_ title: String) {
+ gtk_window_set_title(pointer, title)
+ }
+
+ /// Set the window's child.
+ /// - Parameter child: The child.
+ public func setChild(_ child: OpaquePointer?) {
+ gtk_window_set_child(pointer, child?.cast())
+ }
+
+ /// Present the window.
+ public func show() {
+ gtk_window_present(pointer)
+ }
+
+ /// Observe when the window is being closed.
+ /// - Parameter observer: The signal closure.
+ public func observeHide(observer: @escaping () -> Void) {
+ let hideObserver = ViewStorage.SignalData(closure: observer)
+ self.fields["observe-hide"] = hideObserver
+ g_signal_connect_data(
+ pointer?.cast(),
+ "destroy",
+ unsafeBitCast(hideObserver.handler, to: GCallback.self),
+ Unmanaged.passUnretained(hideObserver).toOpaque().cast(),
+ nil,
+ G_CONNECT_AFTER
+ )
+ }
+
+ /// Close the window.
+ public func close() {
+ gtk_window_close(pointer)
+ }
/// Set the window's parent window.
/// - Parameter parent: The parent window.
public func setParentWindow(_ parent: WindowType) {
+ // swiftlint:disable prefer_self_in_static_references
if let window = parent as? GTUIWindow {
- self.setParent(window)
+ gtk_window_set_modal(pointer, 1)
+ gtk_window_set_transient_for(pointer, window.pointer)
}
+ // swiftlint:enable prefer_self_in_static_references
}
}
diff --git a/Sources/Adwaita/Model/User Interface/Window/WindowScene.swift b/Sources/Adwaita/Model/User Interface/Window/WindowScene.swift
index b386f12..fbed105 100644
--- a/Sources/Adwaita/Model/User Interface/Window/WindowScene.swift
+++ b/Sources/Adwaita/Model/User Interface/Window/WindowScene.swift
@@ -5,8 +5,6 @@
// Created by david-swift on 05.08.23.
//
-import Libadwaita
-
/// A structure representing the content for a certain window type.
public protocol WindowScene: WindowSceneGroup {
diff --git a/Sources/Adwaita/Model/User Interface/Window/WindowStorage.swift b/Sources/Adwaita/Model/User Interface/Window/WindowStorage.swift
index 07567c3..4cd6cee 100644
--- a/Sources/Adwaita/Model/User Interface/Window/WindowStorage.swift
+++ b/Sources/Adwaita/Model/User Interface/Window/WindowStorage.swift
@@ -5,8 +5,6 @@
// Created by david-swift on 31.08.23.
//
-import Libadwaita
-
/// A storage for an app's window.
public class WindowStorage {
diff --git a/Sources/Adwaita/Model/User Interface/Window/WindowType.swift b/Sources/Adwaita/Model/User Interface/Window/WindowType.swift
index 283c154..e2b3204 100644
--- a/Sources/Adwaita/Model/User Interface/Window/WindowType.swift
+++ b/Sources/Adwaita/Model/User Interface/Window/WindowType.swift
@@ -5,8 +5,6 @@
// Created by david-swift on 09.12.23.
//
-import Libadwaita
-
/// A window type.
public protocol WindowType {
diff --git a/Sources/Adwaita/View/Banner+.swift b/Sources/Adwaita/View/Banner+.swift
new file mode 100644
index 0000000..064c513
--- /dev/null
+++ b/Sources/Adwaita/View/Banner+.swift
@@ -0,0 +1,33 @@
+//
+// Banner+.swift
+// Adwaita
+//
+// Created by david-swift on 17.01.24.
+//
+
+import CAdw
+
+extension Banner {
+
+ /// Initialize a text widget.
+ /// - Parameters:
+ /// - title: The content.
+ /// - visible: Whether the banner is visible.
+ public init(_ title: String, visible: Bool) {
+ self.init(title: title)
+ self = self.revealed(visible)
+ }
+
+ /// Configure the banner's button.
+ /// - Parameters:
+ /// - label: The button's title.
+ /// - handler: The button's handler.
+ /// - Returns: The banner.
+ public func button(_ label: String, handler: @escaping () -> Void) -> Self {
+ buttonLabel(label)
+ .buttonClicked {
+ handler()
+ }
+ }
+
+}
diff --git a/Sources/Adwaita/View/Banner.swift b/Sources/Adwaita/View/Banner.swift
deleted file mode 100644
index 96ee65d..0000000
--- a/Sources/Adwaita/View/Banner.swift
+++ /dev/null
@@ -1,76 +0,0 @@
-//
-// Banner.swift
-// Adwaita
-//
-// Created by david-swift on 03.01.24.
-//
-
-import Libadwaita
-
-/// A banner widget.
-public struct Banner: Widget {
-
- /// The content.
- var title: String
- /// Whether the banner is visible.
- var visible: Bool
- /// The button's label.
- var buttonLabel: String?
- /// The button's handler.
- var handler: () -> Void = { }
-
- /// Initialize a text widget.
- /// - Parameters:
- /// - title: The content.
- /// - visible: Whether the banner is visible.
- public init(_ title: String, visible: Bool) {
- self.title = title
- self.visible = visible
- }
-
- /// Update the view storage of the text widget.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let banner = storage.view as? Libadwaita.Banner {
- update(banner: banner)
- }
- }
-
- /// Get the container of the text widget.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let banner = Libadwaita.Banner(title)
- update(banner: banner)
- return .init(banner)
- }
-
- /// Update the banner.
- /// - Parameter banner: The banner.
- func update(banner: Libadwaita.Banner) {
- _ = banner.title(title)
- if let buttonLabel {
- _ = banner.buttonLabel(buttonLabel)
- _ = banner.buttonHandler(handler)
- }
- if visible {
- banner.show()
- } else {
- banner.hide()
- }
- }
-
- /// Configure the banner's button.
- /// - Parameters:
- /// - label: The button's title.
- /// - handler: The button's handler.
- /// - Returns: The banner.
- public func button(_ label: String, handler: @escaping () -> Void) -> Self {
- var newSelf = self
- newSelf.buttonLabel = label
- newSelf.handler = handler
- return newSelf
- }
-
-}
diff --git a/Sources/Adwaita/View/Button+.swift b/Sources/Adwaita/View/Button+.swift
new file mode 100644
index 0000000..7cd8d78
--- /dev/null
+++ b/Sources/Adwaita/View/Button+.swift
@@ -0,0 +1,64 @@
+//
+// Button+.swift
+// Adwaita
+//
+// Created by david-swift on 15.01.24.
+//
+
+import CAdw
+
+/// A button widget.
+extension Button {
+
+ // swiftlint:disable function_default_parameter_at_end
+ /// Initialize a button.
+ /// - Parameters:
+ /// - label: The button's label.
+ /// - icon: The button's icon.
+ /// - handler: The button's action handler.
+ public init(_ label: String? = nil, icon: Icon, handler: @escaping () -> Void) {
+ self.init()
+ self = self.child {
+ ButtonContent()
+ .label(label)
+ .iconName(icon.string)
+ }
+ self = self.clicked(handler)
+ }
+ // swiftlint:enable function_default_parameter_at_end
+
+ /// Initialize a button.
+ /// - Parameters:
+ /// - label: The buttons label.
+ /// - handler: The button's action handler.
+ public init(_ label: String, handler: @escaping () -> Void) {
+ self.init()
+ self = self.label(label)
+ self = self.clicked(handler)
+ }
+
+ /// Create a keyboard shortcut for an application window 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.
+ /// - window: The application window.
+ /// - Returns: The button.
+ public func keyboardShortcut(_ shortcut: String, window: GTUIApplicationWindow) -> Self {
+ window.addKeyboardShortcut(shortcut, id: shortcut) { self.clicked?() }
+ return self
+ }
+
+ /// 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.
+ /// - window: The application.
+ /// - Returns: The button.
+ public func keyboardShortcut(_ shortcut: String, app: GTUIApp) -> Self {
+ app.addKeyboardShortcut(shortcut, id: shortcut) { self.clicked?() }
+ return self
+ }
+
+}
diff --git a/Sources/Adwaita/View/Button.swift b/Sources/Adwaita/View/Button.swift
deleted file mode 100644
index e872c1c..0000000
--- a/Sources/Adwaita/View/Button.swift
+++ /dev/null
@@ -1,98 +0,0 @@
-//
-// Button.swift
-// Adwaita
-//
-// Created by david-swift on 10.09.23.
-//
-
-import Libadwaita
-
-/// A button widget.
-public struct Button: Widget {
-
- /// The button's label.
- var label: String?
- /// The button's icon.
- var icon: Icon?
- /// The button's action handler.
- var handler: () -> Void
-
- // swiftlint:disable function_default_parameter_at_end
- /// Initialize a button.
- /// - Parameters:
- /// - label: The button's label.
- /// - icon: The button's icon.
- /// - handler: The button's action handler.
- public init(_ label: String? = nil, icon: Icon, handler: @escaping () -> Void) {
- self.label = label
- self.icon = icon
- self.handler = handler
- }
- // swiftlint:enable function_default_parameter_at_end
-
- /// Initialize a button.
- /// - Parameters:
- /// - label: The buttons label.
- /// - handler: The button's action handler.
- public init(_ label: String, handler: @escaping () -> Void) {
- self.label = label
- self.handler = handler
- }
-
- /// Update a button's view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let button = storage.view as? Libadwaita.Button {
- let content = button.getContent()
- if let label {
- if icon == nil {
- button.setLabel(label)
- } else {
- content?.setLabel(label)
- }
- }
- if let icon {
- content?.setIcon(icon)
- }
- _ = button.handler(handler)
- }
- }
-
- /// Get a button's view storage.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The button's view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- if let icon {
- return .init(Libadwaita.Button(label, icon: icon).handler(handler))
- } else {
- return .init(Libadwaita.Button(label ?? .init()).handler(handler))
- }
- }
-
- /// Create a keyboard shortcut for an application window 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.
- /// - window: The application window.
- /// - Returns: The button.
- public func keyboardShortcut(_ shortcut: String, window: GTUIApplicationWindow) -> Self {
- window.addKeyboardShortcut(shortcut, id: shortcut, handler: handler)
- return self
- }
-
- /// 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.
- /// - window: The application.
- /// - Returns: The button.
- public func keyboardShortcut(_ shortcut: String, app: GTUIApp) -> Self {
- app.addKeyboardShortcut(shortcut, id: shortcut, handler: handler)
- return self
- }
-
-}
diff --git a/Sources/Adwaita/View/Carousel+.swift b/Sources/Adwaita/View/Carousel+.swift
new file mode 100644
index 0000000..99b08e2
--- /dev/null
+++ b/Sources/Adwaita/View/Carousel+.swift
@@ -0,0 +1,17 @@
+//
+// Carousel+.swift
+// Adwaita
+//
+// Created by david-swift on 18.01.24.
+//
+
+extension Carousel {
+
+ /// Set whether long swipes are allowed or not.
+ /// - Parameter longSwipes: Whether long swipes are allowed.
+ /// - Returns: The carousel.
+ public func longSwipes(_ longSwipes: Bool = true) -> Self {
+ allowLongSwipes(longSwipes)
+ }
+
+}
diff --git a/Sources/Adwaita/View/Carousel.swift b/Sources/Adwaita/View/Carousel.swift
deleted file mode 100644
index ad04265..0000000
--- a/Sources/Adwaita/View/Carousel.swift
+++ /dev/null
@@ -1,49 +0,0 @@
-//
-// Carousel.swift
-// Adwaita
-//
-// Created by david-swift on 01.01.24.
-//
-
-import Libadwaita
-
-/// A carousel view.
-public struct Carousel: View where Element: Identifiable {
-
- /// The elements.
- var elements: [Element]
- /// The content.
- var content: (Element) -> Body
- /// Whether long swipes are allowed.
- var allowLongSwipes = false
-
- /// The view.
- public var view: Body {
- Container(elements, content: content) {
- Libadwaita.Carousel()
- }
- .inspect { _ = ($0 as? Libadwaita.Carousel)?.longSwipes(allowLongSwipes) }
- }
-
- /// Initialize `Carousel`.
- /// - Parameters:
- /// - elements: The elements.
- /// - content: The view for an element.
- public init(
- _ elements: [Element],
- @ViewBuilder content: @escaping (Element) -> Body
- ) {
- self.content = content
- self.elements = elements
- }
-
- /// Set whether long swipes are allowed or not.
- /// - Parameter longSwipes: Whether long swipes are allowed.
- /// - Returns: The carousel.
- public func longSwipes(_ longSwipes: Bool = true) -> Self {
- var newSelf = self
- newSelf.allowLongSwipes = longSwipes
- return newSelf
- }
-
-}
diff --git a/Sources/Adwaita/View/Container.swift b/Sources/Adwaita/View/Container.swift
deleted file mode 100644
index aceec96..0000000
--- a/Sources/Adwaita/View/Container.swift
+++ /dev/null
@@ -1,101 +0,0 @@
-//
-// Container.swift
-// Adwaita
-//
-// Created by david-swift on 01.01.24.
-//
-
-import LevenshteinTransformations
-import Libadwaita
-
-/// A container widget.
-public struct Container: Widget
-where Element: Identifiable, Type: InsertableContainer, Type: NativeWidgetPeer {
-
- /// The elements.
- var elements: [Element]
- /// The content.
- var content: (Element) -> Body
- /// Get the container for initialization.
- var container: () -> Type
-
- /// The identifier of the elements storage.
- let elementsID = "elements"
-
- /// Initialize `Container`.
- /// - Parameters:
- /// - elements: The elements.
- /// - content: The view for an element.
- /// - container: Get the initial Libadwaita container widget.
- public init(
- _ elements: [Element],
- @ViewBuilder content: @escaping (Element) -> Body,
- container: @escaping () -> Type
- ) {
- self.content = content
- self.elements = elements
- self.container = container
- }
-
- /// Update a view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let container = storage.view as? Type {
- var content: [ViewStorage] = storage.content[.mainContent] ?? []
- updateContainer(container, content: .init { content } set: { content = $0 }, modifiers: modifiers)
- storage.content[.mainContent] = content
- for (index, element) in elements.enumerated() {
- self.content(element).widget(modifiers: modifiers).update(content[index], modifiers: modifiers)
- }
- }
- }
-
- /// Get a view storage.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let container = self.container()
- var content: [ViewStorage] = []
- updateContainer(container, content: .init { content } set: { content = $0 }, modifiers: modifiers)
- return .init(container, content: [.mainContent: content])
- }
-
- /// Update the container's content.
- /// - Parameters:
- /// - container: The container.
- /// - content: The content's view storage.
- /// - modifiers: The view modifiers.
- func updateContainer(_ container: Type, content: Binding<[ViewStorage]>, modifiers: [(View) -> View]) {
- let old = container.fields[elementsID] as? [Element] ?? []
- old.identifiableTransform(
- to: elements,
- functions: .init { index, element in
- let widget = getWidget(element: element, modifiers: modifiers)
- _ = container.removeWidgets([content.wrappedValue[index].view])
- _ = container.insert(widget.view, at: index)
- content.wrappedValue.remove(at: index)
- content.wrappedValue.insert(widget, at: index)
- } delete: { index in
- _ = container.removeWidgets([content.wrappedValue[index].view])
- content.wrappedValue.remove(at: index)
- } insert: { index, element in
- let widget = getWidget(element: element, modifiers: modifiers)
- _ = container.insert(widget.view, at: index)
- content.wrappedValue.insert(widget, at: index)
- }
- )
- container.fields[elementsID] = elements
- }
-
- /// Get the view storage of an element.
- /// - Parameters:
- /// - element: The element.
- /// - modifiers: The modifiers.
- /// - Returns: The view storage.
- func getWidget(element: Element, modifiers: [(View) -> View]) -> ViewStorage {
- self.content(element).widget(modifiers: modifiers).container(modifiers: modifiers)
- }
-
-}
diff --git a/Sources/Adwaita/View/Forms/ActionRow+.swift b/Sources/Adwaita/View/Forms/ActionRow+.swift
new file mode 100644
index 0000000..0a4257d
--- /dev/null
+++ b/Sources/Adwaita/View/Forms/ActionRow+.swift
@@ -0,0 +1,17 @@
+//
+// ActionRow+.swift
+// Adwaita
+//
+// Created by david-swift on 20.01.24.
+//
+
+/// A form content row showing a title and optionally a subtitle and widgets.
+extension ActionRow {
+
+ /// Initialize an action row.
+ /// - Parameter title: The row's title.
+ public init(_ title: String) {
+ self = self.title(title)
+ }
+
+}
diff --git a/Sources/Adwaita/View/Forms/ActionRow.swift b/Sources/Adwaita/View/Forms/ActionRow.swift
deleted file mode 100644
index caa5168..0000000
--- a/Sources/Adwaita/View/Forms/ActionRow.swift
+++ /dev/null
@@ -1,99 +0,0 @@
-//
-// ActionRow.swift
-// Adwaita
-//
-// Created by david-swift on 03.01.24.
-//
-
-import Libadwaita
-
-/// A form content row showing a title and optionally a subtitle and widgets.
-public struct ActionRow: Widget {
-
- /// The title.
- var title: String
- /// The subtitle.
- var subtitle = ""
- /// The prefix.
- var prefix: Body = []
- /// The suffix.
- var suffix: Body = []
-
- /// The identifier for the prefix content.
- let prefixID = "prefix"
- /// The identifier for the suffix content.
- let suffixID = "suffix"
-
- /// Initialize an action row.
- /// - Parameter title: The row's title.
- public init(_ title: String) {
- self.title = title
- }
-
- /// Update a view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let row = storage.view as? Libadwaita.ActionRow {
- update(row: row)
- }
- if let prefixStorage = storage.content[prefixID]?.first {
- prefix.widget(modifiers: modifiers).update(prefixStorage, modifiers: modifiers)
- }
- if let suffixStorage = storage.content[suffixID]?.first {
- suffix.widget(modifiers: modifiers).update(suffixStorage, modifiers: modifiers)
- }
- }
-
- /// Get a view storage.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let row: Libadwaita.ActionRow = .init(title: title, subtitle: subtitle)
- let prefixContent = prefix.widget(modifiers: modifiers).container(modifiers: modifiers)
- let suffixContent = suffix.widget(modifiers: modifiers).container(modifiers: modifiers)
- if !prefix.isEmpty {
- _ = row.addPrefix(prefixContent.view)
- }
- if !suffix.isEmpty {
- _ = row.addSuffix(suffixContent.view)
- }
- return .init(row, content: [prefixID: [prefixContent], suffixID: [suffixContent]])
- }
-
- /// Update the action row.
- /// - Parameter row: The action row.
- func update(row: Libadwaita.ActionRow) {
- _ = row.title(title)
- _ = row.subtitle(subtitle)
- }
-
- /// Set the action row's subtitle.
- /// - Parameter subtitle: The subtitle.
- /// - Returns: The action row.
- public func subtitle(_ subtitle: String) -> Self {
- var newSelf = self
- newSelf.subtitle = subtitle
- return newSelf
- }
-
- /// Set the action row's prefix view.
- /// - Parameter prefix: The prefix.
- /// - Returns: The action row.
- public func prefix(@ViewBuilder _ prefix: @escaping () -> Body) -> Self {
- var newSelf = self
- newSelf.prefix = prefix()
- return newSelf
- }
-
- /// Set the action row's suffix view.
- /// - Parameter suffix: The suffix.
- /// - Returns: The action row.
- public func suffix(@ViewBuilder _ suffix: @escaping () -> Body) -> Self {
- var newSelf = self
- newSelf.suffix = suffix()
- return newSelf
- }
-
-}
diff --git a/Sources/Adwaita/View/Forms/ComboRow+.swift b/Sources/Adwaita/View/Forms/ComboRow+.swift
new file mode 100644
index 0000000..1286d13
--- /dev/null
+++ b/Sources/Adwaita/View/Forms/ComboRow+.swift
@@ -0,0 +1,61 @@
+//
+// ComboRow+.swift
+// Adwaita
+//
+// Created by david-swift on 20.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A row for selecting an element out of a list of elements.
+extension ComboRow {
+
+ static var values: String { "values" }
+ static var stringList: String { "string-list" }
+
+ /// Initialize a combo row.
+ /// - Parameters:
+ /// - title: The row's title.
+ /// - selection: The selected value.
+ /// - values: The available values.
+ public init(
+ _ title: String,
+ selection: Binding,
+ values: [Element]
+ ) where Element: Identifiable, Element: CustomStringConvertible {
+ self = self.title(title)
+ self = self.selected(.init {
+ .init(values.firstIndex { $0.id == selection.wrappedValue } ?? 0)
+ } set: { index in
+ if let id = values[safe: .init(index)]?.id {
+ selection.wrappedValue = id
+ }
+ })
+ appearFunctions.append { storage in
+ let list = gtk_string_list_new(nil)
+ storage.fields[Self.stringList] = list
+ adw_combo_row_set_model(storage.pointer?.cast(), list)
+ }
+ updateFunctions.append { storage in
+ if let list = storage.fields[Self.stringList] as? OpaquePointer {
+ let old = storage.fields[Self.values] as? [Element] ?? []
+ var new = values.filter { element in !old.contains { $0.id == element.id } }
+ new = old + new
+ old.identifiableTransform(
+ to: new,
+ functions: .init { index, element in
+ gtk_string_list_remove(list, .init(index))
+ gtk_string_list_append(list, element.description)
+ } delete: { index in
+ gtk_string_list_remove(list, .init(index))
+ } insert: { _, element in
+ gtk_string_list_append(list, element.description)
+ }
+ )
+ storage.fields[Self.values] = new
+ }
+ }
+ }
+
+}
diff --git a/Sources/Adwaita/View/Forms/ComboRow.swift b/Sources/Adwaita/View/Forms/ComboRow.swift
deleted file mode 100644
index a281310..0000000
--- a/Sources/Adwaita/View/Forms/ComboRow.swift
+++ /dev/null
@@ -1,132 +0,0 @@
-//
-// ComboRow.swift
-// Adwaita
-//
-// Created by david-swift on 04.01.24.
-//
-
-import Libadwaita
-
-/// A row for selecting an element out of a list of elements.
-public struct ComboRow: Widget
-where Element: CustomStringConvertible, Element: Identifiable, Element: Equatable {
-
- /// The title.
- var title: String
- /// The selected element.
- @Binding var selection: Element.ID
- /// The content.
- var content: [Element]
- /// The subtitle.
- var subtitle = ""
- /// The prefix.
- var prefix: Body = []
- /// The suffix.
- var suffix: Body = []
-
- /// The identifier for the prefix content.
- let prefixID = "prefix"
- /// The identifier for the suffix content.
- let suffixID = "suffix"
-
- /// The identifier for the elements.
- let elementsID = "elements"
-
- /// Initialize a combo row.
- /// - Parameters:
- /// - title: The row's title.
- /// - selection: The selected value.
- /// - values: The available values.
- public init(_ title: String, selection: Binding, values: [Element]) {
- self.title = title
- self._selection = selection
- content = values
- }
-
- /// Update a view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let row = storage.view as? Libadwaita.ComboRow {
- update(row: row)
- }
- if let prefixStorage = storage.content[prefixID]?.first {
- prefix.widget(modifiers: modifiers).update(prefixStorage, modifiers: modifiers)
- }
- if let suffixStorage = storage.content[suffixID]?.first {
- suffix.widget(modifiers: modifiers).update(suffixStorage, modifiers: modifiers)
- }
- }
-
- /// Get a view storage.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let row: Libadwaita.ComboRow = .init(title: title, subtitle: subtitle)
- let prefixContent = prefix.widget(modifiers: modifiers).container(modifiers: modifiers)
- let suffixContent = suffix.widget(modifiers: modifiers).container(modifiers: modifiers)
- if !prefix.isEmpty {
- _ = row.addPrefix(prefixContent.view)
- }
- if !suffix.isEmpty {
- _ = row.addSuffix(suffixContent.view)
- }
- update(row: row)
- _ = row.onChange {
- if let element = content.first(where: { $0.description == row.selected() }), element.id != selection {
- selection = element.id
- }
- }
- return .init(row, content: [prefixID: [prefixContent], suffixID: [suffixContent]])
- }
-
- /// Update the combo row.
- /// - Parameter row: The combo row.
- func update(row: Libadwaita.ComboRow) {
- _ = row.title(title)
- _ = row.subtitle(subtitle)
- let oldElements = row.fields[elementsID] as? [Element] ?? []
- if oldElements != content {
- for element in oldElements {
- row.remove(at: 0)
- }
- for element in content {
- row.append(element.description)
- }
- }
- let index = content.firstIndex { $0.id == selection } ?? 0
- if row.selected() != content[safe: index]?.description {
- row.select(at: index)
- }
- row.fields[elementsID] = content
- }
-
- /// Set the combo row's subtitle.
- /// - Parameter subtitle: The subtitle.
- /// - Returns: The combo row.
- public func subtitle(_ subtitle: String) -> Self {
- var newSelf = self
- newSelf.subtitle = subtitle
- return newSelf
- }
-
- /// Set the combo row's prefix view.
- /// - Parameter prefix: The prefix.
- /// - Returns: The combo row.
- public func prefix(@ViewBuilder _ prefix: @escaping () -> Body) -> Self {
- var newSelf = self
- newSelf.prefix = prefix()
- return newSelf
- }
-
- /// Set the combo row's suffix view.
- /// - Parameter suffix: The suffix.
- /// - Returns: The combo row.
- public func suffix(@ViewBuilder _ suffix: @escaping () -> Body) -> Self {
- var newSelf = self
- newSelf.suffix = suffix()
- return newSelf
- }
-
-}
diff --git a/Sources/Adwaita/View/Forms/EntryRow+.swift b/Sources/Adwaita/View/Forms/EntryRow+.swift
new file mode 100644
index 0000000..1bf0d90
--- /dev/null
+++ b/Sources/Adwaita/View/Forms/EntryRow+.swift
@@ -0,0 +1,59 @@
+//
+// EntryRow+.swift
+// Adwaita
+//
+// Created by david-swift on 20.01.24.
+//
+
+import CAdw
+
+extension EntryRow {
+
+ static var textField: String { "text" }
+
+ /// Initialize an entry row.
+ /// - Parameters:
+ /// - title: The row's title.
+ /// - text: The text.
+ public init(_ title: String, text: Binding) {
+ self.init()
+ self = self.title(title)
+ appearFunctions.append { storage in
+ storage.fields[Self.textField] = text
+ storage.notify(name: "text") {
+ if let binding = storage.fields[Self.textField] as? Binding {
+ binding.wrappedValue = .init(cString: gtk_editable_get_text(storage.pointer))
+ }
+ }
+ }
+ updateFunctions.append { storage in
+ if text.wrappedValue != .init(cString: gtk_editable_get_text(storage.pointer)) {
+ gtk_editable_set_text(storage.pointer, text.wrappedValue)
+ }
+ }
+ }
+
+ /// Set the entry row's subtitle.
+ /// - Parameter subtitle: The subtitle.
+ /// - Returns: The entry row.
+ public func onSubmit(_ onSubmit: @escaping () -> Void) -> Self {
+ showApplyButton()
+ .apply(onSubmit)
+ }
+
+ /// Let the user securely enter private text.
+ /// - Parameter: The text.
+ /// - Returns: The entry row.
+ public func secure(text: Binding? = nil) -> PasswordEntryRow {
+ .init(title ?? "", text: text ?? .constant(""))
+ .activatesDefault(activatesDefault)
+ .enableEmojiCompletion(enableEmojiCompletion)
+ .showApplyButton(showApplyButton)
+ .titleSelectable(titleSelectable)
+ .useMarkup(useMarkup)
+ .useUnderline(useUnderline)
+ .apply(apply ?? { })
+ .entryActivated(entryActivated ?? { })
+ }
+
+}
diff --git a/Sources/Adwaita/View/Forms/EntryRow.swift b/Sources/Adwaita/View/Forms/EntryRow.swift
deleted file mode 100644
index 1b1968a..0000000
--- a/Sources/Adwaita/View/Forms/EntryRow.swift
+++ /dev/null
@@ -1,136 +0,0 @@
-//
-// EntryRow.swift
-// Adwaita
-//
-// Created by david-swift on 04.01.24.
-//
-
-import Libadwaita
-
-/// A form content row accepting text input.
-public struct EntryRow: Widget {
-
- /// The title.
- var title: String
- /// The text.
- @Binding var text: String
- /// The prefix.
- var prefix: Body = []
- /// The suffix.
- var suffix: Body = []
- /// The handler that gets executed when the user submits the content.
- var onSubmit: (() -> Void)?
- /// Whether the password entry row should be used.
- var password = false
-
- /// The identifier for the prefix content.
- let prefixID = "prefix"
- /// The identifier for the suffix content.
- let suffixID = "suffix"
-
- /// The identifier for the title.
- let titleID = "title"
-
- /// Initialize an entry row.
- /// - Parameters:
- /// - title: The row's title.
- /// - text: The text.
- public init(_ title: String, text: Binding) {
- self.title = title
- self._text = text
- }
-
- /// Update a view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let row = storage.view as? Libadwaita.EntryRow {
- update(row: row)
- }
- if let prefixStorage = storage.content[prefixID]?.first {
- prefix.widget(modifiers: modifiers).update(prefixStorage, modifiers: modifiers)
- }
- if let suffixStorage = storage.content[suffixID]?.first {
- suffix.widget(modifiers: modifiers).update(suffixStorage, modifiers: modifiers)
- }
- }
-
- /// Get a view storage.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let row: Libadwaita.EntryRow
- if password {
- row = PasswordEntryRow(title: title)
- } else {
- row = .init(title: title)
- }
- let prefixContent = prefix.widget(modifiers: modifiers).container(modifiers: modifiers)
- let suffixContent = suffix.widget(modifiers: modifiers).container(modifiers: modifiers)
- if !prefix.isEmpty {
- _ = row.addPrefix(prefixContent.view)
- }
- if !suffix.isEmpty {
- _ = row.addSuffix(suffixContent.view)
- }
- _ = row.changeHandler {
- if row.contents() != text {
- text = row.contents()
- }
- }
- update(row: row)
- return .init(row, content: [prefixID: [prefixContent], suffixID: [suffixContent]])
- }
-
- /// Update the entry row.
- /// - Parameter row: The entry row.
- func update(row: Libadwaita.EntryRow) {
- if row.fields[titleID] as? String != title {
- _ = row.title(title)
- row.fields[titleID] = title
- }
- if row.contents() != text {
- row.setContents(text)
- }
- if let onSubmit {
- _ = row.submitHandler(onSubmit)
- }
- }
-
- /// Set the entry row's subtitle.
- /// - Parameter subtitle: The subtitle.
- /// - Returns: The entry row.
- public func onSubmit(_ onSubmit: @escaping () -> Void) -> Self {
- var newSelf = self
- newSelf.onSubmit = onSubmit
- return newSelf
- }
-
- /// Set the entry row's prefix view.
- /// - Parameter prefix: The prefix.
- /// - Returns: The entry row.
- public func prefix(@ViewBuilder _ prefix: @escaping () -> Body) -> Self {
- var newSelf = self
- newSelf.prefix = prefix()
- return newSelf
- }
-
- /// Set the entry row's suffix view.
- /// - Parameter suffix: The suffix.
- /// - Returns: The entry row.
- public func suffix(@ViewBuilder _ suffix: @escaping () -> Body) -> Self {
- var newSelf = self
- newSelf.suffix = suffix()
- return newSelf
- }
-
- /// Let the user securely enter private text.
- /// - Returns: The entry row.
- public func secure() -> Self {
- var newSelf = self
- newSelf.password = true
- return newSelf
- }
-
-}
diff --git a/Sources/Adwaita/View/Forms/Form.swift b/Sources/Adwaita/View/Forms/Form.swift
index d172f3c..386436e 100644
--- a/Sources/Adwaita/View/Forms/Form.swift
+++ b/Sources/Adwaita/View/Forms/Form.swift
@@ -5,43 +5,24 @@
// Created by david-swift on 03.01.24.
//
-import Libadwaita
+import CAdw
/// A list with no dynamic content styled as a boxed list.
-public struct Form: Widget {
+public struct Form: View {
/// The content.
- var content: () -> Body
+ var content: Body
+
+ /// The view's body.
+ public var view: Body {
+ List([Int](content.indices), selection: nil) { index in content[index] }
+ .style("boxed-list")
+ }
/// Initialize a `Form`.
/// - Parameter content: The view content, usually different kind of rows.
public init(@ViewBuilder content: @escaping () -> Body) {
- self.content = content
- }
-
- /// Update a view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- content().update(storage.content[.mainContent] ?? [], modifiers: modifiers)
- }
-
- /// Get a view storage.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let form: ListBox = .init()
- _ = form
- .noSelection()
- .addStyle("boxed-list")
- var content: [ViewStorage] = []
- for element in self.content() {
- let widget = element.storage(modifiers: modifiers)
- _ = form.append(widget.view)
- content.append(widget)
- }
- return .init(form, content: [.mainContent: content])
+ self.content = content()
}
}
diff --git a/Sources/Adwaita/View/Forms/FormSection+.swift b/Sources/Adwaita/View/Forms/FormSection+.swift
new file mode 100644
index 0000000..2b86000
--- /dev/null
+++ b/Sources/Adwaita/View/Forms/FormSection+.swift
@@ -0,0 +1,30 @@
+//
+// FormSection+.swift
+// Adwaita
+//
+// Created by david-swift on 20.01.24.
+//
+
+/// A section usually groups forms.
+public typealias FormSection = PreferencesGroup
+
+extension FormSection {
+
+ /// Initialize a form section.
+ /// - Parameters:
+ /// - title: The title.
+ /// - content: The content, usually one or more forms.
+ public init(_ title: String, @ViewBuilder content: @escaping () -> Body) {
+ self.init()
+ self = self.title(title)
+ self = self.child(content)
+ }
+
+ /// Set the form section's suffix view.
+ /// - Parameter suffix: The suffix.
+ /// - Returns: The form section.
+ public func suffix(@ViewBuilder _ suffix: @escaping () -> Body) -> Self {
+ headerSuffix(suffix)
+ }
+
+}
diff --git a/Sources/Adwaita/View/Forms/FormSection.swift b/Sources/Adwaita/View/Forms/FormSection.swift
deleted file mode 100644
index 4936501..0000000
--- a/Sources/Adwaita/View/Forms/FormSection.swift
+++ /dev/null
@@ -1,89 +0,0 @@
-//
-// FormSection.swift
-// Adwaita
-//
-// Created by david-swift on 03.01.24.
-//
-
-import Libadwaita
-
-/// A section usually groups forms.
-public struct FormSection: Widget {
-
- /// The title.
- var title: String
- /// The content.
- var content: Body
- /// The description.
- var description = ""
- /// The suffix.
- var suffix: Body = []
-
- /// The identifier for the suffix content.
- let suffixID = "suffix"
-
- /// Initialize a form section.
- /// - Parameters:
- /// - title: The title.
- /// - content: The content, usually one or more forms.
- public init(_ title: String, @ViewBuilder content: () -> Body) {
- self.title = title
- self.content = content()
- }
-
- /// Update a view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let group = storage.view as? Libadwaita.PreferencesGroup {
- update(group: group)
- }
- if let storage = storage.content[.mainContent]?.first {
- content.widget(modifiers: modifiers).update(storage, modifiers: modifiers)
- }
- if let suffixStorage = storage.content[suffixID]?.first {
- suffix.widget(modifiers: modifiers).update(suffixStorage, modifiers: modifiers)
- }
- }
-
- /// Get a view storage.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let group: Libadwaita.PreferencesGroup = .init(name: title, description: description)
- let content = content.widget(modifiers: modifiers).container(modifiers: modifiers)
- let suffixContent = suffix.widget(modifiers: modifiers).container(modifiers: modifiers)
- group.add(content.view)
- if !suffix.isEmpty {
- _ = group.headerSuffix(suffixContent.view)
- }
- return .init(group, content: [.mainContent: [content], suffixID: [suffixContent]])
- }
-
- /// Update the form section.
- /// - Parameter group: The form section.
- func update(group: Libadwaita.PreferencesGroup) {
- _ = group.title(title)
- _ = group.description(description)
- }
-
- /// Set the form section's description.
- /// - Parameter description: The description.
- /// - Returns: The form section.
- public func description(_ description: String) -> Self {
- var newSelf = self
- newSelf.description = description
- return newSelf
- }
-
- /// Set the form section's suffix view.
- /// - Parameter suffix: The suffix.
- /// - Returns: The form section.
- public func suffix(@ViewBuilder _ suffix: @escaping () -> Body) -> Self {
- var newSelf = self
- newSelf.suffix = suffix()
- return newSelf
- }
-
-}
diff --git a/Sources/Adwaita/View/Forms/PasswordEntryRow+.swift b/Sources/Adwaita/View/Forms/PasswordEntryRow+.swift
new file mode 100644
index 0000000..5fdf8fc
--- /dev/null
+++ b/Sources/Adwaita/View/Forms/PasswordEntryRow+.swift
@@ -0,0 +1,43 @@
+//
+// PasswordEntryRow+.swift
+// Adwaita
+//
+// Created by david-swift on 20.01.24.
+//
+
+import CAdw
+
+extension PasswordEntryRow {
+
+ static var textField: String { "text" }
+
+ /// Initialize an entry row.
+ /// - Parameters:
+ /// - title: The row's title.
+ /// - text: The text.
+ public init(_ title: String, text: Binding) {
+ self.init()
+ self = self.title(title)
+ appearFunctions.append { storage in
+ storage.fields[Self.textField] = text
+ storage.notify(name: "text") {
+ if let binding = storage.fields[Self.textField] as? Binding {
+ binding.wrappedValue = .init(cString: gtk_editable_get_text(storage.pointer))
+ }
+ }
+ }
+ updateFunctions.append { storage in
+ if text.wrappedValue != .init(cString: gtk_editable_get_text(storage.pointer)) {
+ gtk_editable_set_text(storage.pointer, text.wrappedValue)
+ }
+ }
+ }
+
+ /// Set the entry row's subtitle.
+ /// - Parameter subtitle: The subtitle.
+ /// - Returns: The entry row.
+ public func onSubmit(_ onSubmit: @escaping () -> Void) -> Self {
+ apply(onSubmit)
+ }
+
+}
diff --git a/Sources/Adwaita/View/Forms/SpinRow+.swift b/Sources/Adwaita/View/Forms/SpinRow+.swift
new file mode 100644
index 0000000..2d145ed
--- /dev/null
+++ b/Sources/Adwaita/View/Forms/SpinRow+.swift
@@ -0,0 +1,62 @@
+//
+// SpinRow+.swift
+// Adwaita
+//
+// Created by david-swift on 20.01.24.
+//
+
+import CAdw
+
+extension SpinRow {
+
+ /// Initialize a spin row.
+ /// - Parameters:
+ /// - title: The row's title.
+ /// - value: The selected value.
+ /// - min: The minimum value.
+ /// - max: The maximum value.
+ public init(_ title: String, value: Binding, min: Int, max: Int) {
+ self.init(
+ title,
+ value: .init { .init(value.wrappedValue) } set: { value.wrappedValue = .init($0) },
+ min: .init(min),
+ max: .init(max)
+ )
+ }
+
+ /// Initialize a spin row.
+ /// - Parameters:
+ /// - title: The row's title.
+ /// - value: The selected value.
+ /// - min: The minimum value.
+ /// - max: The maximum value.
+ public init(_ title: String, value: Binding, min: Double, max: Double) {
+ self.init(climbRate: 1, digits: 0)
+ self = self.title(title)
+ self = self.value(value)
+ self = self.step(1)
+ updateFunctions.append { storage in
+ adw_spin_row_set_range(storage.pointer, min, max)
+ }
+ }
+
+ /// Set the difference a single click on the increase/decrease buttons makes.
+ /// - Parameter step: The increase/decrease step.
+ /// - Returns: The spin row.
+ public func step(_ step: Int) -> Self {
+ self.step(.init(step))
+ }
+
+ /// Set the difference a single click on the increase/decrease buttons makes.
+ /// - Parameter step: The increase/decrease step.
+ /// - Returns: The spin row.
+ public func step(_ step: Double) -> Self {
+ var newSelf = self
+ newSelf.updateFunctions.append { storage in
+ let adjustment = adw_spin_row_get_adjustment(storage.pointer)
+ gtk_adjustment_set_step_increment(adjustment, step)
+ }
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Forms/SpinRow.swift b/Sources/Adwaita/View/Forms/SpinRow.swift
deleted file mode 100644
index fdee2eb..0000000
--- a/Sources/Adwaita/View/Forms/SpinRow.swift
+++ /dev/null
@@ -1,149 +0,0 @@
-//
-// SpinRow.swift
-// Adwaita
-//
-// Created by david-swift on 04.01.24.
-//
-
-import Libadwaita
-
-/// A spin row lets the user select an integer in a certain range.
-public struct SpinRow: Widget {
-
- /// The title.
- var title: String
- /// The selected value.
- @Binding var value: Int
- /// The minimum value.
- var min: Int
- /// The maximum value.
- var max: Int
- /// The increase/decrease step.
- var step = 1
- /// The subtitle.
- var subtitle = ""
- /// The prefix.
- var prefix: Body = []
- /// The suffix.
- var suffix: Body = []
-
- /// The identifier for the prefix content.
- let prefixID = "prefix"
- /// The identifier for the suffix content.
- let suffixID = "suffix"
-
- /// The identifier of the configuration field.
- let configID = "config"
-
- /// Initialize a spin row.
- /// - Parameters:
- /// - title: The row's title.
- /// - value: The selected value.
- /// - min: The minimum value.
- /// - max: The maximum value.
- public init(_ title: String, value: Binding, min: Int, max: Int) {
- self.title = title
- self._value = value
- self.min = min
- self.max = max
- }
-
- /// Update a view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let row = storage.view as? Libadwaita.SpinRow {
- update(row: row)
- }
- if let prefixStorage = storage.content[prefixID]?.first {
- prefix.widget(modifiers: modifiers).update(prefixStorage, modifiers: modifiers)
- }
- if let suffixStorage = storage.content[suffixID]?.first {
- suffix.widget(modifiers: modifiers).update(suffixStorage, modifiers: modifiers)
- }
- }
-
- /// Get a view storage.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let row: Libadwaita.SpinRow = .init(
- title: title,
- subtitle: subtitle,
- min: .init(min),
- max: .init(max),
- step: .init(step)
- )
- row.fields[configID] = (min, max, step)
- let prefixContent = prefix.widget(modifiers: modifiers).container(modifiers: modifiers)
- let suffixContent = suffix.widget(modifiers: modifiers).container(modifiers: modifiers)
- if !prefix.isEmpty {
- _ = row.addPrefix(prefixContent.view)
- }
- if !suffix.isEmpty {
- _ = row.addSuffix(suffixContent.view)
- }
- _ = row.onChange {
- let value = Int(row.getValue().rounded())
- if self.value != value {
- self.value = value
- }
- }
- update(row: row)
- return .init(row, content: [prefixID: [prefixContent], suffixID: [suffixContent]])
- }
-
- // swiftlint:disable large_tuple
- /// Update the spin row.
- /// - Parameter row: The spin row.
- func update(row: Libadwaita.SpinRow) {
- _ = row.title(title)
- _ = row.subtitle(subtitle)
- if row.fields[configID] as? (Int, Int, Int) ?? (0, 0, 0) != (min, max, step) {
- _ = row.configuration(min: .init(min), max: .init(max), step: .init(step))
- row.fields[configID] = (min, max, step)
- }
- if row.getValue() != .init(value) {
- row.setValue(.init(value))
- }
- }
- // swiftlint:enable large_tuple
-
- /// Set the spin row's subtitle.
- /// - Parameter subtitle: The subtitle.
- /// - Returns: The spin row.
- public func subtitle(_ subtitle: String) -> Self {
- var newSelf = self
- newSelf.subtitle = subtitle
- return newSelf
- }
-
- /// Set the spin row's prefix view.
- /// - Parameter prefix: The prefix.
- /// - Returns: The spin row.
- public func prefix(@ViewBuilder _ prefix: @escaping () -> Body) -> Self {
- var newSelf = self
- newSelf.prefix = prefix()
- return newSelf
- }
-
- /// Set the spin row's suffix view.
- /// - Parameter suffix: The suffix.
- /// - Returns: The spin row.
- public func suffix(@ViewBuilder _ suffix: @escaping () -> Body) -> Self {
- var newSelf = self
- newSelf.suffix = suffix()
- return newSelf
- }
-
- /// Set the difference a single click on the increase/decrease buttons makes.
- /// - Parameter step: The increase/decrease step.
- /// - Returns: The spin row.
- public func step(_ step: Int) -> Self {
- var newSelf = self
- newSelf.step = step
- return newSelf
- }
-
-}
diff --git a/Sources/Adwaita/View/Forms/SwitchRow+.swift b/Sources/Adwaita/View/Forms/SwitchRow+.swift
new file mode 100644
index 0000000..48c6b44
--- /dev/null
+++ b/Sources/Adwaita/View/Forms/SwitchRow+.swift
@@ -0,0 +1,21 @@
+//
+// SwitchRow+.swift
+// Adwaita
+//
+// Created by david-swift on 20.01.24.
+//
+
+/// A row representing a boolean state.
+extension SwitchRow {
+
+ /// Initialize a switch row.
+ /// - Parameters:
+ /// - title: The row's title.
+ /// - isOn: Whether the switch is on.
+ public init(_ title: String, isOn: Binding) {
+ self.init()
+ self = self.title(title)
+ self = self.active(isOn)
+ }
+
+}
diff --git a/Sources/Adwaita/View/Forms/SwitchRow.swift b/Sources/Adwaita/View/Forms/SwitchRow.swift
deleted file mode 100644
index ec863e6..0000000
--- a/Sources/Adwaita/View/Forms/SwitchRow.swift
+++ /dev/null
@@ -1,113 +0,0 @@
-//
-// SwitchRow.swift
-// Adwaita
-//
-// Created by david-swift on 04.01.24.
-//
-
-import Libadwaita
-
-/// A row representing a boolean state.
-public struct SwitchRow: Widget {
-
- /// The title.
- var title: String
- /// Whether the switch is activated.
- @Binding var isOn: Bool
- /// The subtitle.
- var subtitle = ""
- /// The prefix.
- var prefix: Body = []
- /// The suffix.
- var suffix: Body = []
-
- /// The identifier for the prefix content.
- let prefixID = "prefix"
- /// The identifier for the suffix content.
- let suffixID = "suffix"
-
- /// Initialize a switch row.
- /// - Parameters:
- /// - title: The row's title.
- /// - isOn: Whether the switch is on.
- public init(_ title: String, isOn: Binding) {
- self.title = title
- self._isOn = isOn
- }
-
- /// Update a view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let row = storage.view as? Libadwaita.SwitchRow {
- update(row: row)
- }
- if let prefixStorage = storage.content[prefixID]?.first {
- prefix.widget(modifiers: modifiers).update(prefixStorage, modifiers: modifiers)
- }
- if let suffixStorage = storage.content[suffixID]?.first {
- suffix.widget(modifiers: modifiers).update(suffixStorage, modifiers: modifiers)
- }
- }
-
- /// Get a view storage.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let row: Libadwaita.SwitchRow = .init(title: title, subtitle: subtitle)
- let prefixContent = prefix.widget(modifiers: modifiers).container(modifiers: modifiers)
- let suffixContent = suffix.widget(modifiers: modifiers).container(modifiers: modifiers)
- if !prefix.isEmpty {
- _ = row.addPrefix(prefixContent.view)
- }
- if !suffix.isEmpty {
- _ = row.addSuffix(suffixContent.view)
- }
- _ = row.onChange {
- if row.getActive() != isOn {
- isOn = row.getActive()
- }
- }
- update(row: row)
- return .init(row, content: [prefixID: [prefixContent], suffixID: [suffixContent]])
- }
-
- /// Update the switch row.
- /// - Parameter row: The switch row.
- func update(row: Libadwaita.SwitchRow) {
- _ = row.title(title)
- _ = row.subtitle(subtitle)
- if row.getActive() != isOn {
- row.setActive(isOn)
- }
- }
-
- /// Set the switch row's subtitle.
- /// - Parameter subtitle: The subtitle.
- /// - Returns: The switch row.
- public func subtitle(_ subtitle: String) -> Self {
- var newSelf = self
- newSelf.subtitle = subtitle
- return newSelf
- }
-
- /// Set the switch row's prefix view.
- /// - Parameter prefix: The prefix.
- /// - Returns: The switch row.
- public func prefix(@ViewBuilder _ prefix: @escaping () -> Body) -> Self {
- var newSelf = self
- newSelf.prefix = prefix()
- return newSelf
- }
-
- /// Set the switch row's suffix view.
- /// - Parameter suffix: The suffix.
- /// - Returns: The switch row.
- public func suffix(@ViewBuilder _ suffix: @escaping () -> Body) -> Self {
- var newSelf = self
- newSelf.suffix = suffix()
- return newSelf
- }
-
-}
diff --git a/Sources/Adwaita/View/Generated/ActionRow.swift b/Sources/Adwaita/View/Generated/ActionRow.swift
new file mode 100644
index 0000000..95c071b
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/ActionRow.swift
@@ -0,0 +1,334 @@
+//
+// ActionRow.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A [class@Gtk.ListBoxRow] used to present actions.
+///
+///
+///
+/// The `AdwActionRow` widget can have a title, a subtitle and an icon. The row
+/// can receive additional widgets at its end, or prefix widgets at its start.
+///
+/// It is convenient to present a preference and its related actions.
+///
+/// `AdwActionRow` is unactivatable by default, giving it an activatable widget
+/// will automatically make it activatable, but unsetting it won't change the
+/// row's activatability.
+///
+/// ## AdwActionRow as GtkBuildable
+///
+/// The `AdwActionRow` implementation of the [iface@Gtk.Buildable] interface
+/// supports adding a child at its end by specifying “suffix” or omitting the
+/// “type” attribute of a element.
+///
+/// It also supports adding a child as a prefix widget by specifying “prefix” as
+/// the “type” attribute of a element.
+///
+/// ## CSS nodes
+///
+/// `AdwActionRow` has a main CSS node with name `row`.
+///
+/// It contains the subnode `box.header` for its main horizontal box, and
+/// `box.title` for the vertical box containing the title and subtitle labels.
+///
+/// It contains subnodes `label.title` and `label.subtitle` representing
+/// respectively the title label and subtitle label.
+public struct ActionRow: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The widget to activate when the row is activated.
+ ///
+ /// The row can be activated either by clicking on it, calling
+ /// [method@ActionRow.activate], or via mnemonics in the title.
+ /// See the [property@PreferencesRow:use-underline] property to enable
+ /// mnemonics.
+ ///
+ /// The target widget will be activated by emitting the
+ /// [signal@Gtk.Widget::mnemonic-activate] signal on it.
+ var activatableWidget: (() -> Body)?
+ /// The icon name for this row.
+ var iconName: String?
+ /// The subtitle for this row.
+ ///
+ /// The subtitle is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ var subtitle: String?
+ /// The number of lines at the end of which the subtitle label will be
+ /// ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ var subtitleLines: Int?
+ /// Whether the user can copy the subtitle from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ var subtitleSelectable: Bool?
+ /// The number of lines at the end of which the title label will be ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ var titleLines: Int?
+ /// The title of the preference represented by this row.
+ ///
+ /// The title is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ var title: String?
+ /// Whether the user can copy the title from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ var titleSelectable: Bool?
+ /// Whether to use Pango markup for the title label.
+ ///
+ /// Subclasses may also use it for other labels, such as subtitle.
+ ///
+ /// See also [func@Pango.parse_markup].
+ var useMarkup: Bool?
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ var useUnderline: Bool?
+ /// This signal is emitted after the row has been activated.
+ var activated: (() -> Void)?
+ /// The body for the widget "suffix".
+ var suffix: () -> Body = { [] }
+ /// The body for the widget "prefix".
+ var prefix: () -> Body = { [] }
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `ActionRow`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_action_row_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let activatableWidgetStorage = activatableWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["activatableWidget"] = [activatableWidgetStorage]
+ adw_action_row_set_activatable_widget(storage.pointer?.cast(), activatableWidgetStorage.pointer?.cast())
+ }
+
+ var suffixStorage: [ViewStorage] = []
+ for view in suffix() {
+ suffixStorage.append(view.storage(modifiers: modifiers))
+ adw_action_row_add_suffix(storage.pointer?.cast(), suffixStorage.last?.pointer?.cast())
+ }
+ storage.content["suffix"] = suffixStorage
+ var prefixStorage: [ViewStorage] = []
+ for view in prefix() {
+ prefixStorage.append(view.storage(modifiers: modifiers))
+ adw_action_row_add_prefix(storage.pointer?.cast(), prefixStorage.last?.pointer?.cast())
+ }
+ storage.content["prefix"] = prefixStorage
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let activated {
+ storage.connectSignal(name: "activated") {
+ activated()
+ }
+ }
+ storage.modify { widget in
+ if let widget = storage.content["activatableWidget"]?.first {
+ activatableWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let iconName {
+ adw_action_row_set_icon_name(widget?.cast(), iconName)
+ }
+ if let subtitle {
+ adw_action_row_set_subtitle(widget?.cast(), subtitle)
+ }
+ if let subtitleLines {
+ adw_action_row_set_subtitle_lines(widget?.cast(), subtitleLines.cInt)
+ }
+ if let subtitleSelectable {
+ adw_action_row_set_subtitle_selectable(widget?.cast(), subtitleSelectable.cBool)
+ }
+ if let titleLines {
+ adw_action_row_set_title_lines(widget?.cast(), titleLines.cInt)
+ }
+ if let title {
+ adw_preferences_row_set_title(widget?.cast(), title)
+ }
+ if let titleSelectable {
+ adw_preferences_row_set_title_selectable(widget?.cast(), titleSelectable.cBool)
+ }
+ if let useMarkup {
+ adw_preferences_row_set_use_markup(widget?.cast(), useMarkup.cBool)
+ }
+ if let useUnderline {
+ adw_preferences_row_set_use_underline(widget?.cast(), useUnderline.cBool)
+ }
+
+ if let suffixStorage = storage.content["suffix"] {
+ for (index, view) in suffix().enumerated() {
+ if let storage = suffixStorage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+ if let prefixStorage = storage.content["prefix"] {
+ for (index, view) in prefix().enumerated() {
+ if let storage = prefixStorage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The widget to activate when the row is activated.
+ ///
+ /// The row can be activated either by clicking on it, calling
+ /// [method@ActionRow.activate], or via mnemonics in the title.
+ /// See the [property@PreferencesRow:use-underline] property to enable
+ /// mnemonics.
+ ///
+ /// The target widget will be activated by emitting the
+ /// [signal@Gtk.Widget::mnemonic-activate] signal on it.
+ public func activatableWidget(@ViewBuilder _ activatableWidget: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.activatableWidget = activatableWidget
+
+ return newSelf
+ }
+
+ /// The icon name for this row.
+ public func iconName(_ iconName: String?) -> Self {
+ var newSelf = self
+ newSelf.iconName = iconName
+
+ return newSelf
+ }
+
+ /// The subtitle for this row.
+ ///
+ /// The subtitle is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ public func subtitle(_ subtitle: String?) -> Self {
+ var newSelf = self
+ newSelf.subtitle = subtitle
+
+ return newSelf
+ }
+
+ /// The number of lines at the end of which the subtitle label will be
+ /// ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ public func subtitleLines(_ subtitleLines: Int?) -> Self {
+ var newSelf = self
+ newSelf.subtitleLines = subtitleLines
+
+ return newSelf
+ }
+
+ /// Whether the user can copy the subtitle from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ public func subtitleSelectable(_ subtitleSelectable: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.subtitleSelectable = subtitleSelectable
+
+ return newSelf
+ }
+
+ /// The number of lines at the end of which the title label will be ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ public func titleLines(_ titleLines: Int?) -> Self {
+ var newSelf = self
+ newSelf.titleLines = titleLines
+
+ return newSelf
+ }
+
+ /// The title of the preference represented by this row.
+ ///
+ /// The title is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ public func title(_ title: String?) -> Self {
+ var newSelf = self
+ newSelf.title = title
+
+ return newSelf
+ }
+
+ /// Whether the user can copy the title from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ public func titleSelectable(_ titleSelectable: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.titleSelectable = titleSelectable
+
+ return newSelf
+ }
+
+ /// Whether to use Pango markup for the title label.
+ ///
+ /// Subclasses may also use it for other labels, such as subtitle.
+ ///
+ /// See also [func@Pango.parse_markup].
+ public func useMarkup(_ useMarkup: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useMarkup = useMarkup
+
+ return newSelf
+ }
+
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+ /// This signal is emitted after the row has been activated.
+ public func activated(_ activated: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.activated = activated
+ return newSelf
+ }
+
+ /// Set the body for "suffix".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func suffix(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.suffix = body
+ return newSelf
+ }
+ /// Set the body for "prefix".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func prefix(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.prefix = body
+ return newSelf
+ }
+}
diff --git a/Sources/Adwaita/View/Generated/Avatar.swift b/Sources/Adwaita/View/Generated/Avatar.swift
new file mode 100644
index 0000000..1ae76f6
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/Avatar.swift
@@ -0,0 +1,139 @@
+//
+// Avatar.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A widget displaying an image, with a generated fallback.
+///
+///
+///
+/// `AdwAvatar` is a widget that shows a round avatar.
+///
+/// `AdwAvatar` generates an avatar with the initials of the
+/// [property@Avatar:text] on top of a colored background.
+///
+/// The color is picked based on the hash of the [property@Avatar:text].
+///
+/// If [property@Avatar:show-initials] is set to `FALSE`,
+/// [property@Avatar:icon-name] or `avatar-default-symbolic` is shown instead of
+/// the initials.
+///
+/// Use [property@Avatar:custom-image] to set a custom image.
+///
+/// ## CSS nodes
+///
+/// `AdwAvatar` has a single CSS node with name `avatar`.
+public struct Avatar: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The name of an icon to use as a fallback.
+ ///
+ /// If no name is set, `avatar-default-symbolic` will be used.
+ var iconName: String?
+ /// Whether initials are used instead of an icon on the fallback avatar.
+ ///
+ /// See [property@Avatar:icon-name] for how to change the fallback icon.
+ var showInitials: Bool
+ /// The size of the avatar.
+ var size: Int
+ /// Sets the text used to generate the fallback initials and color.
+ ///
+ /// It's only used to generate the color if [property@Avatar:show-initials] is
+ /// `FALSE`.
+ var text: String?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `Avatar`.
+ public init(showInitials: Bool, size: Int) {
+ self.showInitials = showInitials
+ self.size = size
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_avatar_new(size.cInt, text, showInitials.cBool)?.opaque())
+ update(storage, modifiers: modifiers)
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let iconName {
+ adw_avatar_set_icon_name(widget, iconName)
+ }
+ adw_avatar_set_show_initials(widget, showInitials.cBool)
+ adw_avatar_set_size(widget, size.cInt)
+ if let text {
+ adw_avatar_set_text(widget, text)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The name of an icon to use as a fallback.
+ ///
+ /// If no name is set, `avatar-default-symbolic` will be used.
+ public func iconName(_ iconName: String?) -> Self {
+ var newSelf = self
+ newSelf.iconName = iconName
+
+ return newSelf
+ }
+
+ /// Whether initials are used instead of an icon on the fallback avatar.
+ ///
+ /// See [property@Avatar:icon-name] for how to change the fallback icon.
+ public func showInitials(_ showInitials: Bool) -> Self {
+ var newSelf = self
+ newSelf.showInitials = showInitials
+
+ return newSelf
+ }
+
+ /// The size of the avatar.
+ public func size(_ size: Int) -> Self {
+ var newSelf = self
+ newSelf.size = size
+
+ return newSelf
+ }
+
+ /// Sets the text used to generate the fallback initials and color.
+ ///
+ /// It's only used to generate the color if [property@Avatar:show-initials] is
+ /// `FALSE`.
+ public func text(_ text: String?) -> Self {
+ var newSelf = self
+ newSelf.text = text
+
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/Banner.swift b/Sources/Adwaita/View/Generated/Banner.swift
new file mode 100644
index 0000000..1aa1a33
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/Banner.swift
@@ -0,0 +1,161 @@
+//
+// Banner.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A bar with contextual information.
+///
+///
+///
+/// Banners are hidden by default, use [property@Banner:revealed] to show them.
+///
+/// Banners have a title, set with [property@Banner:title]. Titles can be marked
+/// up with Pango markup, use [property@Banner:use-markup] to enable it.
+///
+/// The title will be shown centered or left-aligned depending on available
+/// space.
+///
+/// Banners can optionally have a button with text on it, set through
+/// [property@Banner:button-label]. The button can be used with a `GAction`,
+/// or with the [signal@Banner::button-clicked] signal.
+///
+/// ## CSS nodes
+///
+/// `AdwBanner` has a main CSS node with the name `banner`.
+public struct Banner: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The label to show on the button.
+ ///
+ /// If set to `""` or `NULL`, the button won't be shown.
+ ///
+ /// The button can be used with a `GAction`, or with the
+ /// [signal@Banner::button-clicked] signal.
+ var buttonLabel: String?
+ /// Whether the banner is currently revealed.
+ var revealed: Bool?
+ /// The title for this banner.
+ ///
+ /// See also: [property@Banner:use-markup].
+ var title: String
+ /// Whether to use Pango markup for the banner title.
+ ///
+ /// See also [func@Pango.parse_markup].
+ var useMarkup: Bool?
+ /// This signal is emitted after the action button has been clicked.
+ ///
+ /// It can be used as an alternative to setting an action.
+ var buttonClicked: (() -> Void)?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `Banner`.
+ public init(title: String) {
+ self.title = title
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_banner_new(title)?.opaque())
+ update(storage, modifiers: modifiers)
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let buttonClicked {
+ storage.connectSignal(name: "button-clicked") {
+ buttonClicked()
+ }
+ }
+ storage.modify { widget in
+ if let buttonLabel {
+ adw_banner_set_button_label(widget, buttonLabel)
+ }
+ if let revealed {
+ adw_banner_set_revealed(widget, revealed.cBool)
+ }
+ adw_banner_set_title(widget, title)
+ if let useMarkup {
+ adw_banner_set_use_markup(widget, useMarkup.cBool)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The label to show on the button.
+ ///
+ /// If set to `""` or `NULL`, the button won't be shown.
+ ///
+ /// The button can be used with a `GAction`, or with the
+ /// [signal@Banner::button-clicked] signal.
+ public func buttonLabel(_ buttonLabel: String?) -> Self {
+ var newSelf = self
+ newSelf.buttonLabel = buttonLabel
+
+ return newSelf
+ }
+
+ /// Whether the banner is currently revealed.
+ public func revealed(_ revealed: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.revealed = revealed
+
+ return newSelf
+ }
+
+ /// The title for this banner.
+ ///
+ /// See also: [property@Banner:use-markup].
+ public func title(_ title: String) -> Self {
+ var newSelf = self
+ newSelf.title = title
+
+ return newSelf
+ }
+
+ /// Whether to use Pango markup for the banner title.
+ ///
+ /// See also [func@Pango.parse_markup].
+ public func useMarkup(_ useMarkup: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useMarkup = useMarkup
+
+ return newSelf
+ }
+
+ /// This signal is emitted after the action button has been clicked.
+ ///
+ /// It can be used as an alternative to setting an action.
+ public func buttonClicked(_ buttonClicked: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.buttonClicked = buttonClicked
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/Bin.swift b/Sources/Adwaita/View/Generated/Bin.swift
new file mode 100644
index 0000000..0555bdf
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/Bin.swift
@@ -0,0 +1,81 @@
+//
+// Bin.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A widget with one child.
+///
+///
+///
+/// The `AdwBin` widget has only one child, set with the [property@Bin:child]
+/// property.
+///
+/// It is useful for deriving subclasses, since it provides common code needed
+/// for handling a single child widget.
+public struct Bin: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The child widget of the `AdwBin`.
+ var child: (() -> Body)?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `Bin`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_bin_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["child"] = [childStorage]
+ adw_bin_set_child(storage.pointer?.cast(), childStorage.pointer?.cast())
+ }
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let widget = storage.content["child"]?.first {
+ child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The child widget of the `AdwBin`.
+ public func child(@ViewBuilder _ child: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.child = child
+
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/Box.swift b/Sources/Adwaita/View/Generated/Box.swift
new file mode 100644
index 0000000..5bc77a5
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/Box.swift
@@ -0,0 +1,173 @@
+//
+// Box.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// The `GtkBox` widget arranges child widgets into a single row or column.
+///
+/// ![An example GtkBox](box.png)
+///
+/// Whether it is a row or column depends on the value of its
+/// [property@Gtk.Orientable:orientation] property. Within the other
+/// dimension, all children are allocated the same size. Of course, the
+/// [property@Gtk.Widget:halign] and [property@Gtk.Widget:valign] properties
+/// can be used on the children to influence their allocation.
+///
+/// Use repeated calls to [method@Gtk.Box.append] to pack widgets into a
+/// `GtkBox` from start to end. Use [method@Gtk.Box.remove] to remove widgets
+/// from the `GtkBox`. [method@Gtk.Box.insert_child_after] can be used to add
+/// a child at a particular position.
+///
+/// Use [method@Gtk.Box.set_homogeneous] to specify whether or not all children
+/// of the `GtkBox` are forced to get the same amount of space.
+///
+/// Use [method@Gtk.Box.set_spacing] to determine how much space will be minimally
+/// placed between all children in the `GtkBox`. Note that spacing is added
+/// *between* the children.
+///
+/// Use [method@Gtk.Box.reorder_child_after] to move a child to a different
+/// place in the box.
+///
+/// # CSS nodes
+///
+/// `GtkBox` uses a single CSS node with name box.
+///
+/// # Accessibility
+///
+/// Until GTK 4.10, `GtkBox` used the `GTK_ACCESSIBLE_ROLE_GROUP` role.
+///
+/// Starting from GTK 4.12, `GtkBox` uses the `GTK_ACCESSIBLE_ROLE_GENERIC` role.
+public struct Box: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The child that determines the baseline, in vertical orientation.
+ var baselineChild: Int?
+ /// Whether the children should all be the same size.
+ var homogeneous: Bool?
+ /// The amount of space between children.
+ var spacing: Int
+ /// The body for the widget "append".
+ var append: () -> Body = { [] }
+ /// The body for the widget "prepend".
+ var prepend: () -> Body = { [] }
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `Box`.
+ public init(spacing: Int) {
+ self.spacing = spacing
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing.cInt)?.opaque())
+ update(storage, modifiers: modifiers)
+
+ var appendStorage: [ViewStorage] = []
+ for view in append() {
+ appendStorage.append(view.storage(modifiers: modifiers))
+ gtk_box_append(storage.pointer?.cast(), appendStorage.last?.pointer?.cast())
+ }
+ storage.content["append"] = appendStorage
+ var prependStorage: [ViewStorage] = []
+ for view in prepend() {
+ prependStorage.append(view.storage(modifiers: modifiers))
+ gtk_box_prepend(storage.pointer?.cast(), prependStorage.last?.pointer?.cast())
+ }
+ storage.content["prepend"] = prependStorage
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let baselineChild {
+ gtk_box_set_baseline_child(widget?.cast(), baselineChild.cInt)
+ }
+ if let homogeneous {
+ gtk_box_set_homogeneous(widget?.cast(), homogeneous.cBool)
+ }
+ gtk_box_set_spacing(widget?.cast(), spacing.cInt)
+
+ if let appendStorage = storage.content["append"] {
+ for (index, view) in append().enumerated() {
+ if let storage = appendStorage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+ if let prependStorage = storage.content["prepend"] {
+ for (index, view) in prepend().enumerated() {
+ if let storage = prependStorage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The child that determines the baseline, in vertical orientation.
+ public func baselineChild(_ baselineChild: Int?) -> Self {
+ var newSelf = self
+ newSelf.baselineChild = baselineChild
+
+ return newSelf
+ }
+
+ /// Whether the children should all be the same size.
+ public func homogeneous(_ homogeneous: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.homogeneous = homogeneous
+
+ return newSelf
+ }
+
+ /// The amount of space between children.
+ public func spacing(_ spacing: Int) -> Self {
+ var newSelf = self
+ newSelf.spacing = spacing
+
+ return newSelf
+ }
+
+ /// Set the body for "append".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func append(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.append = body
+ return newSelf
+ }
+ /// Set the body for "prepend".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func prepend(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.prepend = body
+ return newSelf
+ }
+}
diff --git a/Sources/Adwaita/View/Generated/Button.swift b/Sources/Adwaita/View/Generated/Button.swift
new file mode 100644
index 0000000..ec2f0d6
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/Button.swift
@@ -0,0 +1,221 @@
+//
+// Button.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// The `GtkButton` widget is generally used to trigger a callback function that is
+/// called when the button is pressed.
+///
+/// ![An example GtkButton](button.png)
+///
+/// The `GtkButton` widget can hold any valid child widget. That is, it can hold
+/// almost any other standard `GtkWidget`. The most commonly used child is the
+/// `GtkLabel`.
+///
+/// # CSS nodes
+///
+/// `GtkButton` has a single CSS node with name button. The node will get the
+/// style classes .image-button or .text-button, if the content is just an
+/// image or label, respectively. It may also receive the .flat style class.
+/// When activating a button via the keyboard, the button will temporarily
+/// gain the .keyboard-activating style class.
+///
+/// Other style classes that are commonly used with `GtkButton` include
+/// .suggested-action and .destructive-action. In special cases, buttons
+/// can be made round by adding the .circular style class.
+///
+/// Button-like widgets like [class@Gtk.ToggleButton], [class@Gtk.MenuButton],
+/// [class@Gtk.VolumeButton], [class@Gtk.LockButton], [class@Gtk.ColorButton]
+/// or [class@Gtk.FontButton] use style classes such as .toggle, .popup, .scale,
+/// .lock, .color on the button node to differentiate themselves from a plain
+/// `GtkButton`.
+///
+/// # Accessibility
+///
+/// `GtkButton` uses the %GTK_ACCESSIBLE_ROLE_BUTTON role.
+public struct Button: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// Whether the size of the button can be made smaller than the natural
+ /// size of its contents.
+ ///
+ /// For text buttons, setting this property will allow ellipsizing the label.
+ ///
+ /// If the contents of a button are an icon or a custom widget, setting this
+ /// property has no effect.
+ var canShrink: Bool?
+ /// The child widget.
+ var child: (() -> Body)?
+ /// Whether the button has a frame.
+ var hasFrame: Bool?
+ /// The name of the icon used to automatically populate the button.
+ var iconName: String?
+ /// Text of the label inside the button, if the button contains a label widget.
+ var label: String?
+ /// If set, an underline in the text indicates that the following character is
+ /// to be used as mnemonic.
+ var useUnderline: Bool?
+ /// Emitted to animate press then release.
+ ///
+ /// This is an action signal. Applications should never connect
+ /// to this signal, but use the [signal@Gtk.Button::clicked] signal.
+ ///
+ /// The default bindings for this signal are all forms of the
+ /// ␣ and Enter keys.
+ var activate: (() -> Void)?
+ /// Emitted when the button has been activated (pressed and released).
+ var clicked: (() -> Void)?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `Button`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(gtk_button_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["child"] = [childStorage]
+ gtk_button_set_child(storage.pointer?.cast(), childStorage.pointer?.cast())
+ }
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let activate {
+ storage.connectSignal(name: "activate") {
+ activate()
+ }
+ }
+ if let clicked {
+ storage.connectSignal(name: "clicked") {
+ clicked()
+ }
+ }
+ storage.modify { widget in
+ if let canShrink {
+ gtk_button_set_can_shrink(widget?.cast(), canShrink.cBool)
+ }
+ if let widget = storage.content["child"]?.first {
+ child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let hasFrame {
+ gtk_button_set_has_frame(widget?.cast(), hasFrame.cBool)
+ }
+ if let iconName {
+ gtk_button_set_icon_name(widget?.cast(), iconName)
+ }
+ if let label, storage.content["child"] == nil {
+ gtk_button_set_label(widget?.cast(), label)
+ }
+ if let useUnderline {
+ gtk_button_set_use_underline(widget?.cast(), useUnderline.cBool)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// Whether the size of the button can be made smaller than the natural
+ /// size of its contents.
+ ///
+ /// For text buttons, setting this property will allow ellipsizing the label.
+ ///
+ /// If the contents of a button are an icon or a custom widget, setting this
+ /// property has no effect.
+ public func canShrink(_ canShrink: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.canShrink = canShrink
+
+ return newSelf
+ }
+
+ /// The child widget.
+ public func child(@ViewBuilder _ child: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.child = child
+
+ return newSelf
+ }
+
+ /// Whether the button has a frame.
+ public func hasFrame(_ hasFrame: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.hasFrame = hasFrame
+
+ return newSelf
+ }
+
+ /// The name of the icon used to automatically populate the button.
+ public func iconName(_ iconName: String?) -> Self {
+ var newSelf = self
+ newSelf.iconName = iconName
+
+ return newSelf
+ }
+
+ /// Text of the label inside the button, if the button contains a label widget.
+ public func label(_ label: String?) -> Self {
+ var newSelf = self
+ newSelf.label = label
+
+ return newSelf
+ }
+
+ /// If set, an underline in the text indicates that the following character is
+ /// to be used as mnemonic.
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+ /// Emitted to animate press then release.
+ ///
+ /// This is an action signal. Applications should never connect
+ /// to this signal, but use the [signal@Gtk.Button::clicked] signal.
+ ///
+ /// The default bindings for this signal are all forms of the
+ /// ␣ and Enter keys.
+ public func activate(_ activate: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.activate = activate
+ return newSelf
+ }
+
+ /// Emitted when the button has been activated (pressed and released).
+ public func clicked(_ clicked: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.clicked = clicked
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/ButtonContent.swift b/Sources/Adwaita/View/Generated/ButtonContent.swift
new file mode 100644
index 0000000..35b3928
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/ButtonContent.swift
@@ -0,0 +1,162 @@
+//
+// ButtonContent.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A helper widget for creating buttons.
+///
+///
+///
+/// `AdwButtonContent` is a box-like widget with an icon and a label.
+///
+/// It's intended to be used as a direct child of [class@Gtk.Button],
+/// [class@Gtk.MenuButton] or [class@SplitButton], when they need to have both an
+/// icon and a label, as follows:
+///
+/// ```xml
+///
+/// ```
+///
+/// `AdwButtonContent` handles style classes and connecting the mnemonic to the
+/// button automatically.
+///
+/// ## CSS nodes
+///
+/// ```
+/// buttoncontent
+/// ├── image
+/// ╰── label
+/// ```
+///
+/// `AdwButtonContent`'s CSS node is called `buttoncontent`. It contains the
+/// subnodes `image` and `label`.
+///
+/// When inside a `GtkButton` or `AdwSplitButton`, the button will receive the
+/// `.image-text-button` style class. When inside a `GtkMenuButton`, the
+/// internal `GtkButton` will receive it instead.
+///
+/// ## Accessibility
+///
+/// `AdwButtonContent` uses the `GTK_ACCESSIBLE_ROLE_GROUP` role.
+public struct ButtonContent: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// Whether the button can be smaller than the natural size of its contents.
+ ///
+ /// If set to `TRUE`, the label will ellipsize.
+ ///
+ /// See [property@Gtk.Button:can-shrink].
+ var canShrink: Bool?
+ /// The name of the displayed icon.
+ ///
+ /// If empty, the icon is not shown.
+ var iconName: String?
+ /// The displayed label.
+ var label: String?
+ /// Whether an underline in the text indicates a mnemonic.
+ ///
+ /// The mnemonic can be used to activate the parent button.
+ ///
+ /// See [property@ButtonContent:label].
+ var useUnderline: Bool?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `ButtonContent`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_button_content_new()?.opaque())
+ update(storage, modifiers: modifiers)
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let canShrink {
+ adw_button_content_set_can_shrink(widget, canShrink.cBool)
+ }
+ if let iconName {
+ adw_button_content_set_icon_name(widget, iconName)
+ }
+ if let label {
+ adw_button_content_set_label(widget, label)
+ }
+ if let useUnderline {
+ adw_button_content_set_use_underline(widget, useUnderline.cBool)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// Whether the button can be smaller than the natural size of its contents.
+ ///
+ /// If set to `TRUE`, the label will ellipsize.
+ ///
+ /// See [property@Gtk.Button:can-shrink].
+ public func canShrink(_ canShrink: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.canShrink = canShrink
+
+ return newSelf
+ }
+
+ /// The name of the displayed icon.
+ ///
+ /// If empty, the icon is not shown.
+ public func iconName(_ iconName: String?) -> Self {
+ var newSelf = self
+ newSelf.iconName = iconName
+
+ return newSelf
+ }
+
+ /// The displayed label.
+ public func label(_ label: String?) -> Self {
+ var newSelf = self
+ newSelf.label = label
+
+ return newSelf
+ }
+
+ /// Whether an underline in the text indicates a mnemonic.
+ ///
+ /// The mnemonic can be used to activate the parent button.
+ ///
+ /// See [property@ButtonContent:label].
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/Carousel.swift b/Sources/Adwaita/View/Generated/Carousel.swift
new file mode 100644
index 0000000..978eece
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/Carousel.swift
@@ -0,0 +1,229 @@
+//
+// Carousel.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A paginated scrolling widget.
+///
+///
+///
+/// The `AdwCarousel` widget can be used to display a set of pages with
+/// swipe-based navigation between them.
+///
+/// [class@CarouselIndicatorDots] and [class@CarouselIndicatorLines] can be used
+/// to provide page indicators for `AdwCarousel`.
+///
+/// ## CSS nodes
+///
+/// `AdwCarousel` has a single CSS node with name `carousel`.
+public struct Carousel: Widget where Element: Identifiable {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// Whether to allow swiping for more than one page at a time.
+ ///
+ /// If the value is `FALSE`, each swipe can only move to the adjacent pages.
+ var allowLongSwipes: Bool?
+ /// Sets whether the `AdwCarousel` can be dragged with mouse pointer.
+ ///
+ /// If the value is `FALSE`, dragging is only available on touch.
+ var allowMouseDrag: Bool?
+ /// Whether the widget will respond to scroll wheel events.
+ ///
+ /// If the value is `FALSE`, wheel events will be ignored.
+ var allowScrollWheel: Bool?
+ /// Whether the carousel can be navigated.
+ ///
+ /// This can be used to temporarily disable the carousel to only allow
+ /// navigating it in a certain state.
+ var interactive: Bool?
+ /// The number of pages in a `AdwCarousel`.
+ var nPages: UInt?
+ /// Page reveal duration, in milliseconds.
+ ///
+ /// Reveal duration is used when animating adding or removing pages.
+ var revealDuration: UInt?
+ /// Spacing between pages in pixels.
+ var spacing: UInt?
+ /// This signal is emitted after a page has been changed.
+ ///
+ /// It can be used to implement "infinite scrolling" by amending the pages
+ /// after every scroll. Note that an empty carousel is indicated by
+ /// `(int)index == -1`.
+ var pageChanged: (() -> Void)?
+ /// The dynamic widget elements.
+ var elements: [Element]
+ /// The dynamic widget content.
+ var content: (Element) -> Body
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `Carousel`.
+ public init(_ elements: [Element], @ViewBuilder content: @escaping (Element) -> Body) {
+ self.elements = elements
+ self.content = content
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_carousel_new()?.opaque())
+ update(storage, modifiers: modifiers)
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let pageChanged {
+ storage.connectSignal(name: "page-changed") {
+ pageChanged()
+ }
+ }
+ storage.modify { widget in
+ if let allowLongSwipes {
+ adw_carousel_set_allow_long_swipes(widget, allowLongSwipes.cBool)
+ }
+ if let allowMouseDrag {
+ adw_carousel_set_allow_mouse_drag(widget, allowMouseDrag.cBool)
+ }
+ if let allowScrollWheel {
+ adw_carousel_set_allow_scroll_wheel(widget, allowScrollWheel.cBool)
+ }
+ if let interactive {
+ adw_carousel_set_interactive(widget, interactive.cBool)
+ }
+ if let revealDuration {
+ adw_carousel_set_reveal_duration(widget, revealDuration.cInt)
+ }
+ if let spacing {
+ adw_carousel_set_spacing(widget, spacing.cInt)
+ }
+
+ var contentStorage: [ViewStorage] = storage.content[.mainContent] ?? []
+ let old = storage.fields["element"] as? [Element] ?? []
+ old.identifiableTransform(
+ to: elements,
+ functions: .init { index, element in
+ let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers)
+ adw_carousel_remove(widget, adw_carousel_get_nth_page(widget, UInt(index).cInt))
+ adw_carousel_insert(widget, child.pointer?.cast(), index.cInt)
+ contentStorage.remove(at: index)
+ contentStorage.insert(child, at: index)
+ } delete: { index in
+ adw_carousel_remove(widget, adw_carousel_get_nth_page(widget, UInt(index).cInt))
+ contentStorage.remove(at: index)
+ } insert: { index, element in
+ let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers)
+ adw_carousel_insert(widget, child.pointer?.cast(), index.cInt)
+ contentStorage.insert(child, at: index)
+ }
+ )
+ storage.fields["element"] = elements
+ storage.content[.mainContent] = contentStorage
+ for (index, element) in elements.enumerated() {
+ content(element).widget(modifiers: modifiers).update(contentStorage[index], modifiers: modifiers)
+ }
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// Whether to allow swiping for more than one page at a time.
+ ///
+ /// If the value is `FALSE`, each swipe can only move to the adjacent pages.
+ public func allowLongSwipes(_ allowLongSwipes: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.allowLongSwipes = allowLongSwipes
+
+ return newSelf
+ }
+
+ /// Sets whether the `AdwCarousel` can be dragged with mouse pointer.
+ ///
+ /// If the value is `FALSE`, dragging is only available on touch.
+ public func allowMouseDrag(_ allowMouseDrag: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.allowMouseDrag = allowMouseDrag
+
+ return newSelf
+ }
+
+ /// Whether the widget will respond to scroll wheel events.
+ ///
+ /// If the value is `FALSE`, wheel events will be ignored.
+ public func allowScrollWheel(_ allowScrollWheel: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.allowScrollWheel = allowScrollWheel
+
+ return newSelf
+ }
+
+ /// Whether the carousel can be navigated.
+ ///
+ /// This can be used to temporarily disable the carousel to only allow
+ /// navigating it in a certain state.
+ public func interactive(_ interactive: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.interactive = interactive
+
+ return newSelf
+ }
+
+ /// The number of pages in a `AdwCarousel`.
+ public func nPages(_ nPages: UInt?) -> Self {
+ var newSelf = self
+ newSelf.nPages = nPages
+
+ return newSelf
+ }
+
+ /// Page reveal duration, in milliseconds.
+ ///
+ /// Reveal duration is used when animating adding or removing pages.
+ public func revealDuration(_ revealDuration: UInt?) -> Self {
+ var newSelf = self
+ newSelf.revealDuration = revealDuration
+
+ return newSelf
+ }
+
+ /// Spacing between pages in pixels.
+ public func spacing(_ spacing: UInt?) -> Self {
+ var newSelf = self
+ newSelf.spacing = spacing
+
+ return newSelf
+ }
+
+ /// This signal is emitted after a page has been changed.
+ ///
+ /// It can be used to implement "infinite scrolling" by amending the pages
+ /// after every scroll. Note that an empty carousel is indicated by
+ /// `(int)index == -1`.
+ public func pageChanged(_ pageChanged: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.pageChanged = pageChanged
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/CenterBox.swift b/Sources/Adwaita/View/Generated/CenterBox.swift
new file mode 100644
index 0000000..7055e4d
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/CenterBox.swift
@@ -0,0 +1,183 @@
+//
+// CenterBox.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// `GtkCenterBox` arranges three children in a row, keeping the middle child
+/// centered as well as possible.
+///
+/// ![An example GtkCenterBox](centerbox.png)
+///
+/// To add children to `GtkCenterBox`, use [method@Gtk.CenterBox.set_start_widget],
+/// [method@Gtk.CenterBox.set_center_widget] and
+/// [method@Gtk.CenterBox.set_end_widget].
+///
+/// The sizing and positioning of children can be influenced with the
+/// align and expand properties of the children.
+///
+/// # GtkCenterBox as GtkBuildable
+///
+/// The `GtkCenterBox` implementation of the `GtkBuildable` interface
+/// supports placing children in the 3 positions by specifying “start”, “center”
+/// or “end” as the “type” attribute of a `` element.
+///
+/// # CSS nodes
+///
+/// `GtkCenterBox` uses a single CSS node with the name “box”,
+///
+/// The first child of the `GtkCenterBox` will be allocated depending on the
+/// text direction, i.e. in left-to-right layouts it will be allocated on the
+/// left and in right-to-left layouts on the right.
+///
+/// In vertical orientation, the nodes of the children are arranged from top to
+/// bottom.
+///
+/// # Accessibility
+///
+/// Until GTK 4.10, `GtkCenterBox` used the `GTK_ACCESSIBLE_ROLE_GROUP` role.
+///
+/// Starting from GTK 4.12, `GtkCenterBox` uses the `GTK_ACCESSIBLE_ROLE_GENERIC` role.
+public struct CenterBox: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The widget that is placed at the center position.
+ var centerWidget: (() -> Body)?
+ /// The widget that is placed at the end position.
+ ///
+ /// In vertical orientation, the end position is at the bottom.
+ /// In horizontal orientation, the end position is at the trailing
+ /// edge wrt. to the text direction.
+ var endWidget: (() -> Body)?
+ /// Whether to shrink the center widget after other children.
+ ///
+ /// By default, when there's no space to give all three children their
+ /// natural widths, the start and end widgets start shrinking and the
+ /// center child keeps natural width until they reach minimum width.
+ ///
+ /// If set to `FALSE`, start and end widgets keep natural width and the
+ /// center widget starts shrinking instead.
+ var shrinkCenterLast: Bool?
+ /// The widget that is placed at the start position.
+ ///
+ /// In vertical orientation, the start position is at the top.
+ /// In horizontal orientation, the start position is at the leading
+ /// edge wrt. to the text direction.
+ var startWidget: (() -> Body)?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `CenterBox`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(gtk_center_box_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let centerWidgetStorage = centerWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["centerWidget"] = [centerWidgetStorage]
+ gtk_center_box_set_center_widget(storage.pointer, centerWidgetStorage.pointer?.cast())
+ }
+ if let endWidgetStorage = endWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["endWidget"] = [endWidgetStorage]
+ gtk_center_box_set_end_widget(storage.pointer, endWidgetStorage.pointer?.cast())
+ }
+ if let startWidgetStorage = startWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["startWidget"] = [startWidgetStorage]
+ gtk_center_box_set_start_widget(storage.pointer, startWidgetStorage.pointer?.cast())
+ }
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let widget = storage.content["centerWidget"]?.first {
+ centerWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let widget = storage.content["endWidget"]?.first {
+ endWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let shrinkCenterLast {
+ gtk_center_box_set_shrink_center_last(widget, shrinkCenterLast.cBool)
+ }
+ if let widget = storage.content["startWidget"]?.first {
+ startWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The widget that is placed at the center position.
+ public func centerWidget(@ViewBuilder _ centerWidget: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.centerWidget = centerWidget
+
+ return newSelf
+ }
+
+ /// The widget that is placed at the end position.
+ ///
+ /// In vertical orientation, the end position is at the bottom.
+ /// In horizontal orientation, the end position is at the trailing
+ /// edge wrt. to the text direction.
+ public func endWidget(@ViewBuilder _ endWidget: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.endWidget = endWidget
+
+ return newSelf
+ }
+
+ /// Whether to shrink the center widget after other children.
+ ///
+ /// By default, when there's no space to give all three children their
+ /// natural widths, the start and end widgets start shrinking and the
+ /// center child keeps natural width until they reach minimum width.
+ ///
+ /// If set to `FALSE`, start and end widgets keep natural width and the
+ /// center widget starts shrinking instead.
+ public func shrinkCenterLast(_ shrinkCenterLast: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.shrinkCenterLast = shrinkCenterLast
+
+ return newSelf
+ }
+
+ /// The widget that is placed at the start position.
+ ///
+ /// In vertical orientation, the start position is at the top.
+ /// In horizontal orientation, the start position is at the leading
+ /// edge wrt. to the text direction.
+ public func startWidget(@ViewBuilder _ startWidget: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.startWidget = startWidget
+
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/CheckButton.swift b/Sources/Adwaita/View/Generated/CheckButton.swift
new file mode 100644
index 0000000..e8198b8
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/CheckButton.swift
@@ -0,0 +1,246 @@
+//
+// CheckButton.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A `GtkCheckButton` places a label next to an indicator.
+///
+/// ![Example GtkCheckButtons](check-button.png)
+///
+/// A `GtkCheckButton` is created by calling either [ctor@Gtk.CheckButton.new]
+/// or [ctor@Gtk.CheckButton.new_with_label].
+///
+/// The state of a `GtkCheckButton` can be set specifically using
+/// [method@Gtk.CheckButton.set_active], and retrieved using
+/// [method@Gtk.CheckButton.get_active].
+///
+/// # Inconsistent state
+///
+/// In addition to "on" and "off", check buttons can be an
+/// "in between" state that is neither on nor off. This can be used
+/// e.g. when the user has selected a range of elements (such as some
+/// text or spreadsheet cells) that are affected by a check button,
+/// and the current values in that range are inconsistent.
+///
+/// To set a `GtkCheckButton` to inconsistent state, use
+/// [method@Gtk.CheckButton.set_inconsistent].
+///
+/// # Grouping
+///
+/// Check buttons can be grouped together, to form mutually exclusive
+/// groups - only one of the buttons can be toggled at a time, and toggling
+/// another one will switch the currently toggled one off.
+///
+/// Grouped check buttons use a different indicator, and are commonly referred
+/// to as *radio buttons*.
+///
+/// ![Example GtkCheckButtons](radio-button.png)
+///
+/// To add a `GtkCheckButton` to a group, use [method@Gtk.CheckButton.set_group].
+///
+/// When the code must keep track of the state of a group of radio buttons, it
+/// is recommended to keep track of such state through a stateful
+/// `GAction` with a target for each button. Using the `toggled` signals to keep
+/// track of the group changes and state is discouraged.
+///
+/// # CSS nodes
+///
+/// ```
+/// checkbutton[.text-button]
+/// ├── check
+/// ╰── [label]
+/// ```
+///
+/// A `GtkCheckButton` has a main node with name checkbutton. If the
+/// [property@Gtk.CheckButton:label] or [property@Gtk.CheckButton:child]
+/// properties are set, it contains a child widget. The indicator node
+/// is named check when no group is set, and radio if the checkbutton
+/// is grouped together with other checkbuttons.
+///
+/// # Accessibility
+///
+/// `GtkCheckButton` uses the %GTK_ACCESSIBLE_ROLE_CHECKBOX role.
+public struct CheckButton: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// If the check button is active.
+ ///
+ /// Setting `active` to %TRUE will add the `:checked:` state to both
+ /// the check button and the indicator CSS node.
+ var active: Binding?
+ /// The child widget.
+ var child: (() -> Body)?
+ /// If the check button is in an “in between” state.
+ ///
+ /// The inconsistent state only affects visual appearance,
+ /// not the semantics of the button.
+ var inconsistent: Bool?
+ /// Text of the label inside the check button, if it contains a label widget.
+ var label: String?
+ /// If set, an underline in the text indicates that the following
+ /// character is to be used as mnemonic.
+ var useUnderline: Bool?
+ /// Emitted to when the check button is activated.
+ ///
+ /// The `::activate` signal on `GtkCheckButton` is an action signal and
+ /// emitting it causes the button to animate press then release.
+ ///
+ /// Applications should never connect to this signal, but use the
+ /// [signal@Gtk.CheckButton::toggled] signal.
+ ///
+ /// The default bindings for this signal are all forms of the
+ /// ␣ and Enter keys.
+ var activate: (() -> Void)?
+ /// Emitted when the buttons's [property@Gtk.CheckButton:active]
+ /// property changes.
+ var toggled: (() -> Void)?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `CheckButton`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(gtk_check_button_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["child"] = [childStorage]
+ gtk_check_button_set_child(storage.pointer?.cast(), childStorage.pointer?.cast())
+ }
+
+
+ storage.notify(name: "active") {
+ active?.wrappedValue = gtk_check_button_get_active(storage.pointer?.cast()) != 0
+ }
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let activate {
+ storage.connectSignal(name: "activate") {
+ activate()
+ }
+ }
+ if let toggled {
+ storage.connectSignal(name: "toggled") {
+ toggled()
+ }
+ }
+ storage.modify { widget in
+ if let active {
+ gtk_check_button_set_active(widget?.cast(), active.wrappedValue.cBool)
+ }
+ if let widget = storage.content["child"]?.first {
+ child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let inconsistent {
+ gtk_check_button_set_inconsistent(widget?.cast(), inconsistent.cBool)
+ }
+ if let label, storage.content["child"] == nil {
+ gtk_check_button_set_label(widget?.cast(), label)
+ }
+ if let useUnderline {
+ gtk_check_button_set_use_underline(widget?.cast(), useUnderline.cBool)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// If the check button is active.
+ ///
+ /// Setting `active` to %TRUE will add the `:checked:` state to both
+ /// the check button and the indicator CSS node.
+ public func active(_ active: Binding?) -> Self {
+ var newSelf = self
+ newSelf.active = active
+
+ return newSelf
+ }
+
+ /// The child widget.
+ public func child(@ViewBuilder _ child: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.child = child
+
+ return newSelf
+ }
+
+ /// If the check button is in an “in between” state.
+ ///
+ /// The inconsistent state only affects visual appearance,
+ /// not the semantics of the button.
+ public func inconsistent(_ inconsistent: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.inconsistent = inconsistent
+
+ return newSelf
+ }
+
+ /// Text of the label inside the check button, if it contains a label widget.
+ public func label(_ label: String?) -> Self {
+ var newSelf = self
+ newSelf.label = label
+
+ return newSelf
+ }
+
+ /// If set, an underline in the text indicates that the following
+ /// character is to be used as mnemonic.
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+ /// Emitted to when the check button is activated.
+ ///
+ /// The `::activate` signal on `GtkCheckButton` is an action signal and
+ /// emitting it causes the button to animate press then release.
+ ///
+ /// Applications should never connect to this signal, but use the
+ /// [signal@Gtk.CheckButton::toggled] signal.
+ ///
+ /// The default bindings for this signal are all forms of the
+ /// ␣ and Enter keys.
+ public func activate(_ activate: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.activate = activate
+ return newSelf
+ }
+
+ /// Emitted when the buttons's [property@Gtk.CheckButton:active]
+ /// property changes.
+ public func toggled(_ toggled: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.toggled = toggled
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/Clamp.swift b/Sources/Adwaita/View/Generated/Clamp.swift
new file mode 100644
index 0000000..ee0a6c2
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/Clamp.swift
@@ -0,0 +1,146 @@
+//
+// Clamp.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A widget constraining its child to a given size.
+///
+///
+///
+/// The `AdwClamp` widget constrains the size of the widget it contains to a
+/// given maximum size. It will constrain the width if it is horizontal, or the
+/// height if it is vertical. The expansion of the child from its minimum to its
+/// maximum size is eased out for a smooth transition.
+///
+/// If the child requires more than the requested maximum size, it will be
+/// allocated the minimum size it can fit in instead.
+///
+/// `AdwClamp` can scale with the text scale factor, use the
+/// [property@ClampLayout:unit] property to enable that behavior.
+///
+/// ## CSS nodes
+///
+/// `AdwClamp` has a single CSS node with name `clamp`.
+public struct Clamp: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The child widget of the `AdwClamp`.
+ var child: (() -> Body)?
+ /// The maximum size allocated to the child.
+ ///
+ /// It is the width if the clamp is horizontal, or the height if it is vertical.
+ var maximumSize: Int?
+ /// The size above which the child is clamped.
+ ///
+ /// Starting from this size, the clamp will tighten its grip on the child,
+ /// slowly allocating less and less of the available size up to the maximum
+ /// allocated size. Below that threshold and below the maximum size, the child
+ /// will be allocated all the available size.
+ ///
+ /// If the threshold is greater than the maximum size to allocate to the child,
+ /// the child will be allocated all the size up to the maximum.
+ /// If the threshold is lower than the minimum size to allocate to the child,
+ /// that size will be used as the tightening threshold.
+ ///
+ /// Effectively, tightening the grip on the child before it reaches its maximum
+ /// size makes transitions to and from the maximum size smoother when resizing.
+ var tighteningThreshold: Int?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `Clamp`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_clamp_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["child"] = [childStorage]
+ adw_clamp_set_child(storage.pointer, childStorage.pointer?.cast())
+ }
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let widget = storage.content["child"]?.first {
+ child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let maximumSize {
+ adw_clamp_set_maximum_size(widget, maximumSize.cInt)
+ }
+ if let tighteningThreshold {
+ adw_clamp_set_tightening_threshold(widget, tighteningThreshold.cInt)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The child widget of the `AdwClamp`.
+ public func child(@ViewBuilder _ child: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.child = child
+
+ return newSelf
+ }
+
+ /// The maximum size allocated to the child.
+ ///
+ /// It is the width if the clamp is horizontal, or the height if it is vertical.
+ public func maximumSize(_ maximumSize: Int?) -> Self {
+ var newSelf = self
+ newSelf.maximumSize = maximumSize
+
+ return newSelf
+ }
+
+ /// The size above which the child is clamped.
+ ///
+ /// Starting from this size, the clamp will tighten its grip on the child,
+ /// slowly allocating less and less of the available size up to the maximum
+ /// allocated size. Below that threshold and below the maximum size, the child
+ /// will be allocated all the available size.
+ ///
+ /// If the threshold is greater than the maximum size to allocate to the child,
+ /// the child will be allocated all the size up to the maximum.
+ /// If the threshold is lower than the minimum size to allocate to the child,
+ /// that size will be used as the tightening threshold.
+ ///
+ /// Effectively, tightening the grip on the child before it reaches its maximum
+ /// size makes transitions to and from the maximum size smoother when resizing.
+ public func tighteningThreshold(_ tighteningThreshold: Int?) -> Self {
+ var newSelf = self
+ newSelf.tighteningThreshold = tighteningThreshold
+
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/ComboRow.swift b/Sources/Adwaita/View/Generated/ComboRow.swift
new file mode 100644
index 0000000..9c7804c
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/ComboRow.swift
@@ -0,0 +1,380 @@
+//
+// ComboRow.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A [class@Gtk.ListBoxRow] used to choose from a list of items.
+///
+///
+///
+/// The `AdwComboRow` widget allows the user to choose from a list of valid
+/// choices. The row displays the selected choice. When activated, the row
+/// displays a popover which allows the user to make a new choice.
+///
+/// Example of an `AdwComboRow` UI definition:
+/// ```xml
+/// Combo RowFooBarBaz
+/// ```
+///
+/// The [property@ComboRow:selected] and [property@ComboRow:selected-item]
+/// properties can be used to keep track of the selected item and react to their
+/// changes.
+///
+/// `AdwComboRow` mirrors [class@Gtk.DropDown], see that widget for details.
+///
+/// `AdwComboRow` is [property@Gtk.ListBoxRow:activatable] if a model is set.
+///
+/// ## CSS nodes
+///
+/// `AdwComboRow` has a main CSS node with name `row` and the `.combo` style
+/// class.
+///
+/// Its popover has the node named `popover` with the `.menu` style class, it
+/// contains a [class@Gtk.ScrolledWindow], which in turn contains a
+/// [class@Gtk.ListView], both are accessible via their regular nodes.
+///
+/// ## Accessibility
+///
+/// `AdwComboRow` uses the `GTK_ACCESSIBLE_ROLE_COMBO_BOX` role.
+public struct ComboRow: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// Whether to show a search entry in the popup.
+ ///
+ /// If set to `TRUE`, a search entry will be shown in the popup that
+ /// allows to search for items in the list.
+ ///
+ /// Search requires [property@ComboRow:expression] to be set.
+ var enableSearch: Bool?
+ /// The position of the selected item.
+ ///
+ /// If no item is selected, the property has the value
+ /// [const@Gtk.INVALID_LIST_POSITION]
+ var selected: Binding?
+ /// Whether to use the current value as the subtitle.
+ ///
+ /// If you use a custom list item factory, you will need to give the row a
+ /// name conversion expression with [property@ComboRow:expression].
+ ///
+ /// If set to `TRUE`, you should not access [property@ActionRow:subtitle].
+ ///
+ /// The subtitle is interpreted as Pango markup if
+ /// [property@PreferencesRow:use-markup] is set to `TRUE`.
+ var useSubtitle: Bool?
+ /// The widget to activate when the row is activated.
+ ///
+ /// The row can be activated either by clicking on it, calling
+ /// [method@ActionRow.activate], or via mnemonics in the title.
+ /// See the [property@PreferencesRow:use-underline] property to enable
+ /// mnemonics.
+ ///
+ /// The target widget will be activated by emitting the
+ /// [signal@Gtk.Widget::mnemonic-activate] signal on it.
+ var activatableWidget: (() -> Body)?
+ /// The icon name for this row.
+ var iconName: String?
+ /// The subtitle for this row.
+ ///
+ /// The subtitle is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ var subtitle: String?
+ /// The number of lines at the end of which the subtitle label will be
+ /// ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ var subtitleLines: Int?
+ /// Whether the user can copy the subtitle from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ var subtitleSelectable: Bool?
+ /// The number of lines at the end of which the title label will be ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ var titleLines: Int?
+ /// The title of the preference represented by this row.
+ ///
+ /// The title is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ var title: String?
+ /// Whether the user can copy the title from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ var titleSelectable: Bool?
+ /// Whether to use Pango markup for the title label.
+ ///
+ /// Subclasses may also use it for other labels, such as subtitle.
+ ///
+ /// See also [func@Pango.parse_markup].
+ var useMarkup: Bool?
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ var useUnderline: Bool?
+ /// This signal is emitted after the row has been activated.
+ var activated: (() -> Void)?
+ /// The body for the widget "suffix".
+ var suffix: () -> Body = { [] }
+ /// The body for the widget "prefix".
+ var prefix: () -> Body = { [] }
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `ComboRow`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_combo_row_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let activatableWidgetStorage = activatableWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["activatableWidget"] = [activatableWidgetStorage]
+ adw_action_row_set_activatable_widget(storage.pointer?.cast(), activatableWidgetStorage.pointer?.cast())
+ }
+
+ var suffixStorage: [ViewStorage] = []
+ for view in suffix() {
+ suffixStorage.append(view.storage(modifiers: modifiers))
+ adw_action_row_add_suffix(storage.pointer?.cast(), suffixStorage.last?.pointer?.cast())
+ }
+ storage.content["suffix"] = suffixStorage
+ var prefixStorage: [ViewStorage] = []
+ for view in prefix() {
+ prefixStorage.append(view.storage(modifiers: modifiers))
+ adw_action_row_add_prefix(storage.pointer?.cast(), prefixStorage.last?.pointer?.cast())
+ }
+ storage.content["prefix"] = prefixStorage
+
+ storage.notify(name: "selected") {
+ selected?.wrappedValue = .init(adw_combo_row_get_selected(storage.pointer?.cast()))
+ }
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let activated {
+ storage.connectSignal(name: "activated") {
+ activated()
+ }
+ }
+ storage.modify { widget in
+ if let enableSearch {
+ adw_combo_row_set_enable_search(widget?.cast(), enableSearch.cBool)
+ }
+ if let selected {
+ adw_combo_row_set_selected(widget?.cast(), selected.wrappedValue.cInt)
+ }
+ if let useSubtitle {
+ adw_combo_row_set_use_subtitle(widget?.cast(), useSubtitle.cBool)
+ }
+ if let widget = storage.content["activatableWidget"]?.first {
+ activatableWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let iconName {
+ adw_action_row_set_icon_name(widget?.cast(), iconName)
+ }
+ if let subtitle {
+ adw_action_row_set_subtitle(widget?.cast(), subtitle)
+ }
+ if let subtitleLines {
+ adw_action_row_set_subtitle_lines(widget?.cast(), subtitleLines.cInt)
+ }
+ if let subtitleSelectable {
+ adw_action_row_set_subtitle_selectable(widget?.cast(), subtitleSelectable.cBool)
+ }
+ if let titleLines {
+ adw_action_row_set_title_lines(widget?.cast(), titleLines.cInt)
+ }
+ if let title {
+ adw_preferences_row_set_title(widget?.cast(), title)
+ }
+ if let titleSelectable {
+ adw_preferences_row_set_title_selectable(widget?.cast(), titleSelectable.cBool)
+ }
+ if let useMarkup {
+ adw_preferences_row_set_use_markup(widget?.cast(), useMarkup.cBool)
+ }
+ if let useUnderline {
+ adw_preferences_row_set_use_underline(widget?.cast(), useUnderline.cBool)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// Whether to show a search entry in the popup.
+ ///
+ /// If set to `TRUE`, a search entry will be shown in the popup that
+ /// allows to search for items in the list.
+ ///
+ /// Search requires [property@ComboRow:expression] to be set.
+ public func enableSearch(_ enableSearch: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.enableSearch = enableSearch
+
+ return newSelf
+ }
+
+ /// The position of the selected item.
+ ///
+ /// If no item is selected, the property has the value
+ /// [const@Gtk.INVALID_LIST_POSITION]
+ public func selected(_ selected: Binding?) -> Self {
+ var newSelf = self
+ newSelf.selected = selected
+
+ return newSelf
+ }
+
+ /// Whether to use the current value as the subtitle.
+ ///
+ /// If you use a custom list item factory, you will need to give the row a
+ /// name conversion expression with [property@ComboRow:expression].
+ ///
+ /// If set to `TRUE`, you should not access [property@ActionRow:subtitle].
+ ///
+ /// The subtitle is interpreted as Pango markup if
+ /// [property@PreferencesRow:use-markup] is set to `TRUE`.
+ public func useSubtitle(_ useSubtitle: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useSubtitle = useSubtitle
+
+ return newSelf
+ }
+
+ /// The widget to activate when the row is activated.
+ ///
+ /// The row can be activated either by clicking on it, calling
+ /// [method@ActionRow.activate], or via mnemonics in the title.
+ /// See the [property@PreferencesRow:use-underline] property to enable
+ /// mnemonics.
+ ///
+ /// The target widget will be activated by emitting the
+ /// [signal@Gtk.Widget::mnemonic-activate] signal on it.
+ public func activatableWidget(@ViewBuilder _ activatableWidget: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.activatableWidget = activatableWidget
+
+ return newSelf
+ }
+
+ /// The icon name for this row.
+ public func iconName(_ iconName: String?) -> Self {
+ var newSelf = self
+ newSelf.iconName = iconName
+
+ return newSelf
+ }
+
+ /// The subtitle for this row.
+ ///
+ /// The subtitle is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ public func subtitle(_ subtitle: String?) -> Self {
+ var newSelf = self
+ newSelf.subtitle = subtitle
+
+ return newSelf
+ }
+
+ /// The number of lines at the end of which the subtitle label will be
+ /// ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ public func subtitleLines(_ subtitleLines: Int?) -> Self {
+ var newSelf = self
+ newSelf.subtitleLines = subtitleLines
+
+ return newSelf
+ }
+
+ /// Whether the user can copy the subtitle from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ public func subtitleSelectable(_ subtitleSelectable: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.subtitleSelectable = subtitleSelectable
+
+ return newSelf
+ }
+
+ /// The number of lines at the end of which the title label will be ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ public func titleLines(_ titleLines: Int?) -> Self {
+ var newSelf = self
+ newSelf.titleLines = titleLines
+
+ return newSelf
+ }
+
+ /// The title of the preference represented by this row.
+ ///
+ /// The title is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ public func title(_ title: String?) -> Self {
+ var newSelf = self
+ newSelf.title = title
+
+ return newSelf
+ }
+
+ /// Whether the user can copy the title from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ public func titleSelectable(_ titleSelectable: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.titleSelectable = titleSelectable
+
+ return newSelf
+ }
+
+ /// Whether to use Pango markup for the title label.
+ ///
+ /// Subclasses may also use it for other labels, such as subtitle.
+ ///
+ /// See also [func@Pango.parse_markup].
+ public func useMarkup(_ useMarkup: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useMarkup = useMarkup
+
+ return newSelf
+ }
+
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+ /// This signal is emitted after the row has been activated.
+ public func activated(_ activated: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.activated = activated
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/EntryRow.swift b/Sources/Adwaita/View/Generated/EntryRow.swift
new file mode 100644
index 0000000..ef2bf50
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/EntryRow.swift
@@ -0,0 +1,293 @@
+//
+// EntryRow.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A [class@Gtk.ListBoxRow] with an embedded text entry.
+///
+///
+///
+/// `AdwEntryRow` has a title that doubles as placeholder text. It shows an icon
+/// indicating that it's editable and can receive additional widgets before or
+/// after the editable part.
+///
+/// If [property@EntryRow:show-apply-button] is set to `TRUE`, `AdwEntryRow` can
+/// show an apply button when editing its contents. This can be useful if
+/// changing its contents can result in an expensive operation, such as network
+/// activity.
+///
+/// `AdwEntryRow` provides only minimal API and should be used with the
+/// [iface@Gtk.Editable] API.
+///
+/// See also [class@PasswordEntryRow].
+///
+/// ## AdwEntryRow as GtkBuildable
+///
+/// The `AdwEntryRow` implementation of the [iface@Gtk.Buildable] interface
+/// supports adding a child at its end by specifying “suffix” or omitting the
+/// “type” attribute of a element.
+///
+/// It also supports adding a child as a prefix widget by specifying “prefix” as
+/// the “type” attribute of a element.
+///
+/// ## CSS nodes
+///
+/// `AdwEntryRow` has a single CSS node with name `row` and the `.entry` style
+/// class.
+public struct EntryRow: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// Whether activating the embedded entry can activate the default widget.
+ var activatesDefault: Bool?
+ /// Whether to suggest emoji replacements on the entry row.
+ ///
+ /// Emoji replacement is done with :-delimited names, like `:heart:`.
+ var enableEmojiCompletion: Bool?
+ /// Whether to show the apply button.
+ ///
+ /// When set to `TRUE`, typing text in the entry will reveal an apply button.
+ /// Clicking it or pressing the Enter key will hide the button and
+ /// emit the [signal@EntryRow::apply] signal.
+ ///
+ /// This is useful if changing the entry contents can trigger an expensive
+ /// operation, e.g. network activity, to avoid triggering it after typing every
+ /// character.
+ var showApplyButton: Bool?
+ /// The title of the preference represented by this row.
+ ///
+ /// The title is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ var title: String?
+ /// Whether the user can copy the title from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ var titleSelectable: Bool?
+ /// Whether to use Pango markup for the title label.
+ ///
+ /// Subclasses may also use it for other labels, such as subtitle.
+ ///
+ /// See also [func@Pango.parse_markup].
+ var useMarkup: Bool?
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ var useUnderline: Bool?
+ /// Emitted when the apply button is pressed.
+ ///
+ /// See [property@EntryRow:show-apply-button].
+ var apply: (() -> Void)?
+ /// Emitted when the embedded entry is activated.
+ var entryActivated: (() -> Void)?
+ /// The body for the widget "suffix".
+ var suffix: () -> Body = { [] }
+ /// The body for the widget "prefix".
+ var prefix: () -> Body = { [] }
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `EntryRow`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_entry_row_new()?.opaque())
+ update(storage, modifiers: modifiers)
+
+ var suffixStorage: [ViewStorage] = []
+ for view in suffix() {
+ suffixStorage.append(view.storage(modifiers: modifiers))
+ adw_entry_row_add_suffix(storage.pointer?.cast(), suffixStorage.last?.pointer?.cast())
+ }
+ storage.content["suffix"] = suffixStorage
+ var prefixStorage: [ViewStorage] = []
+ for view in prefix() {
+ prefixStorage.append(view.storage(modifiers: modifiers))
+ adw_entry_row_add_prefix(storage.pointer?.cast(), prefixStorage.last?.pointer?.cast())
+ }
+ storage.content["prefix"] = prefixStorage
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let apply {
+ storage.connectSignal(name: "apply") {
+ apply()
+ }
+ }
+ if let entryActivated {
+ storage.connectSignal(name: "entry-activated") {
+ entryActivated()
+ }
+ }
+ storage.modify { widget in
+ if let activatesDefault {
+ adw_entry_row_set_activates_default(widget?.cast(), activatesDefault.cBool)
+ }
+ if let enableEmojiCompletion {
+ adw_entry_row_set_enable_emoji_completion(widget?.cast(), enableEmojiCompletion.cBool)
+ }
+ if let showApplyButton {
+ adw_entry_row_set_show_apply_button(widget?.cast(), showApplyButton.cBool)
+ }
+ if let title {
+ adw_preferences_row_set_title(widget?.cast(), title)
+ }
+ if let titleSelectable {
+ adw_preferences_row_set_title_selectable(widget?.cast(), titleSelectable.cBool)
+ }
+ if let useMarkup {
+ adw_preferences_row_set_use_markup(widget?.cast(), useMarkup.cBool)
+ }
+ if let useUnderline {
+ adw_preferences_row_set_use_underline(widget?.cast(), useUnderline.cBool)
+ }
+
+ if let suffixStorage = storage.content["suffix"] {
+ for (index, view) in suffix().enumerated() {
+ if let storage = suffixStorage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+ if let prefixStorage = storage.content["prefix"] {
+ for (index, view) in prefix().enumerated() {
+ if let storage = prefixStorage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// Whether activating the embedded entry can activate the default widget.
+ public func activatesDefault(_ activatesDefault: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.activatesDefault = activatesDefault
+
+ return newSelf
+ }
+
+ /// Whether to suggest emoji replacements on the entry row.
+ ///
+ /// Emoji replacement is done with :-delimited names, like `:heart:`.
+ public func enableEmojiCompletion(_ enableEmojiCompletion: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.enableEmojiCompletion = enableEmojiCompletion
+
+ return newSelf
+ }
+
+ /// Whether to show the apply button.
+ ///
+ /// When set to `TRUE`, typing text in the entry will reveal an apply button.
+ /// Clicking it or pressing the Enter key will hide the button and
+ /// emit the [signal@EntryRow::apply] signal.
+ ///
+ /// This is useful if changing the entry contents can trigger an expensive
+ /// operation, e.g. network activity, to avoid triggering it after typing every
+ /// character.
+ public func showApplyButton(_ showApplyButton: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.showApplyButton = showApplyButton
+
+ return newSelf
+ }
+
+ /// The title of the preference represented by this row.
+ ///
+ /// The title is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ public func title(_ title: String?) -> Self {
+ var newSelf = self
+ newSelf.title = title
+
+ return newSelf
+ }
+
+ /// Whether the user can copy the title from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ public func titleSelectable(_ titleSelectable: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.titleSelectable = titleSelectable
+
+ return newSelf
+ }
+
+ /// Whether to use Pango markup for the title label.
+ ///
+ /// Subclasses may also use it for other labels, such as subtitle.
+ ///
+ /// See also [func@Pango.parse_markup].
+ public func useMarkup(_ useMarkup: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useMarkup = useMarkup
+
+ return newSelf
+ }
+
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+ /// Emitted when the apply button is pressed.
+ ///
+ /// See [property@EntryRow:show-apply-button].
+ public func apply(_ apply: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.apply = apply
+ return newSelf
+ }
+
+ /// Emitted when the embedded entry is activated.
+ public func entryActivated(_ entryActivated: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.entryActivated = entryActivated
+ return newSelf
+ }
+
+ /// Set the body for "suffix".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func suffix(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.suffix = body
+ return newSelf
+ }
+ /// Set the body for "prefix".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func prefix(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.prefix = body
+ return newSelf
+ }
+}
diff --git a/Sources/Adwaita/View/Generated/ExpanderRow.swift b/Sources/Adwaita/View/Generated/ExpanderRow.swift
new file mode 100644
index 0000000..cb2450c
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/ExpanderRow.swift
@@ -0,0 +1,332 @@
+//
+// ExpanderRow.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A [class@Gtk.ListBoxRow] used to reveal widgets.
+///
+///
+///
+/// The `AdwExpanderRow` widget allows the user to reveal or hide widgets below
+/// it. It also allows the user to enable the expansion of the row, allowing to
+/// disable all that the row contains.
+///
+/// ## AdwExpanderRow as GtkBuildable
+///
+/// The `AdwExpanderRow` implementation of the [iface@Gtk.Buildable] interface
+/// supports adding a child as an suffix widget by specifying “suffix” as the
+/// “type” attribute of a element.
+///
+/// It also supports adding it as a prefix widget by specifying “prefix” as the
+/// “type” attribute of a element.
+///
+/// ## CSS nodes
+///
+/// `AdwExpanderRow` has a main CSS node with name `row` and the `.expander`
+/// style class. It has the `.empty` style class when it contains no children.
+///
+/// It contains the subnodes `row.header` for its main embedded row,
+/// `list.nested` for the list it can expand, and `image.expander-row-arrow` for
+/// its arrow.
+public struct ExpanderRow: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// Whether expansion is enabled.
+ var enableExpansion: Binding?
+ /// Whether the row is expanded.
+ var expanded: Binding?
+ /// The icon name for this row.
+ var iconName: String?
+ /// Whether the switch enabling the expansion is visible.
+ var showEnableSwitch: Bool?
+ /// The subtitle for this row.
+ ///
+ /// The subtitle is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ var subtitle: String?
+ /// The number of lines at the end of which the subtitle label will be
+ /// ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ var subtitleLines: Int?
+ /// The number of lines at the end of which the title label will be ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ var titleLines: Int?
+ /// The title of the preference represented by this row.
+ ///
+ /// The title is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ var title: String?
+ /// Whether the user can copy the title from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ var titleSelectable: Bool?
+ /// Whether to use Pango markup for the title label.
+ ///
+ /// Subclasses may also use it for other labels, such as subtitle.
+ ///
+ /// See also [func@Pango.parse_markup].
+ var useMarkup: Bool?
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ var useUnderline: Bool?
+ /// The body for the widget "rows".
+ var rows: () -> Body = { [] }
+ /// The body for the widget "suffix".
+ var suffix: () -> Body = { [] }
+ /// The body for the widget "prefix".
+ var prefix: () -> Body = { [] }
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `ExpanderRow`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_expander_row_new()?.opaque())
+ update(storage, modifiers: modifiers)
+
+ var rowsStorage: [ViewStorage] = []
+ for view in rows() {
+ rowsStorage.append(view.storage(modifiers: modifiers))
+ adw_expander_row_add_row(storage.pointer?.cast(), rowsStorage.last?.pointer?.cast())
+ }
+ storage.content["rows"] = rowsStorage
+ var suffixStorage: [ViewStorage] = []
+ for view in suffix() {
+ suffixStorage.append(view.storage(modifiers: modifiers))
+ adw_expander_row_add_suffix(storage.pointer?.cast(), suffixStorage.last?.pointer?.cast())
+ }
+ storage.content["suffix"] = suffixStorage
+ var prefixStorage: [ViewStorage] = []
+ for view in prefix() {
+ prefixStorage.append(view.storage(modifiers: modifiers))
+ adw_expander_row_add_prefix(storage.pointer?.cast(), prefixStorage.last?.pointer?.cast())
+ }
+ storage.content["prefix"] = prefixStorage
+
+ storage.notify(name: "enable-expansion") {
+ enableExpansion?.wrappedValue = adw_expander_row_get_enable_expansion(storage.pointer?.cast()) != 0
+ }
+ storage.notify(name: "expanded") {
+ expanded?.wrappedValue = adw_expander_row_get_expanded(storage.pointer?.cast()) != 0
+ }
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let enableExpansion {
+ adw_expander_row_set_enable_expansion(widget?.cast(), enableExpansion.wrappedValue.cBool)
+ }
+ if let expanded {
+ adw_expander_row_set_expanded(widget?.cast(), expanded.wrappedValue.cBool)
+ }
+ if let iconName {
+ adw_expander_row_set_icon_name(widget?.cast(), iconName)
+ }
+ if let showEnableSwitch {
+ adw_expander_row_set_show_enable_switch(widget?.cast(), showEnableSwitch.cBool)
+ }
+ if let subtitle {
+ adw_expander_row_set_subtitle(widget?.cast(), subtitle)
+ }
+ if let subtitleLines {
+ adw_expander_row_set_subtitle_lines(widget?.cast(), subtitleLines.cInt)
+ }
+ if let titleLines {
+ adw_expander_row_set_title_lines(widget?.cast(), titleLines.cInt)
+ }
+ if let title {
+ adw_preferences_row_set_title(widget?.cast(), title)
+ }
+ if let titleSelectable {
+ adw_preferences_row_set_title_selectable(widget?.cast(), titleSelectable.cBool)
+ }
+ if let useMarkup {
+ adw_preferences_row_set_use_markup(widget?.cast(), useMarkup.cBool)
+ }
+ if let useUnderline {
+ adw_preferences_row_set_use_underline(widget?.cast(), useUnderline.cBool)
+ }
+
+ if let rowsStorage = storage.content["rows"] {
+ for (index, view) in rows().enumerated() {
+ if let storage = rowsStorage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+ if let suffixStorage = storage.content["suffix"] {
+ for (index, view) in suffix().enumerated() {
+ if let storage = suffixStorage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+ if let prefixStorage = storage.content["prefix"] {
+ for (index, view) in prefix().enumerated() {
+ if let storage = prefixStorage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// Whether expansion is enabled.
+ public func enableExpansion(_ enableExpansion: Binding?) -> Self {
+ var newSelf = self
+ newSelf.enableExpansion = enableExpansion
+
+ return newSelf
+ }
+
+ /// Whether the row is expanded.
+ public func expanded(_ expanded: Binding?) -> Self {
+ var newSelf = self
+ newSelf.expanded = expanded
+
+ return newSelf
+ }
+
+ /// The icon name for this row.
+ public func iconName(_ iconName: String?) -> Self {
+ var newSelf = self
+ newSelf.iconName = iconName
+
+ return newSelf
+ }
+
+ /// Whether the switch enabling the expansion is visible.
+ public func showEnableSwitch(_ showEnableSwitch: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.showEnableSwitch = showEnableSwitch
+
+ return newSelf
+ }
+
+ /// The subtitle for this row.
+ ///
+ /// The subtitle is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ public func subtitle(_ subtitle: String?) -> Self {
+ var newSelf = self
+ newSelf.subtitle = subtitle
+
+ return newSelf
+ }
+
+ /// The number of lines at the end of which the subtitle label will be
+ /// ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ public func subtitleLines(_ subtitleLines: Int?) -> Self {
+ var newSelf = self
+ newSelf.subtitleLines = subtitleLines
+
+ return newSelf
+ }
+
+ /// The number of lines at the end of which the title label will be ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ public func titleLines(_ titleLines: Int?) -> Self {
+ var newSelf = self
+ newSelf.titleLines = titleLines
+
+ return newSelf
+ }
+
+ /// The title of the preference represented by this row.
+ ///
+ /// The title is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ public func title(_ title: String?) -> Self {
+ var newSelf = self
+ newSelf.title = title
+
+ return newSelf
+ }
+
+ /// Whether the user can copy the title from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ public func titleSelectable(_ titleSelectable: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.titleSelectable = titleSelectable
+
+ return newSelf
+ }
+
+ /// Whether to use Pango markup for the title label.
+ ///
+ /// Subclasses may also use it for other labels, such as subtitle.
+ ///
+ /// See also [func@Pango.parse_markup].
+ public func useMarkup(_ useMarkup: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useMarkup = useMarkup
+
+ return newSelf
+ }
+
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+ /// Set the body for "rows".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func rows(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.rows = body
+ return newSelf
+ }
+ /// Set the body for "suffix".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func suffix(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.suffix = body
+ return newSelf
+ }
+ /// Set the body for "prefix".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func prefix(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.prefix = body
+ return newSelf
+ }
+}
diff --git a/Sources/Adwaita/View/Generated/HeaderBar.swift b/Sources/Adwaita/View/Generated/HeaderBar.swift
new file mode 100644
index 0000000..fc9e241
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/HeaderBar.swift
@@ -0,0 +1,331 @@
+//
+// HeaderBar.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A title bar widget.
+///
+///
+///
+/// `AdwHeaderBar` is similar to [class@Gtk.HeaderBar], but provides additional
+/// features compared to it. Refer to `GtkHeaderBar` for details. It is typically
+/// used as a top bar within [class@ToolbarView].
+///
+/// ## Navigation View Integration
+///
+/// When placed inside an [class@NavigationPage], `AdwHeaderBar` will display the
+/// page title instead of window title.
+///
+/// When used together with [class@NavigationView] or [class@NavigationSplitView],
+/// it will also display a back button that can be used to go back to the previous
+/// page. The button also has a context menu, allowing to pop multiple pages at
+/// once, potentially across multiple navigation views. In rare scenarios, set
+/// [property@HeaderBar:show-back-button] to `FALSE` to disable the back button
+/// if it's unwanted (e.g. in an extra header bar on the same page).
+///
+/// ## Split View Integration
+///
+/// When placed inside `AdwNavigationSplitView` or `AdwOverlaySplitView`,
+/// `AdwHeaderBar` will automatically hide the title buttons other than at the
+/// edges of the window.
+///
+/// ## Centering Policy
+///
+/// [property@HeaderBar:centering-policy] allows to enforce strict centering of
+/// the title widget. This can be useful for entries inside [class@Clamp].
+///
+/// ## Title Buttons
+///
+/// Unlike `GtkHeaderBar`, `AdwHeaderBar` allows to toggle title button
+/// visibility for each side individually, using the
+/// [property@HeaderBar:show-start-title-buttons] and
+/// [property@HeaderBar:show-end-title-buttons] properties.
+///
+/// ## CSS nodes
+///
+/// ```
+/// headerbar
+/// ╰── windowhandle
+/// ╰── box
+/// ├── widget
+/// │ ╰── box.start
+/// │ ├── windowcontrols.start
+/// │ ├── widget
+/// │ │ ╰── [button.back]
+/// │ ╰── [other children]
+/// ├── widget
+/// │ ╰── [Title Widget]
+/// ╰── widget
+/// ╰── box.end
+/// ├── [other children]
+/// ╰── windowcontrols.end
+/// ```
+///
+/// `AdwHeaderBar`'s CSS node is called `headerbar`. It contains a `windowhandle`
+/// subnode, which contains a `box` subnode, which contains three `widget`
+/// subnodes at the start, center and end of the header bar. The start and end
+/// subnotes contain a `box` subnode with the `.start` and `.end` style classes
+/// respectively, and the center node contains a node that represents the title.
+///
+/// Each of the boxes contains a `windowcontrols` subnode, see
+/// [class@Gtk.WindowControls] for details, as well as other children.
+///
+/// When [property@HeaderBar:show-back-button] is `TRUE`, the start box also
+/// contains a node with the name `widget` that contains a node with the name
+/// `button` and `.back` style class.
+///
+/// ## Accessibility
+///
+/// `AdwHeaderBar` uses the `GTK_ACCESSIBLE_ROLE_GROUP` role.
+public struct HeaderBar: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The decoration layout for buttons.
+ ///
+ /// If this property is not set, the
+ /// [property@Gtk.Settings:gtk-decoration-layout] setting is used.
+ ///
+ /// The format of the string is button names, separated by commas. A colon
+ /// separates the buttons that should appear at the start from those at the
+ /// end. Recognized button names are minimize, maximize, close and icon (the
+ /// window icon).
+ ///
+ /// For example, “icon:minimize,maximize,close” specifies an icon at the start,
+ /// and minimize, maximize and close buttons at the end.
+ var decorationLayout: String?
+ /// Whether the header bar can show the back button.
+ ///
+ /// The back button will never be shown unless the header bar is placed inside an
+ /// [class@NavigationView]. Usually, there is no reason to set this to `FALSE`.
+ var showBackButton: Bool?
+ /// Whether to show title buttons at the end of the header bar.
+ ///
+ /// See [property@HeaderBar:show-start-title-buttons] for the other side.
+ ///
+ /// Which buttons are actually shown and where is determined by the
+ /// [property@HeaderBar:decoration-layout] property, and by the state of the
+ /// window (e.g. a close button will not be shown if the window can't be
+ /// closed).
+ var showEndTitleButtons: Bool?
+ /// Whether to show title buttons at the start of the header bar.
+ ///
+ /// See [property@HeaderBar:show-end-title-buttons] for the other side.
+ ///
+ /// Which buttons are actually shown and where is determined by the
+ /// [property@HeaderBar:decoration-layout] property, and by the state of the
+ /// window (e.g. a close button will not be shown if the window can't be
+ /// closed).
+ var showStartTitleButtons: Bool?
+ /// Whether the title widget should be shown.
+ var showTitle: Bool?
+ /// The title widget to display.
+ ///
+ /// When set to `NULL`, the header bar will display the title of the window it
+ /// is contained in.
+ ///
+ /// To use a different title, use [class@WindowTitle]:
+ ///
+ /// ```xml
+ /// Title
+ /// ```
+ var titleWidget: (() -> Body)?
+ /// The body for the widget "start".
+ var start: () -> Body = { [] }
+ /// The body for the widget "end".
+ var end: () -> Body = { [] }
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `HeaderBar`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_header_bar_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let titleWidgetStorage = titleWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["titleWidget"] = [titleWidgetStorage]
+ adw_header_bar_set_title_widget(storage.pointer, titleWidgetStorage.pointer?.cast())
+ }
+
+ var startStorage: [ViewStorage] = []
+ for view in start() {
+ startStorage.append(view.storage(modifiers: modifiers))
+ adw_header_bar_pack_start(storage.pointer, startStorage.last?.pointer?.cast())
+ }
+ storage.content["start"] = startStorage
+ var endStorage: [ViewStorage] = []
+ for view in end() {
+ endStorage.append(view.storage(modifiers: modifiers))
+ adw_header_bar_pack_end(storage.pointer, endStorage.last?.pointer?.cast())
+ }
+ storage.content["end"] = endStorage
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let decorationLayout {
+ adw_header_bar_set_decoration_layout(widget, decorationLayout)
+ }
+ if let showBackButton {
+ adw_header_bar_set_show_back_button(widget, showBackButton.cBool)
+ }
+ if let showEndTitleButtons {
+ adw_header_bar_set_show_end_title_buttons(widget, showEndTitleButtons.cBool)
+ }
+ if let showStartTitleButtons {
+ adw_header_bar_set_show_start_title_buttons(widget, showStartTitleButtons.cBool)
+ }
+ if let showTitle {
+ adw_header_bar_set_show_title(widget, showTitle.cBool)
+ }
+ if let widget = storage.content["titleWidget"]?.first {
+ titleWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+
+ if let startStorage = storage.content["start"] {
+ for (index, view) in start().enumerated() {
+ if let storage = startStorage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+ if let endStorage = storage.content["end"] {
+ for (index, view) in end().enumerated() {
+ if let storage = endStorage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The decoration layout for buttons.
+ ///
+ /// If this property is not set, the
+ /// [property@Gtk.Settings:gtk-decoration-layout] setting is used.
+ ///
+ /// The format of the string is button names, separated by commas. A colon
+ /// separates the buttons that should appear at the start from those at the
+ /// end. Recognized button names are minimize, maximize, close and icon (the
+ /// window icon).
+ ///
+ /// For example, “icon:minimize,maximize,close” specifies an icon at the start,
+ /// and minimize, maximize and close buttons at the end.
+ public func decorationLayout(_ decorationLayout: String?) -> Self {
+ var newSelf = self
+ newSelf.decorationLayout = decorationLayout
+
+ return newSelf
+ }
+
+ /// Whether the header bar can show the back button.
+ ///
+ /// The back button will never be shown unless the header bar is placed inside an
+ /// [class@NavigationView]. Usually, there is no reason to set this to `FALSE`.
+ public func showBackButton(_ showBackButton: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.showBackButton = showBackButton
+
+ return newSelf
+ }
+
+ /// Whether to show title buttons at the end of the header bar.
+ ///
+ /// See [property@HeaderBar:show-start-title-buttons] for the other side.
+ ///
+ /// Which buttons are actually shown and where is determined by the
+ /// [property@HeaderBar:decoration-layout] property, and by the state of the
+ /// window (e.g. a close button will not be shown if the window can't be
+ /// closed).
+ public func showEndTitleButtons(_ showEndTitleButtons: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.showEndTitleButtons = showEndTitleButtons
+
+ return newSelf
+ }
+
+ /// Whether to show title buttons at the start of the header bar.
+ ///
+ /// See [property@HeaderBar:show-end-title-buttons] for the other side.
+ ///
+ /// Which buttons are actually shown and where is determined by the
+ /// [property@HeaderBar:decoration-layout] property, and by the state of the
+ /// window (e.g. a close button will not be shown if the window can't be
+ /// closed).
+ public func showStartTitleButtons(_ showStartTitleButtons: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.showStartTitleButtons = showStartTitleButtons
+
+ return newSelf
+ }
+
+ /// Whether the title widget should be shown.
+ public func showTitle(_ showTitle: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.showTitle = showTitle
+
+ return newSelf
+ }
+
+ /// The title widget to display.
+ ///
+ /// When set to `NULL`, the header bar will display the title of the window it
+ /// is contained in.
+ ///
+ /// To use a different title, use [class@WindowTitle]:
+ ///
+ /// ```xml
+ /// Title
+ /// ```
+ public func titleWidget(@ViewBuilder _ titleWidget: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.titleWidget = titleWidget
+
+ return newSelf
+ }
+
+ /// Set the body for "start".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func start(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.start = body
+ return newSelf
+ }
+ /// Set the body for "end".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func end(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.end = body
+ return newSelf
+ }
+}
diff --git a/Sources/Adwaita/View/Generated/Label.swift b/Sources/Adwaita/View/Generated/Label.swift
new file mode 100644
index 0000000..3d879a6
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/Label.swift
@@ -0,0 +1,502 @@
+//
+// Label.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// The `GtkLabel` widget displays a small amount of text.
+///
+/// As the name implies, most labels are used to label another widget
+/// such as a [class@Button].
+///
+/// ![An example GtkLabel](label.png)
+///
+/// # CSS nodes
+///
+/// ```
+/// label
+/// ├── [selection]
+/// ├── [link]
+/// ┊
+/// ╰── [link]
+/// ```
+///
+/// `GtkLabel` has a single CSS node with the name label. A wide variety
+/// of style classes may be applied to labels, such as .title, .subtitle,
+/// .dim-label, etc. In the `GtkShortcutsWindow`, labels are used with the
+/// .keycap style class.
+///
+/// If the label has a selection, it gets a subnode with name selection.
+///
+/// If the label has links, there is one subnode per link. These subnodes
+/// carry the link or visited state depending on whether they have been
+/// visited. In this case, label node also gets a .link style class.
+///
+/// # GtkLabel as GtkBuildable
+///
+/// The GtkLabel implementation of the GtkBuildable interface supports a
+/// custom `` element, which supports any number of ``
+/// elements. The element has attributes named “name“, “value“,
+/// “start“ and “end“ and allows you to specify [struct@Pango.Attribute]
+/// values for this label.
+///
+/// An example of a UI definition fragment specifying Pango attributes:
+/// ```xml
+///
+/// ```
+///
+/// The start and end attributes specify the range of characters to which the
+/// Pango attribute applies. If start and end are not specified, the attribute is
+/// applied to the whole text. Note that specifying ranges does not make much
+/// sense with translatable attributes. Use markup embedded in the translatable
+/// content instead.
+///
+/// # Accessibility
+///
+/// `GtkLabel` uses the %GTK_ACCESSIBLE_ROLE_LABEL role.
+///
+/// # Mnemonics
+///
+/// Labels may contain “mnemonics”. Mnemonics are underlined characters in the
+/// label, used for keyboard navigation. Mnemonics are created by providing a
+/// string with an underscore before the mnemonic character, such as `"_File"`,
+/// to the functions [ctor@Gtk.Label.new_with_mnemonic] or
+/// [method@Gtk.Label.set_text_with_mnemonic].
+///
+/// Mnemonics automatically activate any activatable widget the label is
+/// inside, such as a [class@Gtk.Button]; if the label is not inside the
+/// mnemonic’s target widget, you have to tell the label about the target
+/// using [class@Gtk.Label.set_mnemonic_widget]. Here’s a simple example where
+/// the label is inside a button:
+///
+/// ```c
+/// // Pressing Alt+H will activate this button
+/// GtkWidget *button = gtk_button_new ();
+/// GtkWidget *label = gtk_label_new_with_mnemonic ("_Hello");
+/// gtk_button_set_child (GTK_BUTTON (button), label);
+/// ```
+///
+/// There’s a convenience function to create buttons with a mnemonic label
+/// already inside:
+///
+/// ```c
+/// // Pressing Alt+H will activate this button
+/// GtkWidget *button = gtk_button_new_with_mnemonic ("_Hello");
+/// ```
+///
+/// To create a mnemonic for a widget alongside the label, such as a
+/// [class@Gtk.Entry], you have to point the label at the entry with
+/// [method@Gtk.Label.set_mnemonic_widget]:
+///
+/// ```c
+/// // Pressing Alt+H will focus the entry
+/// GtkWidget *entry = gtk_entry_new ();
+/// GtkWidget *label = gtk_label_new_with_mnemonic ("_Hello");
+/// gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+/// ```
+///
+/// # Markup (styled text)
+///
+/// To make it easy to format text in a label (changing colors,
+/// fonts, etc.), label text can be provided in a simple
+/// markup format:
+///
+/// Here’s how to create a label with a small font:
+/// ```c
+/// GtkWidget *label = gtk_label_new (NULL);
+/// gtk_label_set_markup (GTK_LABEL (label), "Small text");
+/// ```
+///
+/// (See the Pango manual for complete documentation] of available
+/// tags, [func@Pango.parse_markup])
+///
+/// The markup passed to [method@Gtk.Label.set_markup] must be valid; for example,
+/// literal `<`, `>` and `&` characters must be escaped as `<`, `>`, and `&`.
+/// If you pass text obtained from the user, file, or a network to
+/// [method@Gtk.Label.set_markup], you’ll want to escape it with
+/// [func@GLib.markup_escape_text] or [func@GLib.markup_printf_escaped].
+///
+/// Markup strings are just a convenient way to set the [struct@Pango.AttrList]
+/// on a label; [method@Gtk.Label.set_attributes] may be a simpler way to set
+/// attributes in some cases. Be careful though; [struct@Pango.AttrList] tends
+/// to cause internationalization problems, unless you’re applying attributes
+/// to the entire string (i.e. unless you set the range of each attribute
+/// to [0, %G_MAXINT)). The reason is that specifying the start_index and
+/// end_index for a [struct@Pango.Attribute] requires knowledge of the exact
+/// string being displayed, so translations will cause problems.
+///
+/// # Selectable labels
+///
+/// Labels can be made selectable with [method@Gtk.Label.set_selectable].
+/// Selectable labels allow the user to copy the label contents to
+/// the clipboard. Only labels that contain useful-to-copy information
+/// — such as error messages — should be made selectable.
+///
+/// # Text layout
+///
+/// A label can contain any number of paragraphs, but will have
+/// performance problems if it contains more than a small number.
+/// Paragraphs are separated by newlines or other paragraph separators
+/// understood by Pango.
+///
+/// Labels can automatically wrap text if you call [method@Gtk.Label.set_wrap].
+///
+/// [method@Gtk.Label.set_justify] sets how the lines in a label align
+/// with one another. If you want to set how the label as a whole aligns
+/// in its available space, see the [property@Gtk.Widget:halign] and
+/// [property@Gtk.Widget:valign] properties.
+///
+/// The [property@Gtk.Label:width-chars] and [property@Gtk.Label:max-width-chars]
+/// properties can be used to control the size allocation of ellipsized or
+/// wrapped labels. For ellipsizing labels, if either is specified (and less
+/// than the actual text size), it is used as the minimum width, and the actual
+/// text size is used as the natural width of the label. For wrapping labels,
+/// width-chars is used as the minimum width, if specified, and max-width-chars
+/// is used as the natural width. Even if max-width-chars specified, wrapping
+/// labels will be rewrapped to use all of the available width.
+///
+/// # Links
+///
+/// GTK supports markup for clickable hyperlinks in addition to regular Pango
+/// markup. The markup for links is borrowed from HTML, using the `` with
+/// “href“, “title“ and “class“ attributes. GTK renders links similar to the
+/// way they appear in web browsers, with colored, underlined text. The “title“
+/// attribute is displayed as a tooltip on the link. The “class“ attribute is
+/// used as style class on the CSS node for the link.
+///
+/// An example looks like this:
+///
+/// ```c
+/// const char *text =
+/// "Go to the "
+/// ""
+/// "GTK website for more...";
+/// GtkWidget *label = gtk_label_new (NULL);
+/// gtk_label_set_markup (GTK_LABEL (label), text);
+/// ```
+///
+/// It is possible to implement custom handling for links and their tooltips
+/// with the [signal@Gtk.Label::activate-link] signal and the
+/// [method@Gtk.Label.get_current_uri] function.
+public struct Label: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The contents of the label.
+ ///
+ /// If the string contains Pango markup (see [func@Pango.parse_markup]),
+ /// you will have to set the [property@Gtk.Label:use-markup] property to
+ /// %TRUE in order for the label to display the markup attributes. See also
+ /// [method@Gtk.Label.set_markup] for a convenience function that sets both
+ /// this property and the [property@Gtk.Label:use-markup] property at the
+ /// same time.
+ ///
+ /// If the string contains underlines acting as mnemonics, you will have to
+ /// set the [property@Gtk.Label:use-underline] property to %TRUE in order
+ /// for the label to display them.
+ var label: String
+ /// The number of lines to which an ellipsized, wrapping label
+ /// should be limited.
+ ///
+ /// This property has no effect if the label is not wrapping or ellipsized.
+ /// Set this property to -1 if you don't want to limit the number of lines.
+ var lines: Int?
+ /// The desired maximum width of the label, in characters.
+ ///
+ /// If this property is set to -1, the width will be calculated automatically.
+ ///
+ /// See the section on [text layout](class.Label.html#text-layout) for details of how
+ /// [property@Gtk.Label:width-chars] and [property@Gtk.Label:max-width-chars]
+ /// determine the width of ellipsized and wrapped labels.
+ var maxWidthChars: Int?
+ /// The mnemonic accelerator key for the label.
+ var mnemonicKeyval: UInt?
+ /// The widget to be activated when the labels mnemonic key is pressed.
+ var mnemonicWidget: (() -> Body)?
+ /// Whether the label text can be selected with the mouse.
+ var selectable: Bool?
+ /// Whether the label is in single line mode.
+ ///
+ /// In single line mode, the height of the label does not depend on the
+ /// actual text, it is always set to ascent + descent of the font. This
+ /// can be an advantage in situations where resizing the label because
+ /// of text changes would be distracting, e.g. in a statusbar.
+ var singleLineMode: Bool?
+ /// %TRUE if the text of the label includes Pango markup.
+ ///
+ /// See [func@Pango.parse_markup].
+ var useMarkup: Bool?
+ /// %TRUE if the text of the label indicates a mnemonic with an _
+ /// before the mnemonic character.
+ var useUnderline: Bool?
+ /// The desired width of the label, in characters.
+ ///
+ /// If this property is set to -1, the width will be calculated automatically.
+ ///
+ /// See the section on [text layout](class.Label.html#text-layout) for details of how
+ /// [property@Gtk.Label:width-chars] and [property@Gtk.Label:max-width-chars]
+ /// determine the width of ellipsized and wrapped labels.
+ var widthChars: Int?
+ /// %TRUE if the label text will wrap if it gets too wide.
+ var wrap: Bool?
+ /// The horizontal alignment of the label text inside its size allocation.
+ ///
+ /// Compare this to [property@Gtk.Widget:halign], which determines how the
+ /// labels size allocation is positioned in the space available for the label.
+ var xalign: Float?
+ /// The vertical alignment of the label text inside its size allocation.
+ ///
+ /// Compare this to [property@Gtk.Widget:valign], which determines how the
+ /// labels size allocation is positioned in the space available for the label.
+ var yalign: Float?
+ /// Gets emitted to copy the selection to the clipboard.
+ ///
+ /// The ::copy-clipboard signal is a [keybinding signal](class.SignalAction.html).
+ ///
+ /// The default binding for this signal is Ctrl+c.
+ var copyClipboard: (() -> Void)?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `Label`.
+ public init(label: String) {
+ self.label = label
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(gtk_label_new(label)?.opaque())
+ update(storage, modifiers: modifiers)
+ if let mnemonicWidgetStorage = mnemonicWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["mnemonicWidget"] = [mnemonicWidgetStorage]
+ gtk_label_set_mnemonic_widget(storage.pointer, mnemonicWidgetStorage.pointer?.cast())
+ }
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let copyClipboard {
+ storage.connectSignal(name: "copy-clipboard") {
+ copyClipboard()
+ }
+ }
+ storage.modify { widget in
+ gtk_label_set_label(widget, label)
+ if let lines {
+ gtk_label_set_lines(widget, lines.cInt)
+ }
+ if let maxWidthChars {
+ gtk_label_set_max_width_chars(widget, maxWidthChars.cInt)
+ }
+ if let widget = storage.content["mnemonicWidget"]?.first {
+ mnemonicWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let selectable {
+ gtk_label_set_selectable(widget, selectable.cBool)
+ }
+ if let singleLineMode {
+ gtk_label_set_single_line_mode(widget, singleLineMode.cBool)
+ }
+ if let useMarkup {
+ gtk_label_set_use_markup(widget, useMarkup.cBool)
+ }
+ if let useUnderline {
+ gtk_label_set_use_underline(widget, useUnderline.cBool)
+ }
+ if let widthChars {
+ gtk_label_set_width_chars(widget, widthChars.cInt)
+ }
+ if let wrap {
+ gtk_label_set_wrap(widget, wrap.cBool)
+ }
+ if let xalign {
+ gtk_label_set_xalign(widget, xalign)
+ }
+ if let yalign {
+ gtk_label_set_yalign(widget, yalign)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The contents of the label.
+ ///
+ /// If the string contains Pango markup (see [func@Pango.parse_markup]),
+ /// you will have to set the [property@Gtk.Label:use-markup] property to
+ /// %TRUE in order for the label to display the markup attributes. See also
+ /// [method@Gtk.Label.set_markup] for a convenience function that sets both
+ /// this property and the [property@Gtk.Label:use-markup] property at the
+ /// same time.
+ ///
+ /// If the string contains underlines acting as mnemonics, you will have to
+ /// set the [property@Gtk.Label:use-underline] property to %TRUE in order
+ /// for the label to display them.
+ public func label(_ label: String) -> Self {
+ var newSelf = self
+ newSelf.label = label
+
+ return newSelf
+ }
+
+ /// The number of lines to which an ellipsized, wrapping label
+ /// should be limited.
+ ///
+ /// This property has no effect if the label is not wrapping or ellipsized.
+ /// Set this property to -1 if you don't want to limit the number of lines.
+ public func lines(_ lines: Int?) -> Self {
+ var newSelf = self
+ newSelf.lines = lines
+
+ return newSelf
+ }
+
+ /// The desired maximum width of the label, in characters.
+ ///
+ /// If this property is set to -1, the width will be calculated automatically.
+ ///
+ /// See the section on [text layout](class.Label.html#text-layout) for details of how
+ /// [property@Gtk.Label:width-chars] and [property@Gtk.Label:max-width-chars]
+ /// determine the width of ellipsized and wrapped labels.
+ public func maxWidthChars(_ maxWidthChars: Int?) -> Self {
+ var newSelf = self
+ newSelf.maxWidthChars = maxWidthChars
+
+ return newSelf
+ }
+
+ /// The mnemonic accelerator key for the label.
+ public func mnemonicKeyval(_ mnemonicKeyval: UInt?) -> Self {
+ var newSelf = self
+ newSelf.mnemonicKeyval = mnemonicKeyval
+
+ return newSelf
+ }
+
+ /// The widget to be activated when the labels mnemonic key is pressed.
+ public func mnemonicWidget(@ViewBuilder _ mnemonicWidget: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.mnemonicWidget = mnemonicWidget
+
+ return newSelf
+ }
+
+ /// Whether the label text can be selected with the mouse.
+ public func selectable(_ selectable: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.selectable = selectable
+
+ return newSelf
+ }
+
+ /// Whether the label is in single line mode.
+ ///
+ /// In single line mode, the height of the label does not depend on the
+ /// actual text, it is always set to ascent + descent of the font. This
+ /// can be an advantage in situations where resizing the label because
+ /// of text changes would be distracting, e.g. in a statusbar.
+ public func singleLineMode(_ singleLineMode: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.singleLineMode = singleLineMode
+
+ return newSelf
+ }
+
+ /// %TRUE if the text of the label includes Pango markup.
+ ///
+ /// See [func@Pango.parse_markup].
+ public func useMarkup(_ useMarkup: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useMarkup = useMarkup
+
+ return newSelf
+ }
+
+ /// %TRUE if the text of the label indicates a mnemonic with an _
+ /// before the mnemonic character.
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+ /// The desired width of the label, in characters.
+ ///
+ /// If this property is set to -1, the width will be calculated automatically.
+ ///
+ /// See the section on [text layout](class.Label.html#text-layout) for details of how
+ /// [property@Gtk.Label:width-chars] and [property@Gtk.Label:max-width-chars]
+ /// determine the width of ellipsized and wrapped labels.
+ public func widthChars(_ widthChars: Int?) -> Self {
+ var newSelf = self
+ newSelf.widthChars = widthChars
+
+ return newSelf
+ }
+
+ /// %TRUE if the label text will wrap if it gets too wide.
+ public func wrap(_ wrap: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.wrap = wrap
+
+ return newSelf
+ }
+
+ /// The horizontal alignment of the label text inside its size allocation.
+ ///
+ /// Compare this to [property@Gtk.Widget:halign], which determines how the
+ /// labels size allocation is positioned in the space available for the label.
+ public func xalign(_ xalign: Float?) -> Self {
+ var newSelf = self
+ newSelf.xalign = xalign
+
+ return newSelf
+ }
+
+ /// The vertical alignment of the label text inside its size allocation.
+ ///
+ /// Compare this to [property@Gtk.Widget:valign], which determines how the
+ /// labels size allocation is positioned in the space available for the label.
+ public func yalign(_ yalign: Float?) -> Self {
+ var newSelf = self
+ newSelf.yalign = yalign
+
+ return newSelf
+ }
+
+ /// Gets emitted to copy the selection to the clipboard.
+ ///
+ /// The ::copy-clipboard signal is a [keybinding signal](class.SignalAction.html).
+ ///
+ /// The default binding for this signal is Ctrl+c.
+ public func copyClipboard(_ copyClipboard: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.copyClipboard = copyClipboard
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/LevelBar.swift b/Sources/Adwaita/View/Generated/LevelBar.swift
new file mode 100644
index 0000000..c01513d
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/LevelBar.swift
@@ -0,0 +1,238 @@
+//
+// LevelBar.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// `GtkLevelBar` is a widget that can be used as a level indicator.
+///
+/// Typical use cases are displaying the strength of a password, or
+/// showing the charge level of a battery.
+///
+/// ![An example GtkLevelBar](levelbar.png)
+///
+/// Use [method@Gtk.LevelBar.set_value] to set the current value, and
+/// [method@Gtk.LevelBar.add_offset_value] to set the value offsets at which
+/// the bar will be considered in a different state. GTK will add a few
+/// offsets by default on the level bar: %GTK_LEVEL_BAR_OFFSET_LOW,
+/// %GTK_LEVEL_BAR_OFFSET_HIGH and %GTK_LEVEL_BAR_OFFSET_FULL, with
+/// values 0.25, 0.75 and 1.0 respectively.
+///
+/// Note that it is your responsibility to update preexisting offsets
+/// when changing the minimum or maximum value. GTK will simply clamp
+/// them to the new range.
+///
+/// ## Adding a custom offset on the bar
+///
+/// ```c
+/// static GtkWidget *
+/// create_level_bar (void)
+/// {
+/// GtkWidget *widget;
+/// GtkLevelBar *bar;
+///
+/// widget = gtk_level_bar_new ();
+/// bar = GTK_LEVEL_BAR (widget);
+///
+/// // This changes the value of the default low offset
+///
+/// gtk_level_bar_add_offset_value (bar,
+/// GTK_LEVEL_BAR_OFFSET_LOW,
+/// 0.10);
+///
+/// // This adds a new offset to the bar; the application will
+/// // be able to change its color CSS like this:
+/// //
+/// // levelbar block.my-offset {
+/// // background-color: magenta;
+/// // border-style: solid;
+/// // border-color: black;
+/// // border-width: 1px;
+/// // }
+///
+/// gtk_level_bar_add_offset_value (bar, "my-offset", 0.60);
+///
+/// return widget;
+/// }
+/// ```
+///
+/// The default interval of values is between zero and one, but it’s possible
+/// to modify the interval using [method@Gtk.LevelBar.set_min_value] and
+/// [method@Gtk.LevelBar.set_max_value]. The value will be always drawn in
+/// proportion to the admissible interval, i.e. a value of 15 with a specified
+/// interval between 10 and 20 is equivalent to a value of 0.5 with an interval
+/// between 0 and 1. When %GTK_LEVEL_BAR_MODE_DISCRETE is used, the bar level
+/// is rendered as a finite number of separated blocks instead of a single one.
+/// The number of blocks that will be rendered is equal to the number of units
+/// specified by the admissible interval.
+///
+/// For instance, to build a bar rendered with five blocks, it’s sufficient to
+/// set the minimum value to 0 and the maximum value to 5 after changing the
+/// indicator mode to discrete.
+///
+/// # GtkLevelBar as GtkBuildable
+///
+/// The `GtkLevelBar` implementation of the `GtkBuildable` interface supports a
+/// custom `` element, which can contain any number of `` elements,
+/// each of which must have "name" and "value" attributes.
+///
+/// # CSS nodes
+///
+/// ```
+/// levelbar[.discrete]
+/// ╰── trough
+/// ├── block.filled.level-name
+/// ┊
+/// ├── block.empty
+/// ┊
+/// ```
+///
+/// `GtkLevelBar` has a main CSS node with name levelbar and one of the style
+/// classes .discrete or .continuous and a subnode with name trough. Below the
+/// trough node are a number of nodes with name block and style class .filled
+/// or .empty. In continuous mode, there is exactly one node of each, in discrete
+/// mode, the number of filled and unfilled nodes corresponds to blocks that are
+/// drawn. The block.filled nodes also get a style class .level-name corresponding
+/// to the level for the current value.
+///
+/// In horizontal orientation, the nodes are always arranged from left to right,
+/// regardless of text direction.
+///
+/// # Accessibility
+///
+/// `GtkLevelBar` uses the %GTK_ACCESSIBLE_ROLE_METER role.
+public struct LevelBar: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// Whether the `GtkLeveBar` is inverted.
+ ///
+ /// Level bars normally grow from top to bottom or left to right.
+ /// Inverted level bars grow in the opposite direction.
+ var inverted: Bool?
+ /// Determines the maximum value of the interval that can be displayed by the bar.
+ var maxValue: Double?
+ /// Determines the minimum value of the interval that can be displayed by the bar.
+ var minValue: Double?
+ /// Determines the currently filled value of the level bar.
+ var value: Double?
+ /// Emitted when an offset specified on the bar changes value.
+ ///
+ /// This typically is the result of a [method@Gtk.LevelBar.add_offset_value]
+ /// call.
+ ///
+ /// The signal supports detailed connections; you can connect to the
+ /// detailed signal "changed::x" in order to only receive callbacks when
+ /// the value of offset "x" changes.
+ var offsetChanged: (() -> Void)?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `LevelBar`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(gtk_level_bar_new()?.opaque())
+ update(storage, modifiers: modifiers)
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let offsetChanged {
+ storage.connectSignal(name: "offset-changed") {
+ offsetChanged()
+ }
+ }
+ storage.modify { widget in
+ if let inverted {
+ gtk_level_bar_set_inverted(widget, inverted.cBool)
+ }
+ if let maxValue {
+ gtk_level_bar_set_max_value(widget, maxValue)
+ }
+ if let minValue {
+ gtk_level_bar_set_min_value(widget, minValue)
+ }
+ if let value {
+ gtk_level_bar_set_value(widget, value)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// Whether the `GtkLeveBar` is inverted.
+ ///
+ /// Level bars normally grow from top to bottom or left to right.
+ /// Inverted level bars grow in the opposite direction.
+ public func inverted(_ inverted: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.inverted = inverted
+
+ return newSelf
+ }
+
+ /// Determines the maximum value of the interval that can be displayed by the bar.
+ public func maxValue(_ maxValue: Double?) -> Self {
+ var newSelf = self
+ newSelf.maxValue = maxValue
+
+ return newSelf
+ }
+
+ /// Determines the minimum value of the interval that can be displayed by the bar.
+ public func minValue(_ minValue: Double?) -> Self {
+ var newSelf = self
+ newSelf.minValue = minValue
+
+ return newSelf
+ }
+
+ /// Determines the currently filled value of the level bar.
+ public func value(_ value: Double?) -> Self {
+ var newSelf = self
+ newSelf.value = value
+
+ return newSelf
+ }
+
+ /// Emitted when an offset specified on the bar changes value.
+ ///
+ /// This typically is the result of a [method@Gtk.LevelBar.add_offset_value]
+ /// call.
+ ///
+ /// The signal supports detailed connections; you can connect to the
+ /// detailed signal "changed::x" in order to only receive callbacks when
+ /// the value of offset "x" changes.
+ public func offsetChanged(_ offsetChanged: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.offsetChanged = offsetChanged
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/LinkButton.swift b/Sources/Adwaita/View/Generated/LinkButton.swift
new file mode 100644
index 0000000..3270ba2
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/LinkButton.swift
@@ -0,0 +1,246 @@
+//
+// LinkButton.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A `GtkLinkButton` is a button with a hyperlink.
+///
+/// ![An example GtkLinkButton](link-button.png)
+///
+/// It is useful to show quick links to resources.
+///
+/// A link button is created by calling either [ctor@Gtk.LinkButton.new] or
+/// [ctor@Gtk.LinkButton.new_with_label]. If using the former, the URI you
+/// pass to the constructor is used as a label for the widget.
+///
+/// The URI bound to a `GtkLinkButton` can be set specifically using
+/// [method@Gtk.LinkButton.set_uri].
+///
+/// By default, `GtkLinkButton` calls [method@Gtk.FileLauncher.launch] when the button
+/// is clicked. This behaviour can be overridden by connecting to the
+/// [signal@Gtk.LinkButton::activate-link] signal and returning %TRUE from
+/// the signal handler.
+///
+/// # CSS nodes
+///
+/// `GtkLinkButton` has a single CSS node with name button. To differentiate
+/// it from a plain `GtkButton`, it gets the .link style class.
+///
+/// # Accessibility
+///
+/// `GtkLinkButton` uses the %GTK_ACCESSIBLE_ROLE_LINK role.
+public struct LinkButton: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The URI bound to this button.
+ var uri: String
+ /// The 'visited' state of this button.
+ ///
+ /// A visited link is drawn in a different color.
+ var visited: Bool?
+ /// Whether the size of the button can be made smaller than the natural
+ /// size of its contents.
+ ///
+ /// For text buttons, setting this property will allow ellipsizing the label.
+ ///
+ /// If the contents of a button are an icon or a custom widget, setting this
+ /// property has no effect.
+ var canShrink: Bool?
+ /// The child widget.
+ var child: (() -> Body)?
+ /// Whether the button has a frame.
+ var hasFrame: Bool?
+ /// The name of the icon used to automatically populate the button.
+ var iconName: String?
+ /// Text of the label inside the button, if the button contains a label widget.
+ var label: String?
+ /// If set, an underline in the text indicates that the following character is
+ /// to be used as mnemonic.
+ var useUnderline: Bool?
+ /// Emitted to animate press then release.
+ ///
+ /// This is an action signal. Applications should never connect
+ /// to this signal, but use the [signal@Gtk.Button::clicked] signal.
+ ///
+ /// The default bindings for this signal are all forms of the
+ /// ␣ and Enter keys.
+ var activate: (() -> Void)?
+ /// Emitted when the button has been activated (pressed and released).
+ var clicked: (() -> Void)?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `LinkButton`.
+ public init(uri: String) {
+ self.uri = uri
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(gtk_link_button_new(uri)?.opaque())
+ update(storage, modifiers: modifiers)
+ if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["child"] = [childStorage]
+ gtk_button_set_child(storage.pointer?.cast(), childStorage.pointer?.cast())
+ }
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let activate {
+ storage.connectSignal(name: "activate") {
+ activate()
+ }
+ }
+ if let clicked {
+ storage.connectSignal(name: "clicked") {
+ clicked()
+ }
+ }
+ storage.modify { widget in
+ gtk_link_button_set_uri(widget, uri)
+ if let visited {
+ gtk_link_button_set_visited(widget, visited.cBool)
+ }
+ if let canShrink {
+ gtk_button_set_can_shrink(widget?.cast(), canShrink.cBool)
+ }
+ if let widget = storage.content["child"]?.first {
+ child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let hasFrame {
+ gtk_button_set_has_frame(widget?.cast(), hasFrame.cBool)
+ }
+ if let iconName {
+ gtk_button_set_icon_name(widget?.cast(), iconName)
+ }
+ if let label, storage.content["child"] == nil {
+ gtk_button_set_label(widget?.cast(), label)
+ }
+ if let useUnderline {
+ gtk_button_set_use_underline(widget?.cast(), useUnderline.cBool)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The URI bound to this button.
+ public func uri(_ uri: String) -> Self {
+ var newSelf = self
+ newSelf.uri = uri
+
+ return newSelf
+ }
+
+ /// The 'visited' state of this button.
+ ///
+ /// A visited link is drawn in a different color.
+ public func visited(_ visited: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.visited = visited
+
+ return newSelf
+ }
+
+ /// Whether the size of the button can be made smaller than the natural
+ /// size of its contents.
+ ///
+ /// For text buttons, setting this property will allow ellipsizing the label.
+ ///
+ /// If the contents of a button are an icon or a custom widget, setting this
+ /// property has no effect.
+ public func canShrink(_ canShrink: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.canShrink = canShrink
+
+ return newSelf
+ }
+
+ /// The child widget.
+ public func child(@ViewBuilder _ child: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.child = child
+
+ return newSelf
+ }
+
+ /// Whether the button has a frame.
+ public func hasFrame(_ hasFrame: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.hasFrame = hasFrame
+
+ return newSelf
+ }
+
+ /// The name of the icon used to automatically populate the button.
+ public func iconName(_ iconName: String?) -> Self {
+ var newSelf = self
+ newSelf.iconName = iconName
+
+ return newSelf
+ }
+
+ /// Text of the label inside the button, if the button contains a label widget.
+ public func label(_ label: String?) -> Self {
+ var newSelf = self
+ newSelf.label = label
+
+ return newSelf
+ }
+
+ /// If set, an underline in the text indicates that the following character is
+ /// to be used as mnemonic.
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+ /// Emitted to animate press then release.
+ ///
+ /// This is an action signal. Applications should never connect
+ /// to this signal, but use the [signal@Gtk.Button::clicked] signal.
+ ///
+ /// The default bindings for this signal are all forms of the
+ /// ␣ and Enter keys.
+ public func activate(_ activate: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.activate = activate
+ return newSelf
+ }
+
+ /// Emitted when the button has been activated (pressed and released).
+ public func clicked(_ clicked: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.clicked = clicked
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/ListBox.swift b/Sources/Adwaita/View/Generated/ListBox.swift
new file mode 100644
index 0000000..ecb6a83
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/ListBox.swift
@@ -0,0 +1,319 @@
+//
+// ListBox.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// `GtkListBox` is a vertical list.
+///
+/// A `GtkListBox` only contains `GtkListBoxRow` children. These rows can
+/// by dynamically sorted and filtered, and headers can be added dynamically
+/// depending on the row content. It also allows keyboard and mouse navigation
+/// and selection like a typical list.
+///
+/// Using `GtkListBox` is often an alternative to `GtkTreeView`, especially
+/// when the list contents has a more complicated layout than what is allowed
+/// by a `GtkCellRenderer`, or when the contents is interactive (i.e. has a
+/// button in it).
+///
+/// Although a `GtkListBox` must have only `GtkListBoxRow` children, you can
+/// add any kind of widget to it via [method@Gtk.ListBox.prepend],
+/// [method@Gtk.ListBox.append] and [method@Gtk.ListBox.insert] and a
+/// `GtkListBoxRow` widget will automatically be inserted between the list
+/// and the widget.
+///
+/// `GtkListBoxRows` can be marked as activatable or selectable. If a row is
+/// activatable, [signal@Gtk.ListBox::row-activated] will be emitted for it when
+/// the user tries to activate it. If it is selectable, the row will be marked
+/// as selected when the user tries to select it.
+///
+/// # GtkListBox as GtkBuildable
+///
+/// The `GtkListBox` implementation of the `GtkBuildable` interface supports
+/// setting a child as the placeholder by specifying “placeholder” as the “type”
+/// attribute of a `` element. See [method@Gtk.ListBox.set_placeholder]
+/// for info.
+///
+/// # CSS nodes
+///
+/// |[
+/// list[.separators][.rich-list][.navigation-sidebar][.boxed-list]
+/// ╰── row[.activatable]
+/// ]|
+///
+/// `GtkListBox` uses a single CSS node named list. It may carry the .separators
+/// style class, when the [property@Gtk.ListBox:show-separators] property is set.
+/// Each `GtkListBoxRow` uses a single CSS node named row. The row nodes get the
+/// .activatable style class added when appropriate.
+///
+/// It may also carry the .boxed-list style class. In this case, the list will be
+/// automatically surrounded by a frame and have separators.
+///
+/// The main list node may also carry style classes to select
+/// the style of [list presentation](section-list-widget.html#list-styles):
+/// .rich-list, .navigation-sidebar or .data-table.
+///
+/// # Accessibility
+///
+/// `GtkListBox` uses the %GTK_ACCESSIBLE_ROLE_LIST role and `GtkListBoxRow` uses
+/// the %GTK_ACCESSIBLE_ROLE_LIST_ITEM role.
+public struct ListBox: Widget where Element: Identifiable {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// Whether to accept unpaired release events.
+ var acceptUnpairedRelease: Bool?
+ /// Determines whether children can be activated with a single
+ /// click, or require a double-click.
+ var activateOnSingleClick: Bool?
+ /// Whether to show separators between rows.
+ var showSeparators: Bool?
+ /// activateCursorRow
+ var activateCursorRow: (() -> Void)?
+ /// moveCursor
+ var moveCursor: (() -> Void)?
+ /// Emitted when a row has been activated by the user.
+ var rowActivated: (() -> Void)?
+ /// Emitted when a new row is selected, or (with a %NULL @row)
+ /// when the selection is cleared.
+ ///
+ /// When the @box is using %GTK_SELECTION_MULTIPLE, this signal will not
+ /// give you the full picture of selection changes, and you should use
+ /// the [signal@Gtk.ListBox::selected-rows-changed] signal instead.
+ var rowSelected: (() -> Void)?
+ /// Emitted to select all children of the box, if the selection
+ /// mode permits it.
+ ///
+ /// This is a [keybinding signal](class.SignalAction.html).
+ ///
+ /// The default binding for this signal is Ctrl-a.
+ var selectAll: (() -> Void)?
+ /// Emitted when the set of selected rows changes.
+ var selectedRowsChanged: (() -> Void)?
+ /// toggleCursorRow
+ var toggleCursorRow: (() -> Void)?
+ /// Emitted to unselect all children of the box, if the selection
+ /// mode permits it.
+ ///
+ /// This is a [keybinding signal](class.SignalAction.html).
+ ///
+ /// The default binding for this signal is
+ /// Ctrl-Shift-a.
+ var unselectAll: (() -> Void)?
+ /// The dynamic widget elements.
+ var elements: [Element]
+ /// The dynamic widget content.
+ var content: (Element) -> Body
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `ListBox`.
+ public init(_ elements: [Element], @ViewBuilder content: @escaping (Element) -> Body) {
+ self.elements = elements
+ self.content = content
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(gtk_list_box_new()?.opaque())
+ update(storage, modifiers: modifiers)
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let activateCursorRow {
+ storage.connectSignal(name: "activate-cursor-row") {
+ activateCursorRow()
+ }
+ }
+ if let moveCursor {
+ storage.connectSignal(name: "move-cursor") {
+ moveCursor()
+ }
+ }
+ if let rowActivated {
+ storage.connectSignal(name: "row-activated") {
+ rowActivated()
+ }
+ }
+ if let rowSelected {
+ storage.connectSignal(name: "row-selected") {
+ rowSelected()
+ }
+ }
+ if let selectAll {
+ storage.connectSignal(name: "select-all") {
+ selectAll()
+ }
+ }
+ if let selectedRowsChanged {
+ storage.connectSignal(name: "selected-rows-changed") {
+ selectedRowsChanged()
+ }
+ }
+ if let toggleCursorRow {
+ storage.connectSignal(name: "toggle-cursor-row") {
+ toggleCursorRow()
+ }
+ }
+ if let unselectAll {
+ storage.connectSignal(name: "unselect-all") {
+ unselectAll()
+ }
+ }
+ storage.modify { widget in
+ if let activateOnSingleClick {
+ gtk_list_box_set_activate_on_single_click(widget, activateOnSingleClick.cBool)
+ }
+ if let showSeparators {
+ gtk_list_box_set_show_separators(widget, showSeparators.cBool)
+ }
+
+ var contentStorage: [ViewStorage] = storage.content[.mainContent] ?? []
+ let old = storage.fields["element"] as? [Element] ?? []
+ old.identifiableTransform(
+ to: elements,
+ functions: .init { index, element in
+ let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers)
+ gtk_list_box_remove(widget, gtk_list_box_get_row_at_index(widget, index.cInt)?.cast())
+ gtk_list_box_insert(widget, child.pointer?.cast(), index.cInt)
+ contentStorage.remove(at: index)
+ contentStorage.insert(child, at: index)
+ } delete: { index in
+ gtk_list_box_remove(widget, gtk_list_box_get_row_at_index(widget, index.cInt)?.cast())
+ contentStorage.remove(at: index)
+ } insert: { index, element in
+ let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers)
+ gtk_list_box_insert(widget, child.pointer?.cast(), index.cInt)
+ contentStorage.insert(child, at: index)
+ }
+ )
+ storage.fields["element"] = elements
+ storage.content[.mainContent] = contentStorage
+ for (index, element) in elements.enumerated() {
+ content(element).widget(modifiers: modifiers).update(contentStorage[index], modifiers: modifiers)
+ }
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// Whether to accept unpaired release events.
+ public func acceptUnpairedRelease(_ acceptUnpairedRelease: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.acceptUnpairedRelease = acceptUnpairedRelease
+
+ return newSelf
+ }
+
+ /// Determines whether children can be activated with a single
+ /// click, or require a double-click.
+ public func activateOnSingleClick(_ activateOnSingleClick: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.activateOnSingleClick = activateOnSingleClick
+
+ return newSelf
+ }
+
+ /// Whether to show separators between rows.
+ public func showSeparators(_ showSeparators: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.showSeparators = showSeparators
+
+ return newSelf
+ }
+
+ /// activateCursorRow
+ public func activateCursorRow(_ activateCursorRow: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.activateCursorRow = activateCursorRow
+ return newSelf
+ }
+
+ /// moveCursor
+ public func moveCursor(_ moveCursor: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.moveCursor = moveCursor
+ return newSelf
+ }
+
+ /// Emitted when a row has been activated by the user.
+ public func rowActivated(_ rowActivated: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.rowActivated = rowActivated
+ return newSelf
+ }
+
+ /// Emitted when a new row is selected, or (with a %NULL @row)
+ /// when the selection is cleared.
+ ///
+ /// When the @box is using %GTK_SELECTION_MULTIPLE, this signal will not
+ /// give you the full picture of selection changes, and you should use
+ /// the [signal@Gtk.ListBox::selected-rows-changed] signal instead.
+ public func rowSelected(_ rowSelected: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.rowSelected = rowSelected
+ return newSelf
+ }
+
+ /// Emitted to select all children of the box, if the selection
+ /// mode permits it.
+ ///
+ /// This is a [keybinding signal](class.SignalAction.html).
+ ///
+ /// The default binding for this signal is Ctrl-a.
+ public func selectAll(_ selectAll: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.selectAll = selectAll
+ return newSelf
+ }
+
+ /// Emitted when the set of selected rows changes.
+ public func selectedRowsChanged(_ selectedRowsChanged: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.selectedRowsChanged = selectedRowsChanged
+ return newSelf
+ }
+
+ /// toggleCursorRow
+ public func toggleCursorRow(_ toggleCursorRow: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.toggleCursorRow = toggleCursorRow
+ return newSelf
+ }
+
+ /// Emitted to unselect all children of the box, if the selection
+ /// mode permits it.
+ ///
+ /// This is a [keybinding signal](class.SignalAction.html).
+ ///
+ /// The default binding for this signal is
+ /// Ctrl-Shift-a.
+ public func unselectAll(_ unselectAll: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.unselectAll = unselectAll
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/Menu.swift b/Sources/Adwaita/View/Generated/Menu.swift
new file mode 100644
index 0000000..12cc597
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/Menu.swift
@@ -0,0 +1,289 @@
+//
+// Menu.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// The `GtkMenuButton` widget is used to display a popup when clicked.
+///
+/// ![An example GtkMenuButton](menu-button.png)
+///
+/// This popup can be provided either as a `GtkPopover` or as an abstract
+/// `GMenuModel`.
+///
+/// The `GtkMenuButton` widget can show either an icon (set with the
+/// [property@Gtk.MenuButton:icon-name] property) or a label (set with the
+/// [property@Gtk.MenuButton:label] property). If neither is explicitly set,
+/// a [class@Gtk.Image] is automatically created, using an arrow image oriented
+/// according to [property@Gtk.MenuButton:direction] or the generic
+/// “open-menu-symbolic” icon if the direction is not set.
+///
+/// The positioning of the popup is determined by the
+/// [property@Gtk.MenuButton:direction] property of the menu button.
+///
+/// For menus, the [property@Gtk.Widget:halign] and [property@Gtk.Widget:valign]
+/// properties of the menu are also taken into account. For example, when the
+/// direction is %GTK_ARROW_DOWN and the horizontal alignment is %GTK_ALIGN_START,
+/// the menu will be positioned below the button, with the starting edge
+/// (depending on the text direction) of the menu aligned with the starting
+/// edge of the button. If there is not enough space below the button, the
+/// menu is popped up above the button instead. If the alignment would move
+/// part of the menu offscreen, it is “pushed in”.
+///
+/// | | start | center | end |
+/// | - | --- | --- | --- |
+/// | **down** | ![](down-start.png) | ![](down-center.png) | ![](down-end.png) |
+/// | **up** | ![](up-start.png) | ![](up-center.png) | ![](up-end.png) |
+/// | **left** | ![](left-start.png) | ![](left-center.png) | ![](left-end.png) |
+/// | **right** | ![](right-start.png) | ![](right-center.png) | ![](right-end.png) |
+///
+/// # CSS nodes
+///
+/// ```
+/// menubutton
+/// ╰── button.toggle
+/// ╰── ╰── [arrow]
+/// ```
+///
+/// `GtkMenuButton` has a single CSS node with name `menubutton`
+/// which contains a `button` node with a `.toggle` style class.
+///
+/// If the button contains an icon, it will have the `.image-button` style class,
+/// if it contains text, it will have `.text-button` style class. If an arrow is
+/// visible in addition to an icon, text or a custom child, it will also have
+/// `.arrow-button` style class.
+///
+/// Inside the toggle button content, there is an `arrow` node for
+/// the indicator, which will carry one of the `.none`, `.up`, `.down`,
+/// `.left` or `.right` style classes to indicate the direction that
+/// the menu will appear in. The CSS is expected to provide a suitable
+/// image for each of these cases using the `-gtk-icon-source` property.
+///
+/// Optionally, the `menubutton` node can carry the `.circular` style class
+/// to request a round appearance.
+///
+/// # Accessibility
+///
+/// `GtkMenuButton` uses the %GTK_ACCESSIBLE_ROLE_BUTTON role.
+public struct Menu: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// Whether the menu button is active.
+ var active: Binding?
+ /// Whether to show a dropdown arrow even when using an icon or a custom child.
+ var alwaysShowArrow: Bool?
+ /// Whether the size of the button can be made smaller than the natural
+ /// size of its contents.
+ var canShrink: Bool?
+ /// The child widget.
+ var child: (() -> Body)?
+ /// Whether the button has a frame.
+ var hasFrame: Bool?
+ /// The name of the icon used to automatically populate the button.
+ var iconName: String?
+ /// The label for the button.
+ var label: String?
+ /// The `GMenuModel` from which the popup will be created.
+ ///
+ /// See [method@Gtk.MenuButton.set_menu_model] for the interaction
+ /// with the [property@Gtk.MenuButton:popover] property.
+ var menuModel: (() -> MenuContent)?
+ /// Whether the menu button acts as a primary menu.
+ ///
+ /// Primary menus can be opened using the F10 key
+ var primary: Bool?
+ /// If set an underscore in the text indicates a mnemonic.
+ var useUnderline: Bool?
+ /// Emitted to when the menu button is activated.
+ ///
+ /// The `::activate` signal on `GtkMenuButton` is an action signal and
+ /// emitting it causes the button to pop up its menu.
+ var activate: (() -> Void)?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `Menu`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(gtk_menu_button_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["child"] = [childStorage]
+ gtk_menu_button_set_child(storage.pointer, childStorage.pointer?.cast())
+ }
+ if let declarative = menuModel?(), let app {
+ let menu = g_menu_new()
+ gtk_menu_button_set_menu_model(storage.pointer, menu?.cast())
+ for item in declarative {
+ item.addMenuItems(menu: menu, app: app, window: window)
+ }
+ }
+
+
+ storage.notify(name: "active") {
+ active?.wrappedValue = gtk_menu_button_get_active(storage.pointer) != 0
+ }
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let activate {
+ storage.connectSignal(name: "activate") {
+ activate()
+ }
+ }
+ storage.modify { widget in
+ if let active {
+ gtk_menu_button_set_active(widget, active.wrappedValue.cBool)
+ }
+ if let alwaysShowArrow {
+ gtk_menu_button_set_always_show_arrow(widget, alwaysShowArrow.cBool)
+ }
+ if let canShrink {
+ gtk_menu_button_set_can_shrink(widget, canShrink.cBool)
+ }
+ if let widget = storage.content["child"]?.first {
+ child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let hasFrame {
+ gtk_menu_button_set_has_frame(widget, hasFrame.cBool)
+ }
+ if let iconName {
+ gtk_menu_button_set_icon_name(widget, iconName)
+ }
+ if let label, storage.content["child"] == nil {
+ gtk_menu_button_set_label(widget, label)
+ }
+ if let primary {
+ gtk_menu_button_set_primary(widget, primary.cBool)
+ }
+ if let useUnderline {
+ gtk_menu_button_set_use_underline(widget, useUnderline.cBool)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// Whether the menu button is active.
+ public func active(_ active: Binding?) -> Self {
+ var newSelf = self
+ newSelf.active = active
+
+ return newSelf
+ }
+
+ /// Whether to show a dropdown arrow even when using an icon or a custom child.
+ public func alwaysShowArrow(_ alwaysShowArrow: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.alwaysShowArrow = alwaysShowArrow
+
+ return newSelf
+ }
+
+ /// Whether the size of the button can be made smaller than the natural
+ /// size of its contents.
+ public func canShrink(_ canShrink: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.canShrink = canShrink
+
+ return newSelf
+ }
+
+ /// The child widget.
+ public func child(@ViewBuilder _ child: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.child = child
+
+ return newSelf
+ }
+
+ /// Whether the button has a frame.
+ public func hasFrame(_ hasFrame: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.hasFrame = hasFrame
+
+ return newSelf
+ }
+
+ /// The name of the icon used to automatically populate the button.
+ public func iconName(_ iconName: String?) -> Self {
+ var newSelf = self
+ newSelf.iconName = iconName
+
+ return newSelf
+ }
+
+ /// The label for the button.
+ public func label(_ label: String?) -> Self {
+ var newSelf = self
+ newSelf.label = label
+
+ return newSelf
+ }
+
+ /// The `GMenuModel` from which the popup will be created.
+ ///
+ /// See [method@Gtk.MenuButton.set_menu_model] for the interaction
+ /// with the [property@Gtk.MenuButton:popover] property.
+ public func menuModel(app: GTUIApp, window: GTUIApplicationWindow? = nil, @MenuBuilder _ menuModel: @escaping (() -> MenuContent)) -> Self {
+ var newSelf = self
+ newSelf.menuModel = menuModel
+ newSelf.app = app; newSelf.window = window
+ return newSelf
+ }
+
+ /// Whether the menu button acts as a primary menu.
+ ///
+ /// Primary menus can be opened using the F10 key
+ public func primary(_ primary: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.primary = primary
+
+ return newSelf
+ }
+
+ /// If set an underscore in the text indicates a mnemonic.
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+ /// Emitted to when the menu button is activated.
+ ///
+ /// The `::activate` signal on `GtkMenuButton` is an action signal and
+ /// emitting it causes the button to pop up its menu.
+ public func activate(_ activate: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.activate = activate
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/Overlay.swift b/Sources/Adwaita/View/Generated/Overlay.swift
new file mode 100644
index 0000000..a6ed828
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/Overlay.swift
@@ -0,0 +1,167 @@
+//
+// Overlay.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// `GtkOverlay` is a container which contains a single main child, on top
+/// of which it can place “overlay” widgets.
+///
+/// ![An example GtkOverlay](overlay.png)
+///
+/// The position of each overlay widget is determined by its
+/// [property@Gtk.Widget:halign] and [property@Gtk.Widget:valign]
+/// properties. E.g. a widget with both alignments set to %GTK_ALIGN_START
+/// will be placed at the top left corner of the `GtkOverlay` container,
+/// whereas an overlay with halign set to %GTK_ALIGN_CENTER and valign set
+/// to %GTK_ALIGN_END will be placed a the bottom edge of the `GtkOverlay`,
+/// horizontally centered. The position can be adjusted by setting the margin
+/// properties of the child to non-zero values.
+///
+/// More complicated placement of overlays is possible by connecting
+/// to the [signal@Gtk.Overlay::get-child-position] signal.
+///
+/// An overlay’s minimum and natural sizes are those of its main child.
+/// The sizes of overlay children are not considered when measuring these
+/// preferred sizes.
+///
+/// # GtkOverlay as GtkBuildable
+///
+/// The `GtkOverlay` implementation of the `GtkBuildable` interface
+/// supports placing a child as an overlay by specifying “overlay” as
+/// the “type” attribute of a `` element.
+///
+/// # CSS nodes
+///
+/// `GtkOverlay` has a single CSS node with the name “overlay”. Overlay children
+/// whose alignments cause them to be positioned at an edge get the style classes
+/// “.left”, “.right”, “.top”, and/or “.bottom” according to their position.
+public struct Overlay: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The main child widget.
+ var child: (() -> Body)?
+ /// Emitted to determine the position and size of any overlay
+ /// child widgets.
+ ///
+ /// A handler for this signal should fill @allocation with
+ /// the desired position and size for @widget, relative to
+ /// the 'main' child of @overlay.
+ ///
+ /// The default handler for this signal uses the @widget's
+ /// halign and valign properties to determine the position
+ /// and gives the widget its natural size (except that an
+ /// alignment of %GTK_ALIGN_FILL will cause the overlay to
+ /// be full-width/height). If the main child is a
+ /// `GtkScrolledWindow`, the overlays are placed relative
+ /// to its contents.
+ var getChildPosition: (() -> Void)?
+ /// The body for the widget "overlay".
+ var overlay: () -> Body = { [] }
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `Overlay`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(gtk_overlay_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["child"] = [childStorage]
+ gtk_overlay_set_child(storage.pointer, childStorage.pointer?.cast())
+ }
+
+ var overlayStorage: [ViewStorage] = []
+ for view in overlay() {
+ overlayStorage.append(view.storage(modifiers: modifiers))
+ gtk_overlay_add_overlay(storage.pointer, overlayStorage.last?.pointer?.cast())
+ }
+ storage.content["overlay"] = overlayStorage
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let getChildPosition {
+ storage.connectSignal(name: "get-child-position") {
+ getChildPosition()
+ }
+ }
+ storage.modify { widget in
+ if let widget = storage.content["child"]?.first {
+ child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+
+ if let overlayStorage = storage.content["overlay"] {
+ for (index, view) in overlay().enumerated() {
+ if let storage = overlayStorage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The main child widget.
+ public func child(@ViewBuilder _ child: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.child = child
+
+ return newSelf
+ }
+
+ /// Emitted to determine the position and size of any overlay
+ /// child widgets.
+ ///
+ /// A handler for this signal should fill @allocation with
+ /// the desired position and size for @widget, relative to
+ /// the 'main' child of @overlay.
+ ///
+ /// The default handler for this signal uses the @widget's
+ /// halign and valign properties to determine the position
+ /// and gives the widget its natural size (except that an
+ /// alignment of %GTK_ALIGN_FILL will cause the overlay to
+ /// be full-width/height). If the main child is a
+ /// `GtkScrolledWindow`, the overlays are placed relative
+ /// to its contents.
+ public func getChildPosition(_ getChildPosition: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.getChildPosition = getChildPosition
+ return newSelf
+ }
+
+ /// Set the body for "overlay".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func overlay(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.overlay = body
+ return newSelf
+ }
+}
diff --git a/Sources/Adwaita/View/Generated/OverlaySplitView.swift b/Sources/Adwaita/View/Generated/OverlaySplitView.swift
new file mode 100644
index 0000000..8ec0a8a
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/OverlaySplitView.swift
@@ -0,0 +1,361 @@
+//
+// OverlaySplitView.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A widget presenting sidebar and content side by side or as an overlay.
+///
+///
+///
+/// `AdwOverlaySplitView` has two children: sidebar and content, and displays
+/// them side by side.
+///
+/// When [property@OverlaySplitView:collapsed] is set to `TRUE`, the sidebar is
+/// instead shown as an overlay above the content widget.
+///
+/// The sidebar can be hidden or shown using the
+/// [property@OverlaySplitView:show-sidebar] property.
+///
+/// Sidebar can be displayed before or after the content, this can be controlled
+/// with the [property@OverlaySplitView:sidebar-position] property.
+///
+/// Collapsing the split view automatically hides the sidebar widget, and
+/// uncollapsing it shows the sidebar. If this behavior is not desired, the
+/// [property@OverlaySplitView:pin-sidebar] property can be used to override it.
+///
+/// `AdwOverlaySplitView` supports an edge swipe gesture for showing the sidebar,
+/// and a swipe from the sidebar for hiding it. Gestures are only supported on
+/// touchscreen, but not touchpad. Gestures can be controlled with the
+/// [property@OverlaySplitView:enable-show-gesture] and
+/// [property@OverlaySplitView:enable-hide-gesture] properties.
+///
+/// See also [class@NavigationSplitView].
+///
+/// `AdwOverlaySplitView` is typically used together with an [class@Breakpoint]
+/// setting the `collapsed` property to `TRUE` on small widths, as follows:
+///
+/// ```xml
+/// 360200800800max-width: 400spTrue
+/// ```
+///
+/// `AdwOverlaySplitView` is often used for implementing the
+/// [utility pane](https://developer.gnome.org/hig/patterns/containers/utility-panes.html)
+/// pattern.
+///
+/// ## Sizing
+///
+/// When not collapsed, `AdwOverlaySplitView` changes the sidebar width
+/// depending on its own width.
+///
+/// If possible, it tries to allocate a fraction of the total width, controlled
+/// with the [property@OverlaySplitView:sidebar-width-fraction] property.
+///
+/// The sidebar also has minimum and maximum sizes, controlled with the
+/// [property@OverlaySplitView:min-sidebar-width] and
+/// [property@OverlaySplitView:max-sidebar-width] properties.
+///
+/// The minimum and maximum sizes are using the length unit specified with the
+/// [property@OverlaySplitView:sidebar-width-unit].
+///
+/// By default, sidebar is using 25% of the total width, with 180sp as the
+/// minimum size and 280sp as the maximum size.
+///
+/// When collapsed, the preferred width fraction is ignored and the sidebar uses
+/// [property@OverlaySplitView:max-sidebar-width] when possible.
+///
+/// ## Header Bar Integration
+///
+/// When used inside `AdwOverlaySplitView`, [class@HeaderBar] will automatically
+/// hide the window buttons in the middle.
+///
+/// ## `AdwOverlaySplitView` as `GtkBuildable`
+///
+/// The `AdwOverlaySplitView` implementation of the [iface@Gtk.Buildable]
+/// interface supports setting the sidebar widget by specifying “sidebar” as the
+/// “type” attribute of a `` element, Specifying “content” child type or
+/// omitting it results in setting the content widget.
+///
+/// ## CSS nodes
+///
+/// `AdwOverlaySplitView` has a single CSS node with the name
+/// `overlay-split-view`.
+///
+/// It contains two nodes with the name `widget`, containing the sidebar and
+/// content children.
+///
+/// When not collapsed, they have the `.sidebar-view` and `.content-view` style
+/// classes respectively.
+///
+/// ```
+/// overlay-split-view
+/// ├── widget.sidebar-pane
+/// │ ╰── [sidebar child]
+/// ╰── widget.content-pane
+/// ╰── [content child]
+/// ```
+///
+/// When collapsed, the one containing the sidebar child has the `.background`
+/// style class and the other one has no style classes.
+///
+/// ```
+/// overlay-split-view
+/// ├── widget.background
+/// │ ╰── [sidebar child]
+/// ╰── widget
+/// ╰── [content child]
+/// ```
+///
+/// ## Accessibility
+///
+/// `AdwOverlaySplitView` uses the `GTK_ACCESSIBLE_ROLE_GROUP` role.
+public struct OverlaySplitView: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// Whether the split view is collapsed.
+ ///
+ /// When collapsed, the sidebar widget is presented as an overlay above the
+ /// content widget, otherwise they are displayed side by side.
+ var collapsed: Bool?
+ /// The content widget.
+ var content: (() -> Body)?
+ /// Whether the sidebar can be closed with a swipe gesture.
+ ///
+ /// Only touchscreen swipes are supported.
+ var enableHideGesture: Bool?
+ /// Whether the sidebar can be opened with an edge swipe gesture.
+ ///
+ /// Only touchscreen swipes are supported.
+ var enableShowGesture: Bool?
+ /// The maximum sidebar width.
+ ///
+ /// Maximum width is affected by
+ /// [property@OverlaySplitView:sidebar-width-unit].
+ ///
+ /// The sidebar widget can still be allocated with larger width if its own
+ /// minimum width exceeds it.
+ var maxSidebarWidth: Double?
+ /// The minimum sidebar width.
+ ///
+ /// Minimum width is affected by
+ /// [property@OverlaySplitView:sidebar-width-unit].
+ ///
+ /// The sidebar widget can still be allocated with larger width if its own
+ /// minimum width exceeds it.
+ var minSidebarWidth: Double?
+ /// Whether the sidebar widget is pinned.
+ ///
+ /// By default, collapsing @self automatically hides the sidebar widget, and
+ /// uncollapsing it shows the sidebar. If set to `TRUE`, sidebar visibility
+ /// never changes on its own.
+ var pinSidebar: Bool?
+ /// Whether the sidebar widget is shown.
+ var showSidebar: Binding?
+ /// The sidebar widget.
+ var sidebar: (() -> Body)?
+ /// The preferred sidebar width as a fraction of the total width.
+ ///
+ /// The preferred width is additionally limited by
+ /// [property@OverlaySplitView:min-sidebar-width] and
+ /// [property@OverlaySplitView:max-sidebar-width].
+ ///
+ /// The sidebar widget can be allocated with larger width if its own minimum
+ /// width exceeds the preferred width.
+ var sidebarWidthFraction: Double?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `OverlaySplitView`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_overlay_split_view_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let contentStorage = content?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["content"] = [contentStorage]
+ adw_overlay_split_view_set_content(storage.pointer, contentStorage.pointer?.cast())
+ }
+ if let sidebarStorage = sidebar?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["sidebar"] = [sidebarStorage]
+ adw_overlay_split_view_set_sidebar(storage.pointer, sidebarStorage.pointer?.cast())
+ }
+
+
+ storage.notify(name: "show-sidebar") {
+ showSidebar?.wrappedValue = adw_overlay_split_view_get_show_sidebar(storage.pointer) != 0
+ }
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let collapsed {
+ adw_overlay_split_view_set_collapsed(widget, collapsed.cBool)
+ }
+ if let widget = storage.content["content"]?.first {
+ content?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let enableHideGesture {
+ adw_overlay_split_view_set_enable_hide_gesture(widget, enableHideGesture.cBool)
+ }
+ if let enableShowGesture {
+ adw_overlay_split_view_set_enable_show_gesture(widget, enableShowGesture.cBool)
+ }
+ if let maxSidebarWidth {
+ adw_overlay_split_view_set_max_sidebar_width(widget, maxSidebarWidth)
+ }
+ if let minSidebarWidth {
+ adw_overlay_split_view_set_min_sidebar_width(widget, minSidebarWidth)
+ }
+ if let pinSidebar {
+ adw_overlay_split_view_set_pin_sidebar(widget, pinSidebar.cBool)
+ }
+ if let showSidebar {
+ adw_overlay_split_view_set_show_sidebar(widget, showSidebar.wrappedValue.cBool)
+ }
+ if let widget = storage.content["sidebar"]?.first {
+ sidebar?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let sidebarWidthFraction {
+ adw_overlay_split_view_set_sidebar_width_fraction(widget, sidebarWidthFraction)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// Whether the split view is collapsed.
+ ///
+ /// When collapsed, the sidebar widget is presented as an overlay above the
+ /// content widget, otherwise they are displayed side by side.
+ public func collapsed(_ collapsed: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.collapsed = collapsed
+
+ return newSelf
+ }
+
+ /// The content widget.
+ public func content(@ViewBuilder _ content: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.content = content
+
+ return newSelf
+ }
+
+ /// Whether the sidebar can be closed with a swipe gesture.
+ ///
+ /// Only touchscreen swipes are supported.
+ public func enableHideGesture(_ enableHideGesture: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.enableHideGesture = enableHideGesture
+
+ return newSelf
+ }
+
+ /// Whether the sidebar can be opened with an edge swipe gesture.
+ ///
+ /// Only touchscreen swipes are supported.
+ public func enableShowGesture(_ enableShowGesture: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.enableShowGesture = enableShowGesture
+
+ return newSelf
+ }
+
+ /// The maximum sidebar width.
+ ///
+ /// Maximum width is affected by
+ /// [property@OverlaySplitView:sidebar-width-unit].
+ ///
+ /// The sidebar widget can still be allocated with larger width if its own
+ /// minimum width exceeds it.
+ public func maxSidebarWidth(_ maxSidebarWidth: Double?) -> Self {
+ var newSelf = self
+ newSelf.maxSidebarWidth = maxSidebarWidth
+
+ return newSelf
+ }
+
+ /// The minimum sidebar width.
+ ///
+ /// Minimum width is affected by
+ /// [property@OverlaySplitView:sidebar-width-unit].
+ ///
+ /// The sidebar widget can still be allocated with larger width if its own
+ /// minimum width exceeds it.
+ public func minSidebarWidth(_ minSidebarWidth: Double?) -> Self {
+ var newSelf = self
+ newSelf.minSidebarWidth = minSidebarWidth
+
+ return newSelf
+ }
+
+ /// Whether the sidebar widget is pinned.
+ ///
+ /// By default, collapsing @self automatically hides the sidebar widget, and
+ /// uncollapsing it shows the sidebar. If set to `TRUE`, sidebar visibility
+ /// never changes on its own.
+ public func pinSidebar(_ pinSidebar: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.pinSidebar = pinSidebar
+
+ return newSelf
+ }
+
+ /// Whether the sidebar widget is shown.
+ public func showSidebar(_ showSidebar: Binding?) -> Self {
+ var newSelf = self
+ newSelf.showSidebar = showSidebar
+
+ return newSelf
+ }
+
+ /// The sidebar widget.
+ public func sidebar(@ViewBuilder _ sidebar: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.sidebar = sidebar
+
+ return newSelf
+ }
+
+ /// The preferred sidebar width as a fraction of the total width.
+ ///
+ /// The preferred width is additionally limited by
+ /// [property@OverlaySplitView:min-sidebar-width] and
+ /// [property@OverlaySplitView:max-sidebar-width].
+ ///
+ /// The sidebar widget can be allocated with larger width if its own minimum
+ /// width exceeds the preferred width.
+ public func sidebarWidthFraction(_ sidebarWidthFraction: Double?) -> Self {
+ var newSelf = self
+ newSelf.sidebarWidthFraction = sidebarWidthFraction
+
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/PasswordEntryRow.swift b/Sources/Adwaita/View/Generated/PasswordEntryRow.swift
new file mode 100644
index 0000000..ce96051
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/PasswordEntryRow.swift
@@ -0,0 +1,248 @@
+//
+// PasswordEntryRow.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A [class@EntryRow] tailored for entering secrets.
+///
+///
+///
+/// It does not show its contents in clear text, does not allow to copy it to the
+/// clipboard, and shows a warning when Caps Lock is engaged. If the underlying
+/// platform allows it, `AdwPasswordEntryRow` will also place the text in a
+/// non-pageable memory area, to avoid it being written out to disk by the
+/// operating system.
+///
+/// It offer a way to reveal the contents in clear text.
+///
+/// ## CSS Nodes
+///
+/// `AdwPasswordEntryRow` has a single CSS node with name `row` that carries
+/// `.entry` and `.password` style classes.
+public struct PasswordEntryRow: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// Whether activating the embedded entry can activate the default widget.
+ var activatesDefault: Bool?
+ /// Whether to suggest emoji replacements on the entry row.
+ ///
+ /// Emoji replacement is done with :-delimited names, like `:heart:`.
+ var enableEmojiCompletion: Bool?
+ /// Whether to show the apply button.
+ ///
+ /// When set to `TRUE`, typing text in the entry will reveal an apply button.
+ /// Clicking it or pressing the Enter key will hide the button and
+ /// emit the [signal@EntryRow::apply] signal.
+ ///
+ /// This is useful if changing the entry contents can trigger an expensive
+ /// operation, e.g. network activity, to avoid triggering it after typing every
+ /// character.
+ var showApplyButton: Bool?
+ /// The title of the preference represented by this row.
+ ///
+ /// The title is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ var title: String?
+ /// Whether the user can copy the title from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ var titleSelectable: Bool?
+ /// Whether to use Pango markup for the title label.
+ ///
+ /// Subclasses may also use it for other labels, such as subtitle.
+ ///
+ /// See also [func@Pango.parse_markup].
+ var useMarkup: Bool?
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ var useUnderline: Bool?
+ /// Emitted when the apply button is pressed.
+ ///
+ /// See [property@EntryRow:show-apply-button].
+ var apply: (() -> Void)?
+ /// Emitted when the embedded entry is activated.
+ var entryActivated: (() -> Void)?
+ /// The body for the widget "suffix".
+ var suffix: () -> Body = { [] }
+ /// The body for the widget "prefix".
+ var prefix: () -> Body = { [] }
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `PasswordEntryRow`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_password_entry_row_new()?.opaque())
+ update(storage, modifiers: modifiers)
+
+ var suffixStorage: [ViewStorage] = []
+ for view in suffix() {
+ suffixStorage.append(view.storage(modifiers: modifiers))
+ adw_entry_row_add_suffix(storage.pointer?.cast(), suffixStorage.last?.pointer?.cast())
+ }
+ storage.content["suffix"] = suffixStorage
+ var prefixStorage: [ViewStorage] = []
+ for view in prefix() {
+ prefixStorage.append(view.storage(modifiers: modifiers))
+ adw_entry_row_add_prefix(storage.pointer?.cast(), prefixStorage.last?.pointer?.cast())
+ }
+ storage.content["prefix"] = prefixStorage
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let apply {
+ storage.connectSignal(name: "apply") {
+ apply()
+ }
+ }
+ if let entryActivated {
+ storage.connectSignal(name: "entry-activated") {
+ entryActivated()
+ }
+ }
+ storage.modify { widget in
+ if let activatesDefault {
+ adw_entry_row_set_activates_default(widget?.cast(), activatesDefault.cBool)
+ }
+ if let enableEmojiCompletion {
+ adw_entry_row_set_enable_emoji_completion(widget?.cast(), enableEmojiCompletion.cBool)
+ }
+ if let showApplyButton {
+ adw_entry_row_set_show_apply_button(widget?.cast(), showApplyButton.cBool)
+ }
+ if let title {
+ adw_preferences_row_set_title(widget?.cast(), title)
+ }
+ if let titleSelectable {
+ adw_preferences_row_set_title_selectable(widget?.cast(), titleSelectable.cBool)
+ }
+ if let useMarkup {
+ adw_preferences_row_set_use_markup(widget?.cast(), useMarkup.cBool)
+ }
+ if let useUnderline {
+ adw_preferences_row_set_use_underline(widget?.cast(), useUnderline.cBool)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// Whether activating the embedded entry can activate the default widget.
+ public func activatesDefault(_ activatesDefault: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.activatesDefault = activatesDefault
+
+ return newSelf
+ }
+
+ /// Whether to suggest emoji replacements on the entry row.
+ ///
+ /// Emoji replacement is done with :-delimited names, like `:heart:`.
+ public func enableEmojiCompletion(_ enableEmojiCompletion: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.enableEmojiCompletion = enableEmojiCompletion
+
+ return newSelf
+ }
+
+ /// Whether to show the apply button.
+ ///
+ /// When set to `TRUE`, typing text in the entry will reveal an apply button.
+ /// Clicking it or pressing the Enter key will hide the button and
+ /// emit the [signal@EntryRow::apply] signal.
+ ///
+ /// This is useful if changing the entry contents can trigger an expensive
+ /// operation, e.g. network activity, to avoid triggering it after typing every
+ /// character.
+ public func showApplyButton(_ showApplyButton: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.showApplyButton = showApplyButton
+
+ return newSelf
+ }
+
+ /// The title of the preference represented by this row.
+ ///
+ /// The title is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ public func title(_ title: String?) -> Self {
+ var newSelf = self
+ newSelf.title = title
+
+ return newSelf
+ }
+
+ /// Whether the user can copy the title from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ public func titleSelectable(_ titleSelectable: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.titleSelectable = titleSelectable
+
+ return newSelf
+ }
+
+ /// Whether to use Pango markup for the title label.
+ ///
+ /// Subclasses may also use it for other labels, such as subtitle.
+ ///
+ /// See also [func@Pango.parse_markup].
+ public func useMarkup(_ useMarkup: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useMarkup = useMarkup
+
+ return newSelf
+ }
+
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+ /// Emitted when the apply button is pressed.
+ ///
+ /// See [property@EntryRow:show-apply-button].
+ public func apply(_ apply: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.apply = apply
+ return newSelf
+ }
+
+ /// Emitted when the embedded entry is activated.
+ public func entryActivated(_ entryActivated: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.entryActivated = entryActivated
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/PreferencesGroup.swift b/Sources/Adwaita/View/Generated/PreferencesGroup.swift
new file mode 100644
index 0000000..0f50d71
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/PreferencesGroup.swift
@@ -0,0 +1,159 @@
+//
+// PreferencesGroup.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A group of preference rows.
+///
+///
+///
+/// An `AdwPreferencesGroup` represents a group or tightly related preferences,
+/// which in turn are represented by [class@PreferencesRow].
+///
+/// To summarize the role of the preferences it gathers, a group can have both a
+/// title and a description. The title will be used by [class@PreferencesWindow]
+/// to let the user look for a preference.
+///
+/// ## AdwPreferencesGroup as GtkBuildable
+///
+/// The `AdwPreferencesGroup` implementation of the [iface@Gtk.Buildable] interface
+/// supports adding [class@PreferencesRow]s to the list by omitting "type". If "type"
+/// is omitted and the widget isn't a [class@PreferencesRow] the child is added to
+/// a box below the list.
+///
+/// When the "type" attribute of a child is `header-suffix`, the child
+/// is set as the suffix on the end of the title and description.
+///
+/// ## CSS nodes
+///
+/// `AdwPreferencesGroup` has a single CSS node with name `preferencesgroup`.
+///
+/// ## Accessibility
+///
+/// `AdwPreferencesGroup` uses the `GTK_ACCESSIBLE_ROLE_GROUP` role.
+public struct PreferencesGroup: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The description for this group of preferences.
+ var description: String?
+ /// The header suffix widget.
+ ///
+ /// Displayed above the list, next to the title and description.
+ ///
+ /// Suffixes are commonly used to show a button or a spinner for the whole
+ /// group.
+ var headerSuffix: (() -> Body)?
+ /// The title for this group of preferences.
+ var title: String?
+ /// The body for the widget "child".
+ var child: () -> Body = { [] }
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `PreferencesGroup`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_preferences_group_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let headerSuffixStorage = headerSuffix?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["headerSuffix"] = [headerSuffixStorage]
+ adw_preferences_group_set_header_suffix(storage.pointer?.cast(), headerSuffixStorage.pointer?.cast())
+ }
+
+ var childStorage: [ViewStorage] = []
+ for view in child() {
+ childStorage.append(view.storage(modifiers: modifiers))
+ adw_preferences_group_add(storage.pointer?.cast(), childStorage.last?.pointer?.cast())
+ }
+ storage.content["child"] = childStorage
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let description {
+ adw_preferences_group_set_description(widget?.cast(), description)
+ }
+ if let widget = storage.content["headerSuffix"]?.first {
+ headerSuffix?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let title {
+ adw_preferences_group_set_title(widget?.cast(), title)
+ }
+
+ if let childStorage = storage.content["child"] {
+ for (index, view) in child().enumerated() {
+ if let storage = childStorage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The description for this group of preferences.
+ public func description(_ description: String?) -> Self {
+ var newSelf = self
+ newSelf.description = description
+
+ return newSelf
+ }
+
+ /// The header suffix widget.
+ ///
+ /// Displayed above the list, next to the title and description.
+ ///
+ /// Suffixes are commonly used to show a button or a spinner for the whole
+ /// group.
+ public func headerSuffix(@ViewBuilder _ headerSuffix: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.headerSuffix = headerSuffix
+
+ return newSelf
+ }
+
+ /// The title for this group of preferences.
+ public func title(_ title: String?) -> Self {
+ var newSelf = self
+ newSelf.title = title
+
+ return newSelf
+ }
+
+ /// Set the body for "child".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func child(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.child = body
+ return newSelf
+ }
+}
diff --git a/Sources/Adwaita/View/Generated/PreferencesPage.swift b/Sources/Adwaita/View/Generated/PreferencesPage.swift
new file mode 100644
index 0000000..aa9a174
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/PreferencesPage.swift
@@ -0,0 +1,157 @@
+//
+// PreferencesPage.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A page from [class@PreferencesWindow].
+///
+///
+///
+/// The `AdwPreferencesPage` widget gathers preferences groups into a single page
+/// of a preferences window.
+///
+/// ## CSS nodes
+///
+/// `AdwPreferencesPage` has a single CSS node with name `preferencespage`.
+///
+/// ## Accessibility
+///
+/// `AdwPreferencesPage` uses the `GTK_ACCESSIBLE_ROLE_GROUP` role.
+public struct PreferencesPage: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The description to be displayed at the top of the page.
+ var description: String?
+ /// The icon name for this page.
+ var iconName: String?
+ /// The name of this page.
+ var name: String?
+ /// The title for this page.
+ var title: String?
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ var useUnderline: Bool?
+ /// The body for the widget "child".
+ var child: () -> Body = { [] }
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `PreferencesPage`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_preferences_page_new()?.opaque())
+ update(storage, modifiers: modifiers)
+
+ var childStorage: [ViewStorage] = []
+ for view in child() {
+ childStorage.append(view.storage(modifiers: modifiers))
+ adw_preferences_group_add(storage.pointer?.cast(), childStorage.last?.pointer?.cast())
+ }
+ storage.content["child"] = childStorage
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let description {
+ adw_preferences_page_set_description(widget?.cast(), description)
+ }
+ if let iconName {
+ adw_preferences_page_set_icon_name(widget?.cast(), iconName)
+ }
+ if let name {
+ adw_preferences_page_set_name(widget?.cast(), name)
+ }
+ if let title {
+ adw_preferences_page_set_title(widget?.cast(), title)
+ }
+ if let useUnderline {
+ adw_preferences_page_set_use_underline(widget?.cast(), useUnderline.cBool)
+ }
+
+ if let childStorage = storage.content["child"] {
+ for (index, view) in child().enumerated() {
+ if let storage = childStorage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The description to be displayed at the top of the page.
+ public func description(_ description: String?) -> Self {
+ var newSelf = self
+ newSelf.description = description
+
+ return newSelf
+ }
+
+ /// The icon name for this page.
+ public func iconName(_ iconName: String?) -> Self {
+ var newSelf = self
+ newSelf.iconName = iconName
+
+ return newSelf
+ }
+
+ /// The name of this page.
+ public func name(_ name: String?) -> Self {
+ var newSelf = self
+ newSelf.name = name
+
+ return newSelf
+ }
+
+ /// The title for this page.
+ public func title(_ title: String?) -> Self {
+ var newSelf = self
+ newSelf.title = title
+
+ return newSelf
+ }
+
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+ /// Set the body for "child".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func child(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.child = body
+ return newSelf
+ }
+}
diff --git a/Sources/Adwaita/View/Generated/PreferencesRow.swift b/Sources/Adwaita/View/Generated/PreferencesRow.swift
new file mode 100644
index 0000000..922d954
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/PreferencesRow.swift
@@ -0,0 +1,134 @@
+//
+// PreferencesRow.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A [class@Gtk.ListBoxRow] used to present preferences.
+///
+/// The `AdwPreferencesRow` widget has a title that [class@PreferencesWindow]
+/// will use to let the user look for a preference. It doesn't present the title
+/// in any way and lets you present the preference as you please.
+///
+/// [class@ActionRow] and its derivatives are convenient to use as preference
+/// rows as they take care of presenting the preference's title while letting you
+/// compose the inputs of the preference around it.
+public struct PreferencesRow: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The title of the preference represented by this row.
+ ///
+ /// The title is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ var title: String?
+ /// Whether the user can copy the title from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ var titleSelectable: Bool?
+ /// Whether to use Pango markup for the title label.
+ ///
+ /// Subclasses may also use it for other labels, such as subtitle.
+ ///
+ /// See also [func@Pango.parse_markup].
+ var useMarkup: Bool?
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ var useUnderline: Bool?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `PreferencesRow`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_preferences_row_new()?.opaque())
+ update(storage, modifiers: modifiers)
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let title {
+ adw_preferences_row_set_title(widget?.cast(), title)
+ }
+ if let titleSelectable {
+ adw_preferences_row_set_title_selectable(widget?.cast(), titleSelectable.cBool)
+ }
+ if let useMarkup {
+ adw_preferences_row_set_use_markup(widget?.cast(), useMarkup.cBool)
+ }
+ if let useUnderline {
+ adw_preferences_row_set_use_underline(widget?.cast(), useUnderline.cBool)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The title of the preference represented by this row.
+ ///
+ /// The title is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ public func title(_ title: String?) -> Self {
+ var newSelf = self
+ newSelf.title = title
+
+ return newSelf
+ }
+
+ /// Whether the user can copy the title from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ public func titleSelectable(_ titleSelectable: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.titleSelectable = titleSelectable
+
+ return newSelf
+ }
+
+ /// Whether to use Pango markup for the title label.
+ ///
+ /// Subclasses may also use it for other labels, such as subtitle.
+ ///
+ /// See also [func@Pango.parse_markup].
+ public func useMarkup(_ useMarkup: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useMarkup = useMarkup
+
+ return newSelf
+ }
+
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/ProgressBar.swift b/Sources/Adwaita/View/Generated/ProgressBar.swift
new file mode 100644
index 0000000..4d86290
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/ProgressBar.swift
@@ -0,0 +1,184 @@
+//
+// ProgressBar.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// `GtkProgressBar` is typically used to display the progress of a long
+/// running operation.
+///
+/// It provides a visual clue that processing is underway. `GtkProgressBar`
+/// can be used in two different modes: percentage mode and activity mode.
+///
+/// ![An example GtkProgressBar](progressbar.png)
+///
+/// When an application can determine how much work needs to take place
+/// (e.g. read a fixed number of bytes from a file) and can monitor its
+/// progress, it can use the `GtkProgressBar` in percentage mode and the
+/// user sees a growing bar indicating the percentage of the work that
+/// has been completed. In this mode, the application is required to call
+/// [method@Gtk.ProgressBar.set_fraction] periodically to update the progress bar.
+///
+/// When an application has no accurate way of knowing the amount of work
+/// to do, it can use the `GtkProgressBar` in activity mode, which shows
+/// activity by a block moving back and forth within the progress area. In
+/// this mode, the application is required to call [method@Gtk.ProgressBar.pulse]
+/// periodically to update the progress bar.
+///
+/// There is quite a bit of flexibility provided to control the appearance
+/// of the `GtkProgressBar`. Functions are provided to control the orientation
+/// of the bar, optional text can be displayed along with the bar, and the
+/// step size used in activity mode can be set.
+///
+/// # CSS nodes
+///
+/// ```
+/// progressbar[.osd]
+/// ├── [text]
+/// ╰── trough[.empty][.full]
+/// ╰── progress[.pulse]
+/// ```
+///
+/// `GtkProgressBar` has a main CSS node with name progressbar and subnodes with
+/// names text and trough, of which the latter has a subnode named progress. The
+/// text subnode is only present if text is shown. The progress subnode has the
+/// style class .pulse when in activity mode. It gets the style classes .left,
+/// .right, .top or .bottom added when the progress 'touches' the corresponding
+/// end of the GtkProgressBar. The .osd class on the progressbar node is for use
+/// in overlays like the one Epiphany has for page loading progress.
+///
+/// # Accessibility
+///
+/// `GtkProgressBar` uses the %GTK_ACCESSIBLE_ROLE_PROGRESS_BAR role.
+public struct ProgressBar: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The fraction of total work that has been completed.
+ var fraction: Double?
+ /// Invert the direction in which the progress bar grows.
+ var inverted: Bool?
+ /// The fraction of total progress to move the bounding block when pulsed.
+ var pulseStep: Double?
+ /// Sets whether the progress bar will show a text in addition
+ /// to the bar itself.
+ ///
+ /// The shown text is either the value of the [property@Gtk.ProgressBar:text]
+ /// property or, if that is %NULL, the [property@Gtk.ProgressBar:fraction]
+ /// value, as a percentage.
+ ///
+ /// To make a progress bar that is styled and sized suitably for showing text
+ /// (even if the actual text is blank), set [property@Gtk.ProgressBar:show-text]
+ /// to %TRUE and [property@Gtk.ProgressBar:text] to the empty string (not %NULL).
+ var showText: Bool?
+ /// Text to be displayed in the progress bar.
+ var text: String?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `ProgressBar`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(gtk_progress_bar_new()?.opaque())
+ update(storage, modifiers: modifiers)
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let fraction {
+ gtk_progress_bar_set_fraction(widget, fraction)
+ }
+ if let inverted {
+ gtk_progress_bar_set_inverted(widget, inverted.cBool)
+ }
+ if let pulseStep {
+ gtk_progress_bar_set_pulse_step(widget, pulseStep)
+ }
+ if let showText {
+ gtk_progress_bar_set_show_text(widget, showText.cBool)
+ }
+ if let text {
+ gtk_progress_bar_set_text(widget, text)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The fraction of total work that has been completed.
+ public func fraction(_ fraction: Double?) -> Self {
+ var newSelf = self
+ newSelf.fraction = fraction
+
+ return newSelf
+ }
+
+ /// Invert the direction in which the progress bar grows.
+ public func inverted(_ inverted: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.inverted = inverted
+
+ return newSelf
+ }
+
+ /// The fraction of total progress to move the bounding block when pulsed.
+ public func pulseStep(_ pulseStep: Double?) -> Self {
+ var newSelf = self
+ newSelf.pulseStep = pulseStep
+
+ return newSelf
+ }
+
+ /// Sets whether the progress bar will show a text in addition
+ /// to the bar itself.
+ ///
+ /// The shown text is either the value of the [property@Gtk.ProgressBar:text]
+ /// property or, if that is %NULL, the [property@Gtk.ProgressBar:fraction]
+ /// value, as a percentage.
+ ///
+ /// To make a progress bar that is styled and sized suitably for showing text
+ /// (even if the actual text is blank), set [property@Gtk.ProgressBar:show-text]
+ /// to %TRUE and [property@Gtk.ProgressBar:text] to the empty string (not %NULL).
+ public func showText(_ showText: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.showText = showText
+
+ return newSelf
+ }
+
+ /// Text to be displayed in the progress bar.
+ public func text(_ text: String?) -> Self {
+ var newSelf = self
+ newSelf.text = text
+
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/ScrolledWindow.swift b/Sources/Adwaita/View/Generated/ScrolledWindow.swift
new file mode 100644
index 0000000..31f0079
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/ScrolledWindow.swift
@@ -0,0 +1,403 @@
+//
+// ScrolledWindow.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// `GtkScrolledWindow` is a container that makes its child scrollable.
+///
+/// It does so using either internally added scrollbars or externally
+/// associated adjustments, and optionally draws a frame around the child.
+///
+/// Widgets with native scrolling support, i.e. those whose classes implement
+/// the [iface@Gtk.Scrollable] interface, are added directly. For other types
+/// of widget, the class [class@Gtk.Viewport] acts as an adaptor, giving
+/// scrollability to other widgets. [method@Gtk.ScrolledWindow.set_child]
+/// intelligently accounts for whether or not the added child is a `GtkScrollable`.
+/// If it isn’t, then it wraps the child in a `GtkViewport`. Therefore, you can
+/// just add any child widget and not worry about the details.
+///
+/// If [method@Gtk.ScrolledWindow.set_child] has added a `GtkViewport` for you,
+/// it will be automatically removed when you unset the child.
+/// Unless [property@Gtk.ScrolledWindow:hscrollbar-policy] and
+/// [property@Gtk.ScrolledWindow:vscrollbar-policy] are %GTK_POLICY_NEVER or
+/// %GTK_POLICY_EXTERNAL, `GtkScrolledWindow` adds internal `GtkScrollbar` widgets
+/// around its child. The scroll position of the child, and if applicable the
+/// scrollbars, is controlled by the [property@Gtk.ScrolledWindow:hadjustment]
+/// and [property@Gtk.ScrolledWindow:vadjustment] that are associated with the
+/// `GtkScrolledWindow`. See the docs on [class@Gtk.Scrollbar] for the details,
+/// but note that the “step_increment” and “page_increment” fields are only
+/// effective if the policy causes scrollbars to be present.
+///
+/// If a `GtkScrolledWindow` doesn’t behave quite as you would like, or
+/// doesn’t have exactly the right layout, it’s very possible to set up
+/// your own scrolling with `GtkScrollbar` and for example a `GtkGrid`.
+///
+/// # Touch support
+///
+/// `GtkScrolledWindow` has built-in support for touch devices. When a
+/// touchscreen is used, swiping will move the scrolled window, and will
+/// expose 'kinetic' behavior. This can be turned off with the
+/// [property@Gtk.ScrolledWindow:kinetic-scrolling] property if it is undesired.
+///
+/// `GtkScrolledWindow` also displays visual 'overshoot' indication when
+/// the content is pulled beyond the end, and this situation can be
+/// captured with the [signal@Gtk.ScrolledWindow::edge-overshot] signal.
+///
+/// If no mouse device is present, the scrollbars will overlaid as
+/// narrow, auto-hiding indicators over the content. If traditional
+/// scrollbars are desired although no mouse is present, this behaviour
+/// can be turned off with the [property@Gtk.ScrolledWindow:overlay-scrolling]
+/// property.
+///
+/// # CSS nodes
+///
+/// `GtkScrolledWindow` has a main CSS node with name scrolledwindow.
+/// It gets a .frame style class added when [property@Gtk.ScrolledWindow:has-frame]
+/// is %TRUE.
+///
+/// It uses subnodes with names overshoot and undershoot to draw the overflow
+/// and underflow indications. These nodes get the .left, .right, .top or .bottom
+/// style class added depending on where the indication is drawn.
+///
+/// `GtkScrolledWindow` also sets the positional style classes (.left, .right,
+/// .top, .bottom) and style classes related to overlay scrolling
+/// (.overlay-indicator, .dragging, .hovering) on its scrollbars.
+///
+/// If both scrollbars are visible, the area where they meet is drawn
+/// with a subnode named junction.
+///
+/// # Accessibility
+///
+/// Until GTK 4.10, `GtkScrolledWindow` used the `GTK_ACCESSIBLE_ROLE_GROUP` role.
+///
+/// Starting from GTK 4.12, `GtkScrolledWindow` uses the `GTK_ACCESSIBLE_ROLE_GENERIC` role.
+public struct ScrolledWindow: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The child widget.
+ var child: (() -> Body)?
+ /// Whether to draw a frame around the contents.
+ var hasFrame: Bool?
+ /// Whether kinetic scrolling is enabled or not.
+ ///
+ /// Kinetic scrolling only applies to devices with source %GDK_SOURCE_TOUCHSCREEN.
+ var kineticScrolling: Bool?
+ /// The maximum content height of @scrolled_window.
+ var maxContentHeight: Int?
+ /// The maximum content width of @scrolled_window.
+ var maxContentWidth: Int?
+ /// The minimum content height of @scrolled_window.
+ var minContentHeight: Int?
+ /// The minimum content width of @scrolled_window.
+ var minContentWidth: Int?
+ /// Whether overlay scrolling is enabled or not.
+ ///
+ /// If it is, the scrollbars are only added as traditional widgets
+ /// when a mouse is present. Otherwise, they are overlaid on top of
+ /// the content, as narrow indicators.
+ ///
+ /// Note that overlay scrolling can also be globally disabled, with
+ /// the [property@Gtk.Settings:gtk-overlay-scrolling] setting.
+ var overlayScrolling: Bool?
+ /// Whether the natural height of the child should be calculated and propagated
+ /// through the scrolled window’s requested natural height.
+ ///
+ /// This is useful in cases where an attempt should be made to allocate exactly
+ /// enough space for the natural size of the child.
+ var propagateNaturalHeight: Bool?
+ /// Whether the natural width of the child should be calculated and propagated
+ /// through the scrolled window’s requested natural width.
+ ///
+ /// This is useful in cases where an attempt should be made to allocate exactly
+ /// enough space for the natural size of the child.
+ var propagateNaturalWidth: Bool?
+ /// Emitted whenever user initiated scrolling makes the scrolled
+ /// window firmly surpass the limits defined by the adjustment
+ /// in that orientation.
+ ///
+ /// A similar behavior without edge resistance is provided by the
+ /// [signal@Gtk.ScrolledWindow::edge-reached] signal.
+ ///
+ /// Note: The @pos argument is LTR/RTL aware, so callers should be
+ /// aware too if intending to provide behavior on horizontal edges.
+ var edgeOvershot: (() -> Void)?
+ /// Emitted whenever user-initiated scrolling makes the scrolled
+ /// window exactly reach the lower or upper limits defined by the
+ /// adjustment in that orientation.
+ ///
+ /// A similar behavior with edge resistance is provided by the
+ /// [signal@Gtk.ScrolledWindow::edge-overshot] signal.
+ ///
+ /// Note: The @pos argument is LTR/RTL aware, so callers should be
+ /// aware too if intending to provide behavior on horizontal edges.
+ var edgeReached: (() -> Void)?
+ /// Emitted when focus is moved away from the scrolled window by a
+ /// keybinding.
+ ///
+ /// This is a [keybinding signal](class.SignalAction.html).
+ ///
+ /// The default bindings for this signal are
+ /// `Ctrl + Tab` to move forward and `Ctrl + Shift + Tab` to
+ /// move backward.
+ var moveFocusOut: (() -> Void)?
+ /// Emitted when a keybinding that scrolls is pressed.
+ ///
+ /// This is a [keybinding signal](class.SignalAction.html).
+ ///
+ /// The horizontal or vertical adjustment is updated which triggers a
+ /// signal that the scrolled window’s child may listen to and scroll itself.
+ var scrollChild: (() -> Void)?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `ScrolledWindow`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(gtk_scrolled_window_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["child"] = [childStorage]
+ gtk_scrolled_window_set_child(storage.pointer, childStorage.pointer?.cast())
+ }
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let edgeOvershot {
+ storage.connectSignal(name: "edge-overshot") {
+ edgeOvershot()
+ }
+ }
+ if let edgeReached {
+ storage.connectSignal(name: "edge-reached") {
+ edgeReached()
+ }
+ }
+ if let moveFocusOut {
+ storage.connectSignal(name: "move-focus-out") {
+ moveFocusOut()
+ }
+ }
+ if let scrollChild {
+ storage.connectSignal(name: "scroll-child") {
+ scrollChild()
+ }
+ }
+ storage.modify { widget in
+ if let widget = storage.content["child"]?.first {
+ child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let hasFrame {
+ gtk_scrolled_window_set_has_frame(widget, hasFrame.cBool)
+ }
+ if let kineticScrolling {
+ gtk_scrolled_window_set_kinetic_scrolling(widget, kineticScrolling.cBool)
+ }
+ if let maxContentHeight {
+ gtk_scrolled_window_set_max_content_height(widget, maxContentHeight.cInt)
+ }
+ if let maxContentWidth {
+ gtk_scrolled_window_set_max_content_width(widget, maxContentWidth.cInt)
+ }
+ if let minContentHeight {
+ gtk_scrolled_window_set_min_content_height(widget, minContentHeight.cInt)
+ }
+ if let minContentWidth {
+ gtk_scrolled_window_set_min_content_width(widget, minContentWidth.cInt)
+ }
+ if let overlayScrolling {
+ gtk_scrolled_window_set_overlay_scrolling(widget, overlayScrolling.cBool)
+ }
+ if let propagateNaturalHeight {
+ gtk_scrolled_window_set_propagate_natural_height(widget, propagateNaturalHeight.cBool)
+ }
+ if let propagateNaturalWidth {
+ gtk_scrolled_window_set_propagate_natural_width(widget, propagateNaturalWidth.cBool)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The child widget.
+ public func child(@ViewBuilder _ child: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.child = child
+
+ return newSelf
+ }
+
+ /// Whether to draw a frame around the contents.
+ public func hasFrame(_ hasFrame: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.hasFrame = hasFrame
+
+ return newSelf
+ }
+
+ /// Whether kinetic scrolling is enabled or not.
+ ///
+ /// Kinetic scrolling only applies to devices with source %GDK_SOURCE_TOUCHSCREEN.
+ public func kineticScrolling(_ kineticScrolling: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.kineticScrolling = kineticScrolling
+
+ return newSelf
+ }
+
+ /// The maximum content height of @scrolled_window.
+ public func maxContentHeight(_ maxContentHeight: Int?) -> Self {
+ var newSelf = self
+ newSelf.maxContentHeight = maxContentHeight
+
+ return newSelf
+ }
+
+ /// The maximum content width of @scrolled_window.
+ public func maxContentWidth(_ maxContentWidth: Int?) -> Self {
+ var newSelf = self
+ newSelf.maxContentWidth = maxContentWidth
+
+ return newSelf
+ }
+
+ /// The minimum content height of @scrolled_window.
+ public func minContentHeight(_ minContentHeight: Int?) -> Self {
+ var newSelf = self
+ newSelf.minContentHeight = minContentHeight
+
+ return newSelf
+ }
+
+ /// The minimum content width of @scrolled_window.
+ public func minContentWidth(_ minContentWidth: Int?) -> Self {
+ var newSelf = self
+ newSelf.minContentWidth = minContentWidth
+
+ return newSelf
+ }
+
+ /// Whether overlay scrolling is enabled or not.
+ ///
+ /// If it is, the scrollbars are only added as traditional widgets
+ /// when a mouse is present. Otherwise, they are overlaid on top of
+ /// the content, as narrow indicators.
+ ///
+ /// Note that overlay scrolling can also be globally disabled, with
+ /// the [property@Gtk.Settings:gtk-overlay-scrolling] setting.
+ public func overlayScrolling(_ overlayScrolling: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.overlayScrolling = overlayScrolling
+
+ return newSelf
+ }
+
+ /// Whether the natural height of the child should be calculated and propagated
+ /// through the scrolled window’s requested natural height.
+ ///
+ /// This is useful in cases where an attempt should be made to allocate exactly
+ /// enough space for the natural size of the child.
+ public func propagateNaturalHeight(_ propagateNaturalHeight: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.propagateNaturalHeight = propagateNaturalHeight
+
+ return newSelf
+ }
+
+ /// Whether the natural width of the child should be calculated and propagated
+ /// through the scrolled window’s requested natural width.
+ ///
+ /// This is useful in cases where an attempt should be made to allocate exactly
+ /// enough space for the natural size of the child.
+ public func propagateNaturalWidth(_ propagateNaturalWidth: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.propagateNaturalWidth = propagateNaturalWidth
+
+ return newSelf
+ }
+
+ /// Emitted whenever user initiated scrolling makes the scrolled
+ /// window firmly surpass the limits defined by the adjustment
+ /// in that orientation.
+ ///
+ /// A similar behavior without edge resistance is provided by the
+ /// [signal@Gtk.ScrolledWindow::edge-reached] signal.
+ ///
+ /// Note: The @pos argument is LTR/RTL aware, so callers should be
+ /// aware too if intending to provide behavior on horizontal edges.
+ public func edgeOvershot(_ edgeOvershot: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.edgeOvershot = edgeOvershot
+ return newSelf
+ }
+
+ /// Emitted whenever user-initiated scrolling makes the scrolled
+ /// window exactly reach the lower or upper limits defined by the
+ /// adjustment in that orientation.
+ ///
+ /// A similar behavior with edge resistance is provided by the
+ /// [signal@Gtk.ScrolledWindow::edge-overshot] signal.
+ ///
+ /// Note: The @pos argument is LTR/RTL aware, so callers should be
+ /// aware too if intending to provide behavior on horizontal edges.
+ public func edgeReached(_ edgeReached: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.edgeReached = edgeReached
+ return newSelf
+ }
+
+ /// Emitted when focus is moved away from the scrolled window by a
+ /// keybinding.
+ ///
+ /// This is a [keybinding signal](class.SignalAction.html).
+ ///
+ /// The default bindings for this signal are
+ /// `Ctrl + Tab` to move forward and `Ctrl + Shift + Tab` to
+ /// move backward.
+ public func moveFocusOut(_ moveFocusOut: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.moveFocusOut = moveFocusOut
+ return newSelf
+ }
+
+ /// Emitted when a keybinding that scrolls is pressed.
+ ///
+ /// This is a [keybinding signal](class.SignalAction.html).
+ ///
+ /// The horizontal or vertical adjustment is updated which triggers a
+ /// signal that the scrolled window’s child may listen to and scroll itself.
+ public func scrollChild(_ scrollChild: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.scrollChild = scrollChild
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/SpinRow.swift b/Sources/Adwaita/View/Generated/SpinRow.swift
new file mode 100644
index 0000000..f12db34
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/SpinRow.swift
@@ -0,0 +1,432 @@
+//
+// SpinRow.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// An [class@ActionRow] with an embedded spin button.
+///
+///
+///
+/// Example of an `AdwSpinRow` UI definition:
+///
+/// ```xml
+/// Spin Row010050101
+/// ```
+///
+/// See [class@Gtk.SpinButton] for details.
+///
+/// ## CSS nodes
+///
+/// `AdwSpinRow` has the same structure as [class@ActionRow], as well as the
+/// `.spin` style class on the main node.
+public struct SpinRow: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The acceleration rate when you hold down a button or key.
+ var climbRate: Double
+ /// The number of decimal places to display.
+ var digits: UInt
+ /// Whether non-numeric characters should be ignored.
+ var numeric: Bool?
+ /// Whether invalid values are snapped to the nearest step increment.
+ var snapToTicks: Bool?
+ /// The current value.
+ var value: Binding?
+ /// Whether the spin row should wrap upon reaching its limits.
+ var wrap: Bool?
+ /// The widget to activate when the row is activated.
+ ///
+ /// The row can be activated either by clicking on it, calling
+ /// [method@ActionRow.activate], or via mnemonics in the title.
+ /// See the [property@PreferencesRow:use-underline] property to enable
+ /// mnemonics.
+ ///
+ /// The target widget will be activated by emitting the
+ /// [signal@Gtk.Widget::mnemonic-activate] signal on it.
+ var activatableWidget: (() -> Body)?
+ /// The icon name for this row.
+ var iconName: String?
+ /// The subtitle for this row.
+ ///
+ /// The subtitle is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ var subtitle: String?
+ /// The number of lines at the end of which the subtitle label will be
+ /// ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ var subtitleLines: Int?
+ /// Whether the user can copy the subtitle from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ var subtitleSelectable: Bool?
+ /// The number of lines at the end of which the title label will be ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ var titleLines: Int?
+ /// The title of the preference represented by this row.
+ ///
+ /// The title is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ var title: String?
+ /// Whether the user can copy the title from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ var titleSelectable: Bool?
+ /// Whether to use Pango markup for the title label.
+ ///
+ /// Subclasses may also use it for other labels, such as subtitle.
+ ///
+ /// See also [func@Pango.parse_markup].
+ var useMarkup: Bool?
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ var useUnderline: Bool?
+ /// Emitted to convert the user's input into a double value.
+ ///
+ /// The signal handler is expected to use [method@Gtk.Editable.get_text] to
+ /// retrieve the text of the spinbutton and set new_value to the new value.
+ ///
+ /// The default conversion uses [func@GLib.strtod].
+ ///
+ /// See [signal@Gtk.SpinButton::input].
+ var input: (() -> Void)?
+ /// Emitted to tweak the formatting of the value for display.
+ ///
+ /// See [signal@Gtk.SpinButton::output].
+ var output: (() -> Void)?
+ /// Emitted right after the spinbutton wraps.
+ ///
+ /// See [signal@Gtk.SpinButton::wrapped].
+ var wrapped: (() -> Void)?
+ /// This signal is emitted after the row has been activated.
+ var activated: (() -> Void)?
+ /// The body for the widget "suffix".
+ var suffix: () -> Body = { [] }
+ /// The body for the widget "prefix".
+ var prefix: () -> Body = { [] }
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `SpinRow`.
+ public init(climbRate: Double, digits: UInt) {
+ self.climbRate = climbRate
+ self.digits = digits
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_spin_row_new(nil, climbRate, digits.cInt)?.opaque())
+ update(storage, modifiers: modifiers)
+ if let activatableWidgetStorage = activatableWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["activatableWidget"] = [activatableWidgetStorage]
+ adw_action_row_set_activatable_widget(storage.pointer?.cast(), activatableWidgetStorage.pointer?.cast())
+ }
+
+ var suffixStorage: [ViewStorage] = []
+ for view in suffix() {
+ suffixStorage.append(view.storage(modifiers: modifiers))
+ adw_action_row_add_suffix(storage.pointer?.cast(), suffixStorage.last?.pointer?.cast())
+ }
+ storage.content["suffix"] = suffixStorage
+ var prefixStorage: [ViewStorage] = []
+ for view in prefix() {
+ prefixStorage.append(view.storage(modifiers: modifiers))
+ adw_action_row_add_prefix(storage.pointer?.cast(), prefixStorage.last?.pointer?.cast())
+ }
+ storage.content["prefix"] = prefixStorage
+
+ storage.notify(name: "value") {
+ value?.wrappedValue = adw_spin_row_get_value(storage.pointer)
+ }
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let input {
+ storage.connectSignal(name: "input") {
+ input()
+ }
+ }
+ if let output {
+ storage.connectSignal(name: "output") {
+ output()
+ }
+ }
+ if let wrapped {
+ storage.connectSignal(name: "wrapped") {
+ wrapped()
+ }
+ }
+ if let activated {
+ storage.connectSignal(name: "activated") {
+ activated()
+ }
+ }
+ storage.modify { widget in
+ adw_spin_row_set_climb_rate(widget, climbRate)
+ adw_spin_row_set_digits(widget, digits.cInt)
+ if let numeric {
+ adw_spin_row_set_numeric(widget, numeric.cBool)
+ }
+ if let snapToTicks {
+ adw_spin_row_set_snap_to_ticks(widget, snapToTicks.cBool)
+ }
+ if let value {
+ adw_spin_row_set_value(widget, value.wrappedValue)
+ }
+ if let wrap {
+ adw_spin_row_set_wrap(widget, wrap.cBool)
+ }
+ if let widget = storage.content["activatableWidget"]?.first {
+ activatableWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let iconName {
+ adw_action_row_set_icon_name(widget?.cast(), iconName)
+ }
+ if let subtitle {
+ adw_action_row_set_subtitle(widget?.cast(), subtitle)
+ }
+ if let subtitleLines {
+ adw_action_row_set_subtitle_lines(widget?.cast(), subtitleLines.cInt)
+ }
+ if let subtitleSelectable {
+ adw_action_row_set_subtitle_selectable(widget?.cast(), subtitleSelectable.cBool)
+ }
+ if let titleLines {
+ adw_action_row_set_title_lines(widget?.cast(), titleLines.cInt)
+ }
+ if let title {
+ adw_preferences_row_set_title(widget?.cast(), title)
+ }
+ if let titleSelectable {
+ adw_preferences_row_set_title_selectable(widget?.cast(), titleSelectable.cBool)
+ }
+ if let useMarkup {
+ adw_preferences_row_set_use_markup(widget?.cast(), useMarkup.cBool)
+ }
+ if let useUnderline {
+ adw_preferences_row_set_use_underline(widget?.cast(), useUnderline.cBool)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The acceleration rate when you hold down a button or key.
+ public func climbRate(_ climbRate: Double) -> Self {
+ var newSelf = self
+ newSelf.climbRate = climbRate
+
+ return newSelf
+ }
+
+ /// The number of decimal places to display.
+ public func digits(_ digits: UInt) -> Self {
+ var newSelf = self
+ newSelf.digits = digits
+
+ return newSelf
+ }
+
+ /// Whether non-numeric characters should be ignored.
+ public func numeric(_ numeric: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.numeric = numeric
+
+ return newSelf
+ }
+
+ /// Whether invalid values are snapped to the nearest step increment.
+ public func snapToTicks(_ snapToTicks: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.snapToTicks = snapToTicks
+
+ return newSelf
+ }
+
+ /// The current value.
+ public func value(_ value: Binding?) -> Self {
+ var newSelf = self
+ newSelf.value = value
+
+ return newSelf
+ }
+
+ /// Whether the spin row should wrap upon reaching its limits.
+ public func wrap(_ wrap: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.wrap = wrap
+
+ return newSelf
+ }
+
+ /// The widget to activate when the row is activated.
+ ///
+ /// The row can be activated either by clicking on it, calling
+ /// [method@ActionRow.activate], or via mnemonics in the title.
+ /// See the [property@PreferencesRow:use-underline] property to enable
+ /// mnemonics.
+ ///
+ /// The target widget will be activated by emitting the
+ /// [signal@Gtk.Widget::mnemonic-activate] signal on it.
+ public func activatableWidget(@ViewBuilder _ activatableWidget: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.activatableWidget = activatableWidget
+
+ return newSelf
+ }
+
+ /// The icon name for this row.
+ public func iconName(_ iconName: String?) -> Self {
+ var newSelf = self
+ newSelf.iconName = iconName
+
+ return newSelf
+ }
+
+ /// The subtitle for this row.
+ ///
+ /// The subtitle is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ public func subtitle(_ subtitle: String?) -> Self {
+ var newSelf = self
+ newSelf.subtitle = subtitle
+
+ return newSelf
+ }
+
+ /// The number of lines at the end of which the subtitle label will be
+ /// ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ public func subtitleLines(_ subtitleLines: Int?) -> Self {
+ var newSelf = self
+ newSelf.subtitleLines = subtitleLines
+
+ return newSelf
+ }
+
+ /// Whether the user can copy the subtitle from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ public func subtitleSelectable(_ subtitleSelectable: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.subtitleSelectable = subtitleSelectable
+
+ return newSelf
+ }
+
+ /// The number of lines at the end of which the title label will be ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ public func titleLines(_ titleLines: Int?) -> Self {
+ var newSelf = self
+ newSelf.titleLines = titleLines
+
+ return newSelf
+ }
+
+ /// The title of the preference represented by this row.
+ ///
+ /// The title is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ public func title(_ title: String?) -> Self {
+ var newSelf = self
+ newSelf.title = title
+
+ return newSelf
+ }
+
+ /// Whether the user can copy the title from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ public func titleSelectable(_ titleSelectable: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.titleSelectable = titleSelectable
+
+ return newSelf
+ }
+
+ /// Whether to use Pango markup for the title label.
+ ///
+ /// Subclasses may also use it for other labels, such as subtitle.
+ ///
+ /// See also [func@Pango.parse_markup].
+ public func useMarkup(_ useMarkup: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useMarkup = useMarkup
+
+ return newSelf
+ }
+
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+ /// Emitted to convert the user's input into a double value.
+ ///
+ /// The signal handler is expected to use [method@Gtk.Editable.get_text] to
+ /// retrieve the text of the spinbutton and set new_value to the new value.
+ ///
+ /// The default conversion uses [func@GLib.strtod].
+ ///
+ /// See [signal@Gtk.SpinButton::input].
+ public func input(_ input: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.input = input
+ return newSelf
+ }
+
+ /// Emitted to tweak the formatting of the value for display.
+ ///
+ /// See [signal@Gtk.SpinButton::output].
+ public func output(_ output: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.output = output
+ return newSelf
+ }
+
+ /// Emitted right after the spinbutton wraps.
+ ///
+ /// See [signal@Gtk.SpinButton::wrapped].
+ public func wrapped(_ wrapped: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.wrapped = wrapped
+ return newSelf
+ }
+
+ /// This signal is emitted after the row has been activated.
+ public func activated(_ activated: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.activated = activated
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/Spinner.swift b/Sources/Adwaita/View/Generated/Spinner.swift
new file mode 100644
index 0000000..5db7f8c
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/Spinner.swift
@@ -0,0 +1,83 @@
+//
+// Spinner.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A `GtkSpinner` widget displays an icon-size spinning animation.
+///
+/// It is often used as an alternative to a [class@Gtk.ProgressBar]
+/// for displaying indefinite activity, instead of actual progress.
+///
+/// ![An example GtkSpinner](spinner.png)
+///
+/// To start the animation, use [method@Gtk.Spinner.start], to stop it
+/// use [method@Gtk.Spinner.stop].
+///
+/// # CSS nodes
+///
+/// `GtkSpinner` has a single CSS node with the name spinner.
+/// When the animation is active, the :checked pseudoclass is
+/// added to this node.
+public struct Spinner: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// Whether the spinner is spinning
+ var spinning: Bool?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `Spinner`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(gtk_spinner_new()?.opaque())
+ update(storage, modifiers: modifiers)
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let spinning {
+ gtk_spinner_set_spinning(widget, spinning.cBool)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// Whether the spinner is spinning
+ public func spinning(_ spinning: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.spinning = spinning
+
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/SplitButton.swift b/Sources/Adwaita/View/Generated/SplitButton.swift
new file mode 100644
index 0000000..71228e7
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/SplitButton.swift
@@ -0,0 +1,274 @@
+//
+// SplitButton.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A combined button and dropdown widget.
+///
+///
+///
+/// `AdwSplitButton` is typically used to present a set of actions in a menu,
+/// but allow access to one of them with a single click.
+///
+/// The API is very similar to [class@Gtk.Button] and [class@Gtk.MenuButton], see
+/// their documentation for details.
+///
+/// ## CSS nodes
+///
+/// ```
+/// splitbutton[.image-button][.text-button]
+/// ├── button
+/// │ ╰── ├── separator
+/// ╰── menubutton
+/// ╰── button.toggle
+/// ╰── arrow
+/// ```
+///
+/// `AdwSplitButton`'s CSS node is called `splitbutton`. It contains the css
+/// nodes: `button`, `separator`, `menubutton`. See [class@Gtk.MenuButton]
+/// documentation for the `menubutton` contents.
+///
+/// The main CSS node will contain the `.image-button` or `.text-button` style
+/// classes matching the button contents. The nested button nodes will never
+/// contain them.
+///
+/// ## Accessibility
+///
+/// `AdwSplitButton` uses the `GTK_ACCESSIBLE_ROLE_GROUP` role.
+public struct SplitButton: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// Whether the button can be smaller than the natural size of its contents.
+ ///
+ /// If set to `TRUE`, the label will ellipsize.
+ ///
+ /// See [property@Gtk.Button:can-shrink] and
+ /// [property@Gtk.MenuButton:can-shrink].
+ var canShrink: Bool?
+ /// The child widget.
+ ///
+ /// Setting the child widget will set [property@SplitButton:label] and
+ /// [property@SplitButton:icon-name] to `NULL`.
+ var child: (() -> Body)?
+ /// The tooltip of the dropdown button.
+ ///
+ /// The tooltip can be marked up with the Pango text markup language.
+ var dropdownTooltip: String?
+ /// The name of the icon used to automatically populate the button.
+ ///
+ /// Setting the icon name will set [property@SplitButton:label] and
+ /// [property@SplitButton:child] to `NULL`.
+ var iconName: String?
+ /// The label for the button.
+ ///
+ /// Setting the label will set [property@SplitButton:icon-name] and
+ /// [property@SplitButton:child] to `NULL`.
+ var label: String?
+ /// The `GMenuModel` from which the popup will be created.
+ ///
+ /// If the menu model is `NULL`, the dropdown is disabled.
+ ///
+ /// A [class@Gtk.Popover] will be created from the menu model with
+ /// [ctor@Gtk.PopoverMenu.new_from_model]. Actions will be connected as
+ /// documented for this function.
+ ///
+ /// If [property@SplitButton:popover] is already set, it will be dissociated
+ /// from the button, and the property is set to `NULL`.
+ var menuModel: (() -> MenuContent)?
+ /// Whether an underline in the text indicates a mnemonic.
+ ///
+ /// See [property@SplitButton:label].
+ var useUnderline: Bool?
+ /// Emitted to animate press then release.
+ ///
+ /// This is an action signal. Applications should never connect to this signal,
+ /// but use the [signal@SplitButton::clicked] signal.
+ var activate: (() -> Void)?
+ /// Emitted when the button has been activated (pressed and released).
+ var clicked: (() -> Void)?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `SplitButton`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_split_button_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["child"] = [childStorage]
+ adw_split_button_set_child(storage.pointer, childStorage.pointer?.cast())
+ }
+ if let declarative = menuModel?(), let app {
+ let menu = g_menu_new()
+ adw_split_button_set_menu_model(storage.pointer, menu?.cast())
+ for item in declarative {
+ item.addMenuItems(menu: menu, app: app, window: window)
+ }
+ }
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let activate {
+ storage.connectSignal(name: "activate") {
+ activate()
+ }
+ }
+ if let clicked {
+ storage.connectSignal(name: "clicked") {
+ clicked()
+ }
+ }
+ storage.modify { widget in
+ if let canShrink {
+ adw_split_button_set_can_shrink(widget, canShrink.cBool)
+ }
+ if let widget = storage.content["child"]?.first {
+ child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let dropdownTooltip {
+ adw_split_button_set_dropdown_tooltip(widget, dropdownTooltip)
+ }
+ if let iconName {
+ adw_split_button_set_icon_name(widget, iconName)
+ }
+ if let label, storage.content["child"] == nil {
+ adw_split_button_set_label(widget, label)
+ }
+ if let useUnderline {
+ adw_split_button_set_use_underline(widget, useUnderline.cBool)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// Whether the button can be smaller than the natural size of its contents.
+ ///
+ /// If set to `TRUE`, the label will ellipsize.
+ ///
+ /// See [property@Gtk.Button:can-shrink] and
+ /// [property@Gtk.MenuButton:can-shrink].
+ public func canShrink(_ canShrink: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.canShrink = canShrink
+
+ return newSelf
+ }
+
+ /// The child widget.
+ ///
+ /// Setting the child widget will set [property@SplitButton:label] and
+ /// [property@SplitButton:icon-name] to `NULL`.
+ public func child(@ViewBuilder _ child: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.child = child
+
+ return newSelf
+ }
+
+ /// The tooltip of the dropdown button.
+ ///
+ /// The tooltip can be marked up with the Pango text markup language.
+ public func dropdownTooltip(_ dropdownTooltip: String?) -> Self {
+ var newSelf = self
+ newSelf.dropdownTooltip = dropdownTooltip
+
+ return newSelf
+ }
+
+ /// The name of the icon used to automatically populate the button.
+ ///
+ /// Setting the icon name will set [property@SplitButton:label] and
+ /// [property@SplitButton:child] to `NULL`.
+ public func iconName(_ iconName: String?) -> Self {
+ var newSelf = self
+ newSelf.iconName = iconName
+
+ return newSelf
+ }
+
+ /// The label for the button.
+ ///
+ /// Setting the label will set [property@SplitButton:icon-name] and
+ /// [property@SplitButton:child] to `NULL`.
+ public func label(_ label: String?) -> Self {
+ var newSelf = self
+ newSelf.label = label
+
+ return newSelf
+ }
+
+ /// The `GMenuModel` from which the popup will be created.
+ ///
+ /// If the menu model is `NULL`, the dropdown is disabled.
+ ///
+ /// A [class@Gtk.Popover] will be created from the menu model with
+ /// [ctor@Gtk.PopoverMenu.new_from_model]. Actions will be connected as
+ /// documented for this function.
+ ///
+ /// If [property@SplitButton:popover] is already set, it will be dissociated
+ /// from the button, and the property is set to `NULL`.
+ public func menuModel(app: GTUIApp, window: GTUIApplicationWindow? = nil, @MenuBuilder _ menuModel: @escaping (() -> MenuContent)) -> Self {
+ var newSelf = self
+ newSelf.menuModel = menuModel
+ newSelf.app = app; newSelf.window = window
+ return newSelf
+ }
+
+ /// Whether an underline in the text indicates a mnemonic.
+ ///
+ /// See [property@SplitButton:label].
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+ /// Emitted to animate press then release.
+ ///
+ /// This is an action signal. Applications should never connect to this signal,
+ /// but use the [signal@SplitButton::clicked] signal.
+ public func activate(_ activate: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.activate = activate
+ return newSelf
+ }
+
+ /// Emitted when the button has been activated (pressed and released).
+ public func clicked(_ clicked: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.clicked = clicked
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/StatusPage.swift b/Sources/Adwaita/View/Generated/StatusPage.swift
new file mode 100644
index 0000000..2c15b34
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/StatusPage.swift
@@ -0,0 +1,133 @@
+//
+// StatusPage.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A page used for empty/error states and similar use-cases.
+///
+///
+///
+/// The `AdwStatusPage` widget can have an icon, a title, a description and a
+/// custom widget which is displayed below them.
+///
+/// ## CSS nodes
+///
+/// `AdwStatusPage` has a main CSS node with name `statuspage`.
+///
+/// `AdwStatusPage` can use the
+/// [`.compact`](style-classes.html#compact-status-page) style class for when it
+/// needs to fit into a small space such a sidebar or a popover.
+public struct StatusPage: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The child widget.
+ var child: (() -> Body)?
+ /// The description markup to be displayed below the title.
+ var description: String?
+ /// The name of the icon to be used.
+ ///
+ /// Changing this will set [property@StatusPage:paintable] to `NULL`.
+ var iconName: String?
+ /// The title to be displayed below the icon.
+ ///
+ /// It is not parsed as Pango markup.
+ var title: String?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `StatusPage`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_status_page_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["child"] = [childStorage]
+ adw_status_page_set_child(storage.pointer, childStorage.pointer?.cast())
+ }
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let widget = storage.content["child"]?.first {
+ child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let description {
+ adw_status_page_set_description(widget, description)
+ }
+ if let iconName {
+ adw_status_page_set_icon_name(widget, iconName)
+ }
+ if let title {
+ adw_status_page_set_title(widget, title)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The child widget.
+ public func child(@ViewBuilder _ child: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.child = child
+
+ return newSelf
+ }
+
+ /// The description markup to be displayed below the title.
+ public func description(_ description: String?) -> Self {
+ var newSelf = self
+ newSelf.description = description
+
+ return newSelf
+ }
+
+ /// The name of the icon to be used.
+ ///
+ /// Changing this will set [property@StatusPage:paintable] to `NULL`.
+ public func iconName(_ iconName: String?) -> Self {
+ var newSelf = self
+ newSelf.iconName = iconName
+
+ return newSelf
+ }
+
+ /// The title to be displayed below the icon.
+ ///
+ /// It is not parsed as Pango markup.
+ public func title(_ title: String?) -> Self {
+ var newSelf = self
+ newSelf.title = title
+
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/SwitchRow.swift b/Sources/Adwaita/View/Generated/SwitchRow.swift
new file mode 100644
index 0000000..e5f5a7f
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/SwitchRow.swift
@@ -0,0 +1,309 @@
+//
+// SwitchRow.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A [class@Gtk.ListBoxRow] used to represent two states.
+///
+///
+///
+/// The `AdwSwitchRow` widget contains a [class@Gtk.Switch] that allows the user
+/// to select between two states: "on" or "off". When activated, the row will
+/// invert its active state.
+///
+/// The user can control the switch by activating the row or by dragging on the
+/// switch handle.
+///
+/// See [class@Gtk.Switch] for details.
+///
+/// Example of an `AdwSwitchRow` UI definition:
+/// ```xml
+/// Switch Row
+/// ```
+///
+/// The [property@SwitchRow:active] property should be connected to in order to
+/// monitor changes to the active state.
+public struct SwitchRow: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// Whether the switch row is in the "on" or "off" position.
+ var active: Binding?
+ /// The widget to activate when the row is activated.
+ ///
+ /// The row can be activated either by clicking on it, calling
+ /// [method@ActionRow.activate], or via mnemonics in the title.
+ /// See the [property@PreferencesRow:use-underline] property to enable
+ /// mnemonics.
+ ///
+ /// The target widget will be activated by emitting the
+ /// [signal@Gtk.Widget::mnemonic-activate] signal on it.
+ var activatableWidget: (() -> Body)?
+ /// The icon name for this row.
+ var iconName: String?
+ /// The subtitle for this row.
+ ///
+ /// The subtitle is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ var subtitle: String?
+ /// The number of lines at the end of which the subtitle label will be
+ /// ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ var subtitleLines: Int?
+ /// Whether the user can copy the subtitle from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ var subtitleSelectable: Bool?
+ /// The number of lines at the end of which the title label will be ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ var titleLines: Int?
+ /// The title of the preference represented by this row.
+ ///
+ /// The title is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ var title: String?
+ /// Whether the user can copy the title from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ var titleSelectable: Bool?
+ /// Whether to use Pango markup for the title label.
+ ///
+ /// Subclasses may also use it for other labels, such as subtitle.
+ ///
+ /// See also [func@Pango.parse_markup].
+ var useMarkup: Bool?
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ var useUnderline: Bool?
+ /// This signal is emitted after the row has been activated.
+ var activated: (() -> Void)?
+ /// The body for the widget "suffix".
+ var suffix: () -> Body = { [] }
+ /// The body for the widget "prefix".
+ var prefix: () -> Body = { [] }
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `SwitchRow`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_switch_row_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let activatableWidgetStorage = activatableWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["activatableWidget"] = [activatableWidgetStorage]
+ adw_action_row_set_activatable_widget(storage.pointer?.cast(), activatableWidgetStorage.pointer?.cast())
+ }
+
+ var suffixStorage: [ViewStorage] = []
+ for view in suffix() {
+ suffixStorage.append(view.storage(modifiers: modifiers))
+ adw_action_row_add_suffix(storage.pointer?.cast(), suffixStorage.last?.pointer?.cast())
+ }
+ storage.content["suffix"] = suffixStorage
+ var prefixStorage: [ViewStorage] = []
+ for view in prefix() {
+ prefixStorage.append(view.storage(modifiers: modifiers))
+ adw_action_row_add_prefix(storage.pointer?.cast(), prefixStorage.last?.pointer?.cast())
+ }
+ storage.content["prefix"] = prefixStorage
+
+ storage.notify(name: "active") {
+ active?.wrappedValue = adw_switch_row_get_active(storage.pointer) != 0
+ }
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let activated {
+ storage.connectSignal(name: "activated") {
+ activated()
+ }
+ }
+ storage.modify { widget in
+ if let active {
+ adw_switch_row_set_active(widget, active.wrappedValue.cBool)
+ }
+ if let widget = storage.content["activatableWidget"]?.first {
+ activatableWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let iconName {
+ adw_action_row_set_icon_name(widget?.cast(), iconName)
+ }
+ if let subtitle {
+ adw_action_row_set_subtitle(widget?.cast(), subtitle)
+ }
+ if let subtitleLines {
+ adw_action_row_set_subtitle_lines(widget?.cast(), subtitleLines.cInt)
+ }
+ if let subtitleSelectable {
+ adw_action_row_set_subtitle_selectable(widget?.cast(), subtitleSelectable.cBool)
+ }
+ if let titleLines {
+ adw_action_row_set_title_lines(widget?.cast(), titleLines.cInt)
+ }
+ if let title {
+ adw_preferences_row_set_title(widget?.cast(), title)
+ }
+ if let titleSelectable {
+ adw_preferences_row_set_title_selectable(widget?.cast(), titleSelectable.cBool)
+ }
+ if let useMarkup {
+ adw_preferences_row_set_use_markup(widget?.cast(), useMarkup.cBool)
+ }
+ if let useUnderline {
+ adw_preferences_row_set_use_underline(widget?.cast(), useUnderline.cBool)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// Whether the switch row is in the "on" or "off" position.
+ public func active(_ active: Binding?) -> Self {
+ var newSelf = self
+ newSelf.active = active
+
+ return newSelf
+ }
+
+ /// The widget to activate when the row is activated.
+ ///
+ /// The row can be activated either by clicking on it, calling
+ /// [method@ActionRow.activate], or via mnemonics in the title.
+ /// See the [property@PreferencesRow:use-underline] property to enable
+ /// mnemonics.
+ ///
+ /// The target widget will be activated by emitting the
+ /// [signal@Gtk.Widget::mnemonic-activate] signal on it.
+ public func activatableWidget(@ViewBuilder _ activatableWidget: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.activatableWidget = activatableWidget
+
+ return newSelf
+ }
+
+ /// The icon name for this row.
+ public func iconName(_ iconName: String?) -> Self {
+ var newSelf = self
+ newSelf.iconName = iconName
+
+ return newSelf
+ }
+
+ /// The subtitle for this row.
+ ///
+ /// The subtitle is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ public func subtitle(_ subtitle: String?) -> Self {
+ var newSelf = self
+ newSelf.subtitle = subtitle
+
+ return newSelf
+ }
+
+ /// The number of lines at the end of which the subtitle label will be
+ /// ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ public func subtitleLines(_ subtitleLines: Int?) -> Self {
+ var newSelf = self
+ newSelf.subtitleLines = subtitleLines
+
+ return newSelf
+ }
+
+ /// Whether the user can copy the subtitle from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ public func subtitleSelectable(_ subtitleSelectable: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.subtitleSelectable = subtitleSelectable
+
+ return newSelf
+ }
+
+ /// The number of lines at the end of which the title label will be ellipsized.
+ ///
+ /// If the value is 0, the number of lines won't be limited.
+ public func titleLines(_ titleLines: Int?) -> Self {
+ var newSelf = self
+ newSelf.titleLines = titleLines
+
+ return newSelf
+ }
+
+ /// The title of the preference represented by this row.
+ ///
+ /// The title is interpreted as Pango markup unless
+ /// [property@PreferencesRow:use-markup] is set to `FALSE`.
+ public func title(_ title: String?) -> Self {
+ var newSelf = self
+ newSelf.title = title
+
+ return newSelf
+ }
+
+ /// Whether the user can copy the title from the label.
+ ///
+ /// See also [property@Gtk.Label:selectable].
+ public func titleSelectable(_ titleSelectable: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.titleSelectable = titleSelectable
+
+ return newSelf
+ }
+
+ /// Whether to use Pango markup for the title label.
+ ///
+ /// Subclasses may also use it for other labels, such as subtitle.
+ ///
+ /// See also [func@Pango.parse_markup].
+ public func useMarkup(_ useMarkup: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useMarkup = useMarkup
+
+ return newSelf
+ }
+
+ /// Whether an embedded underline in the title indicates a mnemonic.
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+ /// This signal is emitted after the row has been activated.
+ public func activated(_ activated: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.activated = activated
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/ToastOverlay.swift b/Sources/Adwaita/View/Generated/ToastOverlay.swift
new file mode 100644
index 0000000..6605666
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/ToastOverlay.swift
@@ -0,0 +1,108 @@
+//
+// ToastOverlay.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A widget showing toasts above its content.
+///
+///
+///
+/// Much like [class@Gtk.Overlay], `AdwToastOverlay` is a container with a single
+/// main child, on top of which it can display a [class@Toast], overlaid.
+/// Toasts can be shown with [method@ToastOverlay.add_toast].
+///
+/// See [class@Toast] for details.
+///
+/// ## CSS nodes
+///
+/// ```
+/// toastoverlay
+/// ├── [child]
+/// ├── toast
+/// ┊ ├── widget
+/// ┊ │ ├── [label.heading]
+/// │ ╰── [custom title]
+/// ├── [button]
+/// ╰── button.circular.flat
+/// ```
+///
+/// `AdwToastOverlay`'s CSS node is called `toastoverlay`. It contains the child,
+/// as well as zero or more `toast` subnodes.
+///
+/// Each of the `toast` nodes contains a `widget` subnode, optionally a `button`
+/// subnode, and another `button` subnode with `.circular` and `.flat` style
+/// classes.
+///
+/// The `widget` subnode contains a `label` subnode with the `.heading` style
+/// class, or a custom widget provided by the application.
+///
+/// ## Accessibility
+///
+/// `AdwToastOverlay` uses the `GTK_ACCESSIBLE_ROLE_TAB_GROUP` role.
+public struct ToastOverlay: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The child widget.
+ var child: (() -> Body)?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `ToastOverlay`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_toast_overlay_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["child"] = [childStorage]
+ adw_toast_overlay_set_child(storage.pointer, childStorage.pointer?.cast())
+ }
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let widget = storage.content["child"]?.first {
+ child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The child widget.
+ public func child(@ViewBuilder _ child: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.child = child
+
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/ToggleButton.swift b/Sources/Adwaita/View/Generated/ToggleButton.swift
new file mode 100644
index 0000000..8b9b95f
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/ToggleButton.swift
@@ -0,0 +1,296 @@
+//
+// ToggleButton.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A `GtkToggleButton` is a button which remains “pressed-in” when
+/// clicked.
+///
+/// Clicking again will cause the toggle button to return to its normal state.
+///
+/// A toggle button is created by calling either [ctor@Gtk.ToggleButton.new] or
+/// [ctor@Gtk.ToggleButton.new_with_label]. If using the former, it is advisable
+/// to pack a widget, (such as a `GtkLabel` and/or a `GtkImage`), into the toggle
+/// button’s container. (See [class@Gtk.Button] for more information).
+///
+/// The state of a `GtkToggleButton` can be set specifically using
+/// [method@Gtk.ToggleButton.set_active], and retrieved using
+/// [method@Gtk.ToggleButton.get_active].
+///
+/// To simply switch the state of a toggle button, use
+/// [method@Gtk.ToggleButton.toggled].
+///
+/// ## Grouping
+///
+/// Toggle buttons can be grouped together, to form mutually exclusive
+/// groups - only one of the buttons can be toggled at a time, and toggling
+/// another one will switch the currently toggled one off.
+///
+/// To add a `GtkToggleButton` to a group, use [method@Gtk.ToggleButton.set_group].
+///
+/// ## CSS nodes
+///
+/// `GtkToggleButton` has a single CSS node with name button. To differentiate
+/// it from a plain `GtkButton`, it gets the `.toggle` style class.
+///
+/// ## Accessibility
+///
+/// `GtkToggleButton` uses the %GTK_ACCESSIBLE_ROLE_TOGGLE_BUTTON role.
+///
+/// ## Creating two `GtkToggleButton` widgets.
+///
+/// ```c
+/// static void
+/// output_state (GtkToggleButton *source,
+/// gpointer user_data)
+/// {
+/// g_print ("Toggle button "%s" is active: %s",
+/// gtk_button_get_label (GTK_BUTTON (source)),
+/// gtk_toggle_button_get_active (source) ? "Yes" : "No");
+/// }
+///
+/// static void
+/// make_toggles (void)
+/// {
+/// GtkWidget *window, *toggle1, *toggle2;
+/// GtkWidget *box;
+/// const char *text;
+///
+/// window = gtk_window_new ();
+/// box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+///
+/// text = "Hi, I’m toggle button one";
+/// toggle1 = gtk_toggle_button_new_with_label (text);
+///
+/// g_signal_connect (toggle1, "toggled",
+/// G_CALLBACK (output_state),
+/// NULL);
+/// gtk_box_append (GTK_BOX (box), toggle1);
+///
+/// text = "Hi, I’m toggle button two";
+/// toggle2 = gtk_toggle_button_new_with_label (text);
+/// g_signal_connect (toggle2, "toggled",
+/// G_CALLBACK (output_state),
+/// NULL);
+/// gtk_box_append (GTK_BOX (box), toggle2);
+///
+/// gtk_window_set_child (GTK_WINDOW (window), box);
+/// gtk_window_present (GTK_WINDOW (window));
+/// }
+/// ```
+public struct ToggleButton: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// If the toggle button should be pressed in.
+ var active: Binding?
+ /// Whether the size of the button can be made smaller than the natural
+ /// size of its contents.
+ ///
+ /// For text buttons, setting this property will allow ellipsizing the label.
+ ///
+ /// If the contents of a button are an icon or a custom widget, setting this
+ /// property has no effect.
+ var canShrink: Bool?
+ /// The child widget.
+ var child: (() -> Body)?
+ /// Whether the button has a frame.
+ var hasFrame: Bool?
+ /// The name of the icon used to automatically populate the button.
+ var iconName: String?
+ /// Text of the label inside the button, if the button contains a label widget.
+ var label: String?
+ /// If set, an underline in the text indicates that the following character is
+ /// to be used as mnemonic.
+ var useUnderline: Bool?
+ /// Emitted whenever the `GtkToggleButton`'s state is changed.
+ var toggled: (() -> Void)?
+ /// Emitted to animate press then release.
+ ///
+ /// This is an action signal. Applications should never connect
+ /// to this signal, but use the [signal@Gtk.Button::clicked] signal.
+ ///
+ /// The default bindings for this signal are all forms of the
+ /// ␣ and Enter keys.
+ var activate: (() -> Void)?
+ /// Emitted when the button has been activated (pressed and released).
+ var clicked: (() -> Void)?
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `ToggleButton`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(gtk_toggle_button_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["child"] = [childStorage]
+ gtk_button_set_child(storage.pointer?.cast(), childStorage.pointer?.cast())
+ }
+
+
+ storage.notify(name: "active") {
+ active?.wrappedValue = gtk_toggle_button_get_active(storage.pointer?.cast()) != 0
+ }
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ if let toggled {
+ storage.connectSignal(name: "toggled") {
+ toggled()
+ }
+ }
+ if let activate {
+ storage.connectSignal(name: "activate") {
+ activate()
+ }
+ }
+ if let clicked {
+ storage.connectSignal(name: "clicked") {
+ clicked()
+ }
+ }
+ storage.modify { widget in
+ if let active {
+ gtk_toggle_button_set_active(widget?.cast(), active.wrappedValue.cBool)
+ }
+ if let canShrink {
+ gtk_button_set_can_shrink(widget?.cast(), canShrink.cBool)
+ }
+ if let widget = storage.content["child"]?.first {
+ child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let hasFrame {
+ gtk_button_set_has_frame(widget?.cast(), hasFrame.cBool)
+ }
+ if let iconName {
+ gtk_button_set_icon_name(widget?.cast(), iconName)
+ }
+ if let label, storage.content["child"] == nil {
+ gtk_button_set_label(widget?.cast(), label)
+ }
+ if let useUnderline {
+ gtk_button_set_use_underline(widget?.cast(), useUnderline.cBool)
+ }
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// If the toggle button should be pressed in.
+ public func active(_ active: Binding?) -> Self {
+ var newSelf = self
+ newSelf.active = active
+
+ return newSelf
+ }
+
+ /// Whether the size of the button can be made smaller than the natural
+ /// size of its contents.
+ ///
+ /// For text buttons, setting this property will allow ellipsizing the label.
+ ///
+ /// If the contents of a button are an icon or a custom widget, setting this
+ /// property has no effect.
+ public func canShrink(_ canShrink: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.canShrink = canShrink
+
+ return newSelf
+ }
+
+ /// The child widget.
+ public func child(@ViewBuilder _ child: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.child = child
+
+ return newSelf
+ }
+
+ /// Whether the button has a frame.
+ public func hasFrame(_ hasFrame: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.hasFrame = hasFrame
+
+ return newSelf
+ }
+
+ /// The name of the icon used to automatically populate the button.
+ public func iconName(_ iconName: String?) -> Self {
+ var newSelf = self
+ newSelf.iconName = iconName
+
+ return newSelf
+ }
+
+ /// Text of the label inside the button, if the button contains a label widget.
+ public func label(_ label: String?) -> Self {
+ var newSelf = self
+ newSelf.label = label
+
+ return newSelf
+ }
+
+ /// If set, an underline in the text indicates that the following character is
+ /// to be used as mnemonic.
+ public func useUnderline(_ useUnderline: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.useUnderline = useUnderline
+
+ return newSelf
+ }
+
+ /// Emitted whenever the `GtkToggleButton`'s state is changed.
+ public func toggled(_ toggled: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.toggled = toggled
+ return newSelf
+ }
+
+ /// Emitted to animate press then release.
+ ///
+ /// This is an action signal. Applications should never connect
+ /// to this signal, but use the [signal@Gtk.Button::clicked] signal.
+ ///
+ /// The default bindings for this signal are all forms of the
+ /// ␣ and Enter keys.
+ public func activate(_ activate: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.activate = activate
+ return newSelf
+ }
+
+ /// Emitted when the button has been activated (pressed and released).
+ public func clicked(_ clicked: @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.clicked = clicked
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/Generated/ToolbarView.swift b/Sources/Adwaita/View/Generated/ToolbarView.swift
new file mode 100644
index 0000000..41cc162
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/ToolbarView.swift
@@ -0,0 +1,332 @@
+//
+// ToolbarView.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A widget containing a page, as well as top and/or bottom bars.
+///
+///
+///
+/// `AdwToolbarView` has a single content widget and one or multiple top and
+/// bottom bars, shown at the top and bottom sides respectively.
+///
+/// Example of an `AdwToolbarView` UI definition:
+/// ```xml
+///
+/// ```
+///
+/// The following kinds of top and bottom bars are supported:
+///
+/// - [class@HeaderBar]
+/// - [class@TabBar]
+/// - [class@ViewSwitcherBar]
+/// - [class@Gtk.ActionBar]
+/// - [class@Gtk.HeaderBar]
+/// - [class@Gtk.PopoverMenuBar]
+/// - [class@Gtk.SearchBar]
+/// - Any [class@Gtk.Box] or a similar widget with the
+/// [`.toolbar`](style-classes.html#toolbars) style class
+///
+/// By default, top and bottom bars are flat and scrolling content has a subtle
+/// undershoot shadow, same as when using the
+/// [`.undershoot-top`](style-classes.html#undershot-indicators) and
+/// [`.undershoot-bottom`](style-classes.html#undershot-indicators) style
+/// classes. This works well in most cases, e.g. with [class@StatusPage] or
+/// [class@PreferencesPage], where the background at the top and bottom parts of
+/// the page is uniform. Additionally, windows with sidebars should always use
+/// this style.
+///
+/// [property@ToolbarView:top-bar-style] and
+/// [property@ToolbarView:bottom-bar-style] properties can be used add an opaque
+/// background and a persistent shadow to top and bottom bars, this can be useful
+/// for content such as [utility panes](https://developer.gnome.org/hig/patterns/containers/utility-panes.html),
+/// where some elements are adjacent to the top/bottom bars, or [class@TabView],
+/// where each page can have a different background.
+///
+///
+///
+/// `AdwToolbarView` ensures the top and bottom bars have consistent backdrop
+/// styles and vertical spacing. For comparison:
+///
+///
+///
+/// Any top and bottom bars can also be dragged to move the window, equivalent
+/// to putting them into a [class@Gtk.WindowHandle].
+///
+/// Content is typically place between top and bottom bars, but can also extend
+/// behind them. This is controlled with the
+/// [property@ToolbarView:extend-content-to-top-edge] and
+/// [property@ToolbarView:extend-content-to-bottom-edge] properties.
+///
+/// Top and bottom bars can be hidden and revealed with an animation using the
+/// [property@ToolbarView:reveal-top-bars] and
+/// [property@ToolbarView:reveal-bottom-bars] properties.
+///
+/// ## `AdwToolbarView` as `GtkBuildable`
+///
+/// The `AdwToolbarView` implementation of the [iface@Gtk.Buildable] interface
+/// supports adding a top bar by specifying “top” as the “type” attribute of a
+/// `` element, or adding a bottom bar by specifying “bottom”.
+///
+/// ## Accessibility
+///
+/// `AdwToolbarView` uses the `GTK_ACCESSIBLE_ROLE_GROUP` role.
+public struct ToolbarView: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The current bottom bar height.
+ ///
+ /// Bottom bar height does change depending on
+ /// [property@ToolbarView:reveal-bottom-bars], including during the transition.
+ ///
+ /// See [property@ToolbarView:top-bar-height].
+ var bottomBarHeight: Int?
+ /// The content widget.
+ var content: (() -> Body)?
+ /// Whether the content widget can extend behind bottom bars.
+ ///
+ /// This can be used in combination with
+ /// [property@ToolbarView:reveal-bottom-bars] to show and hide toolbars in
+ /// fullscreen.
+ ///
+ /// See [property@ToolbarView:extend-content-to-top-edge].
+ var extendContentToBottomEdge: Bool?
+ /// Whether the content widget can extend behind top bars.
+ ///
+ /// This can be used in combination with [property@ToolbarView:reveal-top-bars]
+ /// to show and hide toolbars in fullscreen.
+ ///
+ /// See [property@ToolbarView:extend-content-to-bottom-edge].
+ var extendContentToTopEdge: Bool?
+ /// Whether bottom bars are visible.
+ ///
+ /// The transition will be animated.
+ ///
+ /// This can be used in combination with
+ /// [property@ToolbarView:extend-content-to-bottom-edge] to show and hide
+ /// toolbars in fullscreen.
+ ///
+ /// See [property@ToolbarView:reveal-top-bars].
+ var revealBottomBars: Bool?
+ /// Whether top bars are revealed.
+ ///
+ /// The transition will be animated.
+ ///
+ /// This can be used in combination with
+ /// [property@ToolbarView:extend-content-to-top-edge] to show and hide toolbars
+ /// in fullscreen.
+ ///
+ /// See [property@ToolbarView:reveal-bottom-bars].
+ var revealTopBars: Bool?
+ /// The current top bar height.
+ ///
+ /// Top bar height does change depending [property@ToolbarView:reveal-top-bars],
+ /// including during the transition.
+ ///
+ /// See [property@ToolbarView:bottom-bar-height].
+ var topBarHeight: Int?
+ /// The body for the widget "bottom".
+ var bottom: () -> Body = { [] }
+ /// The body for the widget "top".
+ var top: () -> Body = { [] }
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `ToolbarView`.
+ public init() {
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_toolbar_view_new()?.opaque())
+ update(storage, modifiers: modifiers)
+ if let contentStorage = content?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["content"] = [contentStorage]
+ adw_toolbar_view_set_content(storage.pointer, contentStorage.pointer?.cast())
+ }
+
+ var bottomStorage: [ViewStorage] = []
+ for view in bottom() {
+ bottomStorage.append(view.storage(modifiers: modifiers))
+ adw_toolbar_view_add_bottom_bar(storage.pointer, bottomStorage.last?.pointer?.cast())
+ }
+ storage.content["bottom"] = bottomStorage
+ var topStorage: [ViewStorage] = []
+ for view in top() {
+ topStorage.append(view.storage(modifiers: modifiers))
+ adw_toolbar_view_add_top_bar(storage.pointer, topStorage.last?.pointer?.cast())
+ }
+ storage.content["top"] = topStorage
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ if let widget = storage.content["content"]?.first {
+ content?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+ if let extendContentToBottomEdge {
+ adw_toolbar_view_set_extend_content_to_bottom_edge(widget, extendContentToBottomEdge.cBool)
+ }
+ if let extendContentToTopEdge {
+ adw_toolbar_view_set_extend_content_to_top_edge(widget, extendContentToTopEdge.cBool)
+ }
+ if let revealBottomBars {
+ adw_toolbar_view_set_reveal_bottom_bars(widget, revealBottomBars.cBool)
+ }
+ if let revealTopBars {
+ adw_toolbar_view_set_reveal_top_bars(widget, revealTopBars.cBool)
+ }
+
+ if let bottomStorage = storage.content["bottom"] {
+ for (index, view) in bottom().enumerated() {
+ if let storage = bottomStorage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+ if let topStorage = storage.content["top"] {
+ for (index, view) in top().enumerated() {
+ if let storage = topStorage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The current bottom bar height.
+ ///
+ /// Bottom bar height does change depending on
+ /// [property@ToolbarView:reveal-bottom-bars], including during the transition.
+ ///
+ /// See [property@ToolbarView:top-bar-height].
+ public func bottomBarHeight(_ bottomBarHeight: Int?) -> Self {
+ var newSelf = self
+ newSelf.bottomBarHeight = bottomBarHeight
+
+ return newSelf
+ }
+
+ /// The content widget.
+ public func content(@ViewBuilder _ content: @escaping (() -> Body)) -> Self {
+ var newSelf = self
+ newSelf.content = content
+
+ return newSelf
+ }
+
+ /// Whether the content widget can extend behind bottom bars.
+ ///
+ /// This can be used in combination with
+ /// [property@ToolbarView:reveal-bottom-bars] to show and hide toolbars in
+ /// fullscreen.
+ ///
+ /// See [property@ToolbarView:extend-content-to-top-edge].
+ public func extendContentToBottomEdge(_ extendContentToBottomEdge: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.extendContentToBottomEdge = extendContentToBottomEdge
+
+ return newSelf
+ }
+
+ /// Whether the content widget can extend behind top bars.
+ ///
+ /// This can be used in combination with [property@ToolbarView:reveal-top-bars]
+ /// to show and hide toolbars in fullscreen.
+ ///
+ /// See [property@ToolbarView:extend-content-to-bottom-edge].
+ public func extendContentToTopEdge(_ extendContentToTopEdge: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.extendContentToTopEdge = extendContentToTopEdge
+
+ return newSelf
+ }
+
+ /// Whether bottom bars are visible.
+ ///
+ /// The transition will be animated.
+ ///
+ /// This can be used in combination with
+ /// [property@ToolbarView:extend-content-to-bottom-edge] to show and hide
+ /// toolbars in fullscreen.
+ ///
+ /// See [property@ToolbarView:reveal-top-bars].
+ public func revealBottomBars(_ revealBottomBars: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.revealBottomBars = revealBottomBars
+
+ return newSelf
+ }
+
+ /// Whether top bars are revealed.
+ ///
+ /// The transition will be animated.
+ ///
+ /// This can be used in combination with
+ /// [property@ToolbarView:extend-content-to-top-edge] to show and hide toolbars
+ /// in fullscreen.
+ ///
+ /// See [property@ToolbarView:reveal-bottom-bars].
+ public func revealTopBars(_ revealTopBars: Bool? = true) -> Self {
+ var newSelf = self
+ newSelf.revealTopBars = revealTopBars
+
+ return newSelf
+ }
+
+ /// The current top bar height.
+ ///
+ /// Top bar height does change depending [property@ToolbarView:reveal-top-bars],
+ /// including during the transition.
+ ///
+ /// See [property@ToolbarView:bottom-bar-height].
+ public func topBarHeight(_ topBarHeight: Int?) -> Self {
+ var newSelf = self
+ newSelf.topBarHeight = topBarHeight
+
+ return newSelf
+ }
+
+ /// Set the body for "bottom".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func bottom(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.bottom = body
+ return newSelf
+ }
+ /// Set the body for "top".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func top(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.top = body
+ return newSelf
+ }
+}
diff --git a/Sources/Adwaita/View/Generated/WindowTitle.swift b/Sources/Adwaita/View/Generated/WindowTitle.swift
new file mode 100644
index 0000000..c2833d8
--- /dev/null
+++ b/Sources/Adwaita/View/Generated/WindowTitle.swift
@@ -0,0 +1,99 @@
+//
+// WindowTitle.swift
+// Adwaita
+//
+// Created by auto-generation on 22.01.24.
+//
+
+import CAdw
+import LevenshteinTransformations
+
+/// A helper widget for setting a window's title and subtitle.
+///
+///
+///
+/// `AdwWindowTitle` shows a title and subtitle. It's intended to be used as the
+/// title child of [class@Gtk.HeaderBar] or [class@HeaderBar].
+///
+/// ## CSS nodes
+///
+/// `AdwWindowTitle` has a single CSS node with name `windowtitle`.
+public struct WindowTitle: Widget {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+
+ /// The subtitle to display.
+ ///
+ /// The subtitle should give the user additional details.
+ var subtitle: String
+ /// The title to display.
+ ///
+ /// The title typically identifies the current view or content item, and
+ /// generally does not use the application name.
+ var title: String
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+
+ /// Initialize `WindowTitle`.
+ public init(subtitle: String, title: String) {
+ self.subtitle = subtitle
+ self.title = title
+ }
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(adw_window_title_new(title, subtitle)?.opaque())
+ update(storage, modifiers: modifiers)
+
+
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
+ storage.modify { widget in
+ adw_window_title_set_subtitle(widget, subtitle)
+ adw_window_title_set_title(widget, title)
+
+
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+
+ /// The subtitle to display.
+ ///
+ /// The subtitle should give the user additional details.
+ public func subtitle(_ subtitle: String) -> Self {
+ var newSelf = self
+ newSelf.subtitle = subtitle
+
+ return newSelf
+ }
+
+ /// The title to display.
+ ///
+ /// The title typically identifies the current view or content item, and
+ /// generally does not use the application name.
+ public func title(_ title: String) -> Self {
+ var newSelf = self
+ newSelf.title = title
+
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/HStack.swift b/Sources/Adwaita/View/HStack.swift
index 9b5fc25..594b706 100644
--- a/Sources/Adwaita/View/HStack.swift
+++ b/Sources/Adwaita/View/HStack.swift
@@ -5,40 +5,21 @@
// Created by david-swift on 26.09.23.
//
-import Libadwaita
-
/// A horizontal GtkBox equivalent.
-public struct HStack: Widget {
+public struct HStack: View {
/// The content.
var content: () -> Body
+ /// The view's body.
+ public var view: Body {
+ VStack(horizontal: true, content: content)
+ }
+
/// Initialize a `HStack`.
/// - Parameter content: The view content.
public init(@ViewBuilder content: @escaping () -> Body) {
self.content = content
}
- /// Update a view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- content().update(storage.content[.mainContent] ?? [], modifiers: modifiers)
- }
-
- /// Get a view storage.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let box: Box = .init(horizontal: true)
- var content: [ViewStorage] = []
- for element in self.content() {
- let widget = element.storage(modifiers: modifiers)
- _ = box.append(widget.view)
- content.append(widget)
- }
- return .init(box, content: [.mainContent: content])
- }
-
}
diff --git a/Sources/Adwaita/View/HeaderBar+.swift b/Sources/Adwaita/View/HeaderBar+.swift
new file mode 100644
index 0000000..59a4e86
--- /dev/null
+++ b/Sources/Adwaita/View/HeaderBar+.swift
@@ -0,0 +1,52 @@
+//
+// HeaderBar+.swift
+// Adwaita
+//
+// Created by david-swift on 21.01.23.
+//
+
+extension HeaderBar {
+
+ /// Initialize a header bar.
+ /// - Parameters:
+ /// - titleButtons: Whether the title buttons (e.g. close button) are visible.
+ /// - start: The start content.
+ /// - end: The end content.
+ public init(
+ titleButtons: Bool = true,
+ @ViewBuilder start: @escaping () -> Body,
+ @ViewBuilder end: @escaping () -> Body
+ ) {
+ self.init()
+ self = self.showStartTitleButtons(titleButtons).showEndTitleButtons(titleButtons)
+ self = self.start(start).end(end)
+ }
+
+ /// Initialize an empty header bar.
+ /// - Returns: The header bar.
+ public static func empty() -> Self {
+ .init(start: { }, end: { })
+ }
+
+ /// Initialize a header bar with only views at the start.
+ /// - Parameter start: The views.
+ /// - Returns: The header bar.
+ public static func start(@ViewBuilder start: @escaping () -> Body) -> Self {
+ .init(start: start) { }
+ }
+
+ /// Initialize a header bar with only views at the end.
+ /// - Parameter start: The views.
+ /// - Returns: The header bar.
+ public static func end(@ViewBuilder end: @escaping () -> Body) -> Self {
+ .init(start: { }, end: end)
+ }
+
+ /// Set the title widget for the header bar.
+ /// - Parameter view: The widget in the header bar.
+ /// - Returns: The header bar.
+ public func headerBarTitle(@ViewBuilder view: @escaping () -> Body) -> Self {
+ titleWidget(view)
+ }
+
+}
diff --git a/Sources/Adwaita/View/HeaderBar.swift b/Sources/Adwaita/View/HeaderBar.swift
deleted file mode 100644
index 38412b5..0000000
--- a/Sources/Adwaita/View/HeaderBar.swift
+++ /dev/null
@@ -1,113 +0,0 @@
-//
-// HeaderBar.swift
-// Adwaita
-//
-// Created by david-swift on 23.08.23.
-//
-
-import Libadwaita
-
-/// A header bar widget.
-public struct HeaderBar: Widget {
-
- /// The start content of the header bar.
- var start: Body
- /// The end content of the header bar.
- var end: Body
- /// Whether the title buttons are visible.
- var titleButtons: Bool
- /// The view acting as the title of the header bar.
- var headerBarTitle: Body?
-
- /// The start content's id.
- let startID = "start"
- /// The end content's id.
- let endID = "end"
- /// The title's id.
- let titleID = "title"
-
- /// Initialize a header bar.
- /// - Parameters:
- /// - titleButtons: Whether the title buttons (e.g. close button) are visible.
- /// - start: The start content.
- /// - end: The end content.
- public init(titleButtons: Bool = true, @ViewBuilder start: () -> Body, @ViewBuilder end: () -> Body) {
- self.titleButtons = titleButtons
- self.start = start()
- self.end = end()
- }
-
- /// Initialize an empty header bar.
- /// - Returns: The header bar.
- public static func empty() -> Self {
- .init(start: { }, end: { })
- }
-
- /// Initialize a header bar with only views at the start.
- /// - Parameter start: The views.
- /// - Returns: The header bar.
- public static func start(@ViewBuilder start: () -> Body) -> Self {
- .init(start: start) { }
- }
-
- /// Initialize a header bar with only views at the end.
- /// - Parameter start: The views.
- /// - Returns: The header bar.
- public static func end(@ViewBuilder end: () -> Body) -> Self {
- .init(start: { }, end: end)
- }
-
- /// Update a header bar's view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let bar = storage.view as? Libadwaita.HeaderBar {
- _ = bar.showTitleButtons(titleButtons)
- }
- start.update(storage.content[startID] ?? [], modifiers: modifiers)
- end.update(storage.content[endID] ?? [], modifiers: modifiers)
- if let first = storage.content[titleID]?.first {
- headerBarTitle?.widget(modifiers: modifiers).update(first, modifiers: modifiers)
- }
- }
-
- /// Get the container for a header bar.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let bar: Libadwaita.HeaderBar = .init()
- var startContent: [ViewStorage] = []
- var endContent: [ViewStorage] = []
- for element in start {
- let element = element.storage(modifiers: modifiers)
- _ = bar.packStart(element.view)
- startContent.append(element)
- }
- for element in end {
- let element = element.storage(modifiers: modifiers)
- _ = bar.packEnd(element.view)
- endContent.append(element)
- }
- let title = headerBarTitle?.widget(modifiers: modifiers).container(modifiers: modifiers)
- let titleStorage: [ViewStorage]
- if let title {
- _ = bar.titleWidget(title.view)
- titleStorage = [title]
- } else {
- titleStorage = []
- }
- _ = bar.showTitleButtons(titleButtons)
- return .init(bar, content: [startID: startContent, endID: endContent, titleID: titleStorage])
- }
-
- /// Set the title widget for the header bar.
- /// - Parameter view: The widget in the header bar.
- /// - Returns: The header bar.
- public func headerBarTitle(@ViewBuilder view: () -> Body) -> Self {
- var newSelf = self
- newSelf.headerBarTitle = view()
- return newSelf
- }
-
-}
diff --git a/Sources/Adwaita/View/List.swift b/Sources/Adwaita/View/List.swift
index 41c4e25..5afec94 100644
--- a/Sources/Adwaita/View/List.swift
+++ b/Sources/Adwaita/View/List.swift
@@ -5,114 +5,59 @@
// Created by david-swift on 25.09.23.
//
-import LevenshteinTransformations
-import Libadwaita
+import CAdw
/// A list box widget.
-public struct List: Widget where Element: Identifiable {
+public typealias List = ListBox
- /// The elements.
- var elements: [Element]
- /// The content.
- var content: (Element) -> Body
- /// The identifier of the selected element.
- @Binding var selection: Element.ID
+extension List {
- /// The identifier of the elements storage.
- let elementsID = "elements"
+ /// The ID for the field storing the selection value.
+ static var selectionField: String { "selection" }
+ /// The ID for the field storing the elements.
+ static var elementsField: String { "element" }
/// Initialize `List`.
/// - Parameters:
/// - elements: The elements.
- /// - selection: The identifier of the selected element.
+ /// - selection: The identifier of the selected element. Selection disabled if `nil`.
/// - content: The view for an element.
public init(
_ elements: [Element],
- selection: Binding,
+ selection: Binding?,
@ViewBuilder content: @escaping (Element) -> Body
) {
- self.content = content
- self.elements = elements
- self._selection = selection
- }
-
- /// Update a view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let box = storage.view as? ListBox {
- var content: [ViewStorage] = storage.content[.mainContent] ?? []
- updateList(box: box, content: .init { content } set: { content = $0 }, modifiers: modifiers)
- storage.content[.mainContent] = content
- for (index, element) in elements.enumerated() {
- self.content(element).widget(modifiers: modifiers).update(content[index], modifiers: modifiers)
+ self.init(elements, content: content)
+ let id: (ViewStorage, [Element]) -> Element.ID? = { storage, elements in
+ if let row = gtk_list_box_get_selected_row(storage.pointer) {
+ return elements[safe: .init(gtk_list_box_row_get_index(row))]?.id
}
+ return nil
}
- }
-
- /// Get a view storage.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let box: ListBox = .init()
- var content: [ViewStorage] = []
- updateList(box: box, content: .init { content } set: { content = $0 }, modifiers: modifiers)
- _ = box.handler {
- let selection = box.getSelectedRow()
- if let id = (box.fields[elementsID] as? [Element] ?? elements)[safe: selection]?.id {
- self.selection = id
+ if let selection {
+ appearFunctions.append { storage in
+ storage.fields[Self.selectionField] = selection
+ storage.connectSignal(name: "selected_rows_changed", id: Self.selectionField) {
+ if let binding = storage.fields[Self.selectionField] as? Binding,
+ let elements = storage.fields[Self.elementsField] as? [Element],
+ let id = id(storage, elements) {
+ binding.wrappedValue = id
+ }
+ }
}
- }
- return .init(box, content: [.mainContent: content])
- }
-
- /// Update the list's content and selection.
- /// - Parameters:
- /// - box: The list box.
- /// - content: The content's view storage.
- /// - modifiers: The view modifiers.
- func updateList(box: ListBox, content: Binding<[ViewStorage]>, modifiers: [(View) -> View]) {
- let old = box.fields[elementsID] as? [Element] ?? []
- old.identifiableTransform(
- to: elements,
- functions: .init { index, element in
- let widget = getWidget(element: element, modifiers: modifiers)
- _ = box.remove(at: index)
- _ = box.insert(widget.view, at: index)
- content.wrappedValue.remove(at: index)
- content.wrappedValue.insert(widget, at: index)
- } delete: { index in
- _ = box.remove(at: index)
- content.wrappedValue.remove(at: index)
- updateSelection(box: box)
- } insert: { index, element in
- let widget = getWidget(element: element, modifiers: modifiers)
- _ = box.insert(widget.view, at: index)
- content.wrappedValue.insert(widget, at: index)
+ updateFunctions.append { storage in
+ if selection.wrappedValue != id(storage, elements),
+ let index = elements.firstIndex(where: { $0.id == selection.wrappedValue })?.cInt {
+ gtk_list_box_select_row(storage.pointer, gtk_list_box_get_row_at_index(storage.pointer, index))
+ }
+ }
+ } else {
+ appearFunctions.append { storage in
+ gtk_list_box_set_selection_mode(storage.pointer, GTK_SELECTION_NONE)
}
- )
- box.fields[elementsID] = elements
- updateSelection(box: box)
- }
-
- /// Update the list's selection.
- /// - Parameter box: The list box.
- func updateSelection(box: ListBox) {
- if let index = elements.firstIndex(where: { $0.id == selection }) {
- box.selectRow(at: index)
}
}
- /// Get the view storage of an element.
- /// - Parameters:
- /// - element: The element.
- /// - modifiers: The modifiers.
- /// - Returns: The view storage.
- func getWidget(element: Element, modifiers: [(View) -> View]) -> ViewStorage {
- self.content(element).widget(modifiers: modifiers).container(modifiers: modifiers)
- }
-
/// Add the "navigation-sidebar" style class.
public func sidebarStyle() -> View {
style("navigation-sidebar")
diff --git a/Sources/Adwaita/View/Menu+.swift b/Sources/Adwaita/View/Menu+.swift
new file mode 100644
index 0000000..1b06e64
--- /dev/null
+++ b/Sources/Adwaita/View/Menu+.swift
@@ -0,0 +1,53 @@
+//
+// Menu+.swift
+// Adwaita
+//
+// Created by david-swift on 21.10.23.
+//
+
+import CAdw
+
+extension Menu {
+
+ // 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: @escaping () -> MenuContent
+ ) {
+ self.init()
+ self = self
+ .child {
+ ButtonContent()
+ .iconName(icon.string)
+ .label(label)
+ }
+ .menuModel(app: app, window: window, 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.init()
+ }
+
+}
diff --git a/Sources/Adwaita/View/Menu.swift b/Sources/Adwaita/View/Menu.swift
deleted file mode 100644
index bd532d6..0000000
--- a/Sources/Adwaita/View/Menu.swift
+++ /dev/null
@@ -1,101 +0,0 @@
-//
-// Menu.swift
-// Adwaita
-//
-// Created by david-swift on 21.10.23.
-//
-
-import Libadwaita
-
-/// 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.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let button = storage.view as? Libadwaita.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.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The button's view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let button: Libadwaita.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/Sources/Adwaita/View/Modifiers/AppearObserver.swift b/Sources/Adwaita/View/Modifiers/AppearObserver.swift
index e8fbf4b..a9569e4 100644
--- a/Sources/Adwaita/View/Modifiers/AppearObserver.swift
+++ b/Sources/Adwaita/View/Modifiers/AppearObserver.swift
@@ -5,13 +5,13 @@
// Created by david-swift on 29.11.23.
//
-import Libadwaita
+import CAdw
/// A widget which executes a custom code when being rendered for the first time.
struct AppearObserver: Widget {
/// The function.
- var onAppear: (NativeWidgetPeer) -> Void
+ var onAppear: (ViewStorage) -> Void
/// The content.
var content: View
@@ -20,7 +20,7 @@ struct AppearObserver: Widget {
/// - Returns: The content's container.
func container(modifiers: [(View) -> View]) -> ViewStorage {
let storage = content.storage(modifiers: modifiers)
- onAppear(storage.view)
+ onAppear(storage)
return storage
}
@@ -39,7 +39,7 @@ extension View {
/// Run a function on the widget when it appears for the first time.
/// - Parameter closure: The function.
/// - Returns: A view.
- public func inspectOnAppear(_ closure: @escaping (NativeWidgetPeer) -> Void) -> View {
+ public func inspectOnAppear(_ closure: @escaping (ViewStorage) -> Void) -> View {
AppearObserver(onAppear: closure, content: self)
}
@@ -54,7 +54,12 @@ extension View {
/// - Parameter handler: The function.
/// - Returns: A view.
public func onClick(handler: @escaping () -> Void) -> View {
- inspectOnAppear { _ = $0.onClick(closure: handler) }
+ inspectOnAppear { storage in
+ let controller = ViewStorage(gtk_gesture_click_new())
+ gtk_widget_add_controller(storage.pointer?.cast(), controller.pointer)
+ storage.fields["controller"] = controller
+ controller.connectSignal(name: "stopped", argCount: 0, handler: handler)
+ }
}
}
diff --git a/Sources/Adwaita/View/Modifiers/Clamp+.swift b/Sources/Adwaita/View/Modifiers/Clamp+.swift
new file mode 100644
index 0000000..c66ec4f
--- /dev/null
+++ b/Sources/Adwaita/View/Modifiers/Clamp+.swift
@@ -0,0 +1,52 @@
+//
+// Clamp+.swift
+// Adwaita
+//
+// Created by david-swift on 20.01.24.
+//
+
+import CAdw
+
+extension Clamp {
+
+ /// Initialize either a horizontal or vertical clamp.
+ /// - Parameter vertical: Whether it is a vertical clamp.
+ init(vertical: Bool) {
+ self.init()
+ if vertical {
+ appearFunctions.append { storage in
+ gtk_orientable_set_orientation(storage.pointer, GTK_ORIENTATION_VERTICAL)
+ }
+ }
+ }
+
+}
+
+extension View {
+
+ /// Set the view's maximum width.
+ /// - Parameter maxSize: The maximum width.
+ /// - Returns: A view.
+ public func frame(maxSize: Int? = nil) -> Clamp {
+ frame(maxWidth: maxSize)
+ }
+
+ /// Set the view's maximum width.
+ /// - Parameter maxWidth: The maximum width.
+ /// - Returns: A view.
+ public func frame(maxWidth: Int? = nil) -> Clamp {
+ .init()
+ .child { self }
+ .maximumSize(maxWidth ?? -1)
+ }
+
+ /// Set the view's maximum height.
+ /// - Parameter maxHeight: The maximum height.
+ /// - Returns: A view.
+ public func frame(maxHeight: Int? = nil) -> Clamp {
+ .init(vertical: true)
+ .child { self }
+ .maximumSize(maxHeight ?? -1)
+ }
+
+}
diff --git a/Sources/Adwaita/View/Modifiers/Clamp.swift b/Sources/Adwaita/View/Modifiers/Clamp.swift
deleted file mode 100644
index 25a9f80..0000000
--- a/Sources/Adwaita/View/Modifiers/Clamp.swift
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-// Clamp.swift
-// Adwaita
-//
-// Created by david-swift on 12.10.23.
-//
-
-import Libadwaita
-
-/// A horizontal AdwClamp equivalent.
-struct Clamp: Widget {
-
- /// The content.
- var content: View
- /// The maximum size.
- var maxSize: Int
-
- /// Update a view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let clamp = storage.view as? Libadwaita.Clamp {
- _ = clamp.maximumSize(maxSize)
- }
- if let storage = storage.content[.mainContent]?[safe: 0] {
- content.widget(modifiers: modifiers).update(storage, modifiers: modifiers)
- }
- }
-
- /// Get a view storage.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- func container(modifiers: [(View) -> View]) -> ViewStorage {
- let container = content.storage(modifiers: modifiers)
- let clamp: Libadwaita.Clamp = .init(container.view)
- _ = clamp.maximumSize(maxSize)
- return .init(clamp, content: [.mainContent: [container]])
- }
-
-}
-
-extension View {
-
- /// Set the view's maximal size.
- /// - Parameter maxSize: The maximal size.
- /// - Returns: A view.
- public func frame(maxSize: Int? = nil) -> View {
- Clamp(content: self, maxSize: maxSize ?? -1)
- }
-
-}
diff --git a/Sources/Adwaita/View/Modifiers/ContentModifier.swift b/Sources/Adwaita/View/Modifiers/ContentModifier.swift
index 74d6802..8614ff3 100644
--- a/Sources/Adwaita/View/Modifiers/ContentModifier.swift
+++ b/Sources/Adwaita/View/Modifiers/ContentModifier.swift
@@ -5,8 +5,6 @@
// Created by david-swift on 11.11.23.
//
-import Libadwaita
-
/// A widget which replaces views of a specific type in its content.
struct ContentModifier: Widget where Content: View {
diff --git a/Sources/Adwaita/View/Modifiers/InspectorWrapper.swift b/Sources/Adwaita/View/Modifiers/InspectorWrapper.swift
index dddf401..41ad057 100644
--- a/Sources/Adwaita/View/Modifiers/InspectorWrapper.swift
+++ b/Sources/Adwaita/View/Modifiers/InspectorWrapper.swift
@@ -5,13 +5,13 @@
// Created by david-swift on 10.09.23.
//
-import Libadwaita
+import CAdw
/// A widget which executes a custom code on the GTUI widget when being created and updated.
struct InspectorWrapper: Widget {
/// The custom code to edit the widget.
- var modify: (NativeWidgetPeer?) -> Void
+ var modify: (ViewStorage) -> Void
/// The wrapped view.
var content: View
@@ -20,7 +20,7 @@ struct InspectorWrapper: Widget {
/// - Returns: The content's container.
func container(modifiers: [(View) -> View]) -> ViewStorage {
let storage = content.storage(modifiers: modifiers)
- modify(storage.view)
+ modify(storage)
return storage
}
@@ -30,7 +30,7 @@ struct InspectorWrapper: Widget {
/// - modifiers: Modify views before being updated.
func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
content.updateStorage(storage, modifiers: modifiers)
- modify(storage.view)
+ modify(storage)
}
}
@@ -40,7 +40,7 @@ extension View {
/// Modify a GTUI widget before being displayed and when being updated.
/// - Parameter modify: Modify the widget.
/// - Returns: A view.
- public func inspect(_ modify: @escaping (NativeWidgetPeer?) -> Void) -> View {
+ public func inspect(_ modify: @escaping (ViewStorage) -> Void) -> View {
InspectorWrapper(modify: modify, content: self)
}
@@ -50,35 +50,40 @@ extension View {
/// - edges: The edges which are affected by the padding.
/// - Returns: A view.
public func padding(_ padding: Int = 10, _ edges: Set = .all) -> View {
- inspect { _ = $0?.padding(padding, edges) }
+ inspect { widget in
+ if edges.contains(.leading) { gtk_widget_set_margin_start(widget.pointer?.cast(), padding.cInt) }
+ if edges.contains(.trailing) { gtk_widget_set_margin_end(widget.pointer?.cast(), padding.cInt) }
+ if edges.contains(.top) { gtk_widget_set_margin_top(widget.pointer?.cast(), padding.cInt) }
+ if edges.contains(.bottom) { gtk_widget_set_margin_bottom(widget.pointer?.cast(), padding.cInt) }
+ }
}
/// Enable or disable the horizontal expansion.
/// - Parameter enabled: Whether it is enabled or disabled.
/// - Returns: A view.
public func hexpand(_ enabled: Bool = true) -> View {
- inspect { _ = $0?.hexpand() }
+ inspect { gtk_widget_set_hexpand($0.pointer?.cast(), enabled.cBool) }
}
/// Enable or disable the vertical expansion.
/// - Parameter enabled: Whether it is enabled or disabled.
/// - Returns: A view.
public func vexpand(_ enabled: Bool = true) -> View {
- inspect { _ = $0?.vexpand() }
+ inspect { gtk_widget_set_vexpand($0.pointer?.cast(), enabled.cBool) }
}
/// Set the horizontal alignment.
/// - Parameter align: The alignment.
/// - Returns: A view.
public func halign(_ align: Alignment) -> View {
- inspect { _ = $0?.halign(align) }
+ inspect { gtk_widget_set_halign($0.pointer?.cast(), align.cAlign) }
}
/// Set the vertical alignment.
/// - Parameter align: The alignment.
/// - Returns: A view.
public func valign(_ align: Alignment) -> View {
- inspect { _ = $0?.valign(align) }
+ inspect { gtk_widget_set_valign($0.pointer?.cast(), align.cAlign) }
}
/// Set the view's minimal width or height.
@@ -87,28 +92,28 @@ extension View {
/// - minHeight: The minimal height.
/// - Returns: A view.
public func frame(minWidth: Int? = nil, minHeight: Int? = nil) -> View {
- inspect { _ = $0?.frame(minWidth: minWidth, minHeight: minHeight) }
+ inspect { gtk_widget_set_size_request($0.pointer?.cast(), minWidth?.cInt ?? 1, minHeight?.cInt ?? -1) }
}
/// Set the view's transition.
/// - Parameter transition: The transition.
/// - Returns: A view.
public func transition(_ transition: Transition) -> View {
- inspect { $0?.fields[.transition] = transition }
+ inspect { $0.fields[.transition] = transition }
}
/// Set the view's navigation title.
/// - Parameter label: The navigation title.
/// - Returns: A view.
public func navigationTitle(_ label: String) -> View {
- inspect { $0?.fields[.navigationLabel] = label }
+ inspect { $0.fields[.navigationLabel] = label }
}
/// Add a style class to the view.
/// - Parameter style: The style class.
/// - Returns: A view.
public func style(_ style: String) -> View {
- inspect { _ = $0?.addStyle(style) }
+ inspect { gtk_widget_add_css_class($0.pointer?.cast(), style) }
}
/// Run a function when the view gets an update.
@@ -122,16 +127,7 @@ extension View {
/// - Parameter insensitive: Whether the view is insensitive.
/// - Returns: A view.
public func insensitive(_ insensitive: Bool = true) -> View {
- inspect { _ = $0?.sensitive(!insensitive) }
+ inspect { gtk_widget_set_sensitive($0.pointer?.cast(), insensitive ? 0 : 1) }
}
}
-
-/// The alignment for a widget.
-public typealias Alignment = Libadwaita.Alignment
-/// The edges of a widget.
-public typealias Edge = Libadwaita.Edge
-/// An icon.
-public typealias Icon = Libadwaita.Icon
-/// A transition for a stack.
-public typealias Transition = Libadwaita.Transition
diff --git a/Sources/Adwaita/View/Modifiers/ModifierStopper.swift b/Sources/Adwaita/View/Modifiers/ModifierStopper.swift
index a6dc3cd..c4b8c26 100644
--- a/Sources/Adwaita/View/Modifiers/ModifierStopper.swift
+++ b/Sources/Adwaita/View/Modifiers/ModifierStopper.swift
@@ -5,8 +5,6 @@
// Created by david-swift on 11.11.23.
//
-import Libadwaita
-
/// Remove all of the content modifiers for the wrapped views.
struct ModifierStopper: Widget {
diff --git a/Sources/Adwaita/View/Modifiers/Overlay.swift b/Sources/Adwaita/View/Modifiers/Overlay.swift
deleted file mode 100644
index 9e95aaf..0000000
--- a/Sources/Adwaita/View/Modifiers/Overlay.swift
+++ /dev/null
@@ -1,56 +0,0 @@
-//
-// Overlay.swift
-// Adwaita
-//
-// Created by david-swift on 03.01.24.
-//
-
-import Libadwaita
-
-/// A wrapper around a view for adding other views on top.
-struct Overlay: Widget {
-
- /// The child view.
- var child: View
- /// The overlay view.
- var overlay: Body
-
- /// The identifier for the overlay content.
- let overlayID = "overlay"
-
- /// Get the overlay's view storage.
- /// - Parameter modifiers: The view modifiers.
- /// - Returns: The view storage.
- func container(modifiers: [(View) -> View]) -> ViewStorage {
- let contentStorage = child.storage(modifiers: modifiers)
- let overlayStorage = overlay.widget(modifiers: modifiers).storage(modifiers: modifiers)
- let overlay = Libadwaita.Overlay().child(contentStorage.view).addOverlay(overlayStorage.view)
- return .init(overlay, content: [.mainContent: [contentStorage], overlayID: [overlayStorage]])
- }
-
- /// Update the overlay's view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: The view modifiers.
- func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let storage = storage.content[.mainContent]?.first {
- child.updateStorage(storage, modifiers: modifiers)
- }
- if let storage = storage.content[overlayID]?.first {
- overlay.widget(modifiers: modifiers).update(storage, modifiers: modifiers)
- }
- }
-
-}
-
-extension View {
-
- /// Add an overlay view.
- /// - Parameters:
- /// - overlay: The overlay view.
- /// - Returns: A view.
- public func overlay(@ViewBuilder _ overlay: @escaping () -> Body) -> View {
- Overlay(child: self, overlay: overlay())
- }
-
-}
diff --git a/Sources/Adwaita/View/Modifiers/ToastOverlay+.swift b/Sources/Adwaita/View/Modifiers/ToastOverlay+.swift
new file mode 100644
index 0000000..ced3207
--- /dev/null
+++ b/Sources/Adwaita/View/Modifiers/ToastOverlay+.swift
@@ -0,0 +1,78 @@
+//
+// ToastOverlay+.swift
+// Adwaita
+//
+// Created by david-swift on 21.01.24.
+//
+
+import CAdw
+import Foundation
+
+/// A wrapper around a view presenting toasts.
+extension ToastOverlay {
+
+ /// Initialize a toast overlay.
+ /// - Parameters:
+ /// - title: The toast's title.
+ /// - signal: The signal for adding a toast.
+ public init(_ title: String, signal: Signal) {
+ appearFunctions.append { storage in
+ storage.fields["signal"] = signal
+ }
+ updateFunctions.append { storage in
+ if let signal = storage.fields["signal"] as? Signal, signal.update {
+ let toast = ViewStorage(adw_toast_new(title))
+ storage.fields[UUID().uuidString] = toast
+ if let button = storage.fields["button"] as? String,
+ let handler = storage.fields["handler"] as? () -> Void {
+ adw_toast_set_button_label(toast.pointer, button)
+ toast.connectSignal(name: "button-clicked", handler: handler)
+ }
+ adw_toast_overlay_add_toast(storage.pointer, toast.pointer)
+ }
+ }
+ }
+
+ /// Add an action button to the toast.
+ /// - Parameters:
+ /// - button: The button's label.
+ /// - handler: The handler.
+ /// - Returns: The toast overlay.
+ public func action(button: String, handler: @escaping () -> Void) -> Self {
+ var newSelf = self
+ let action: (ViewStorage) -> Void = { storage in
+ storage.fields["button"] = button
+ storage.fields["handler"] = handler
+ }
+ newSelf.updateFunctions.insert(action, at: 0)
+ return newSelf
+ }
+
+}
+
+extension View {
+
+ /// Present a toast when the signal gets activated.
+ /// - Parameters:
+ /// - title: The title of the toast.
+ /// - signal: The signal which activates the presentation of a toast.
+ /// - Returns: A view.
+ public func toast(_ title: String, signal: Signal) -> View {
+ ToastOverlay(title, signal: signal)
+ .child { self }
+ }
+
+ /// Present a toast with a button when the signal gets activated.
+ /// - Parameters:
+ /// - title: The title of the toast.
+ /// - signal: The signal which activates the presentation of a toast.
+ /// - button: The button's label.
+ /// - handler: The handler for the button.
+ /// - Returns: A view.
+ public func toast(_ title: String, signal: Signal, button: String, handler: @escaping () -> Void) -> View {
+ ToastOverlay(title, signal: signal)
+ .child { self }
+ .action(button: button, handler: handler)
+ }
+
+}
diff --git a/Sources/Adwaita/View/Modifiers/ToastOverlay.swift b/Sources/Adwaita/View/Modifiers/ToastOverlay.swift
deleted file mode 100644
index 5266519..0000000
--- a/Sources/Adwaita/View/Modifiers/ToastOverlay.swift
+++ /dev/null
@@ -1,83 +0,0 @@
-//
-// ToastOverlay.swift
-// Adwaita
-//
-// Created by david-swift on 30.11.23.
-//
-
-import Libadwaita
-
-/// A wrapper around a view presenting toasts.
-struct ToastOverlay: Widget {
-
- /// The signal for showing the toast./// Present a toast when the signal gets activated.
- var signal: Signal
- /// The child view.
- var child: View
- /// The title of the toast.
- var title: String
- /// Information about the button if available (label and handler).
- var button: (String, () -> Void)?
-
- /// Get the overlay's view storage.
- /// - Parameter modifiers: The view modifiers.
- /// - Returns: The view storage.
- func container(modifiers: [(View) -> View]) -> ViewStorage {
- let contentStorage = child.widget(modifiers: modifiers).storage(modifiers: modifiers)
- let overlay = Libadwaita.ToastOverlay(contentStorage.view)
- setVisibility(overlay)
- return .init(overlay, content: [.mainContent: [contentStorage]])
- }
-
- /// Update the overlay's view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: The view modifiers.
- func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let overlay = storage.view as? Libadwaita.ToastOverlay {
- setVisibility(overlay)
- }
- if let storage = storage.content[.mainContent]?.first {
- child.widget(modifiers: modifiers).update(storage, modifiers: modifiers)
- }
- }
-
- /// Add a toast if the signal is active.
- /// - Parameter overlay: The toast overlay.
- func setVisibility(_ overlay: Libadwaita.ToastOverlay) {
- if signal.update {
- let toast = Toast(title)
- if let button {
- _ = toast
- .buttonLabel(button.0)
- .buttonHandler(button.1)
- }
- overlay.addToast(toast)
- }
- }
-
-}
-
-extension View {
-
- /// Present a toast when the signal gets activated.
- /// - Parameters:
- /// - title: The title of the toast.
- /// - signal: The signal which activates the presentation of a toast.
- /// - Returns: A view.
- public func toast(_ title: String, signal: Signal) -> View {
- ToastOverlay(signal: signal, child: self, title: title)
- }
-
- /// Present a toast with a button when the signal gets activated.
- /// - Parameters:
- /// - title: The title of the toast.
- /// - signal: The signal which activates the presentation of a toast.
- /// - button: The button's label.
- /// - handler: The handler for the button.
- /// - Returns: A view.
- public func toast(_ title: String, signal: Signal, button: String, handler: @escaping () -> Void) -> View {
- ToastOverlay(signal: signal, child: self, title: title, button: (button, handler))
- }
-
-}
diff --git a/Sources/Adwaita/View/Modifiers/ToolbarView.swift b/Sources/Adwaita/View/Modifiers/ToolbarView.swift
deleted file mode 100644
index 03a710a..0000000
--- a/Sources/Adwaita/View/Modifiers/ToolbarView.swift
+++ /dev/null
@@ -1,93 +0,0 @@
-//
-// ToolbarView.swift
-// Adwaita
-//
-// Created by david-swift on 24.09.23.
-//
-
-import Libadwaita
-
-/// A toolbar view widget.
-struct ToolbarView: Widget {
-
- /// The sidebar's content.
- var content: View
- /// The toolbars.
- var toolbar: () -> Body
- /// Whether the toolbars are bottom toolbars.
- var bottom: Bool
- /// Whether the toolbar is visible.
- var visible: Bool
-
- /// The identifier of the toolbar content.
- let toolbarID = "toolbar"
-
- /// Get the container of the toolbar view widget.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- func container(modifiers: [(View) -> View]) -> ViewStorage {
- let content = content.storage(modifiers: modifiers)
- let view = Libadwaita.ToolbarView(content.view)
- var toolbarContent: [ViewStorage] = []
- for item in toolbar() {
- let storage = item.storage(modifiers: modifiers)
- toolbarContent.append(storage)
- if bottom {
- _ = view.addBottomBar(storage.view)
- } else {
- _ = view.addTopBar(storage.view)
- }
- }
- if bottom {
- view.setRevealBottomBar(visible)
- } else {
- view.setRevealTopBar(visible)
- }
- return .init(view, content: [.mainContent: [content], toolbarID: toolbarContent])
- }
-
- /// Update the view storage of the toolbar view widget.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let mainContent = storage.content[.mainContent]?.first {
- content.widget(modifiers: modifiers).update(mainContent, modifiers: modifiers)
- }
- if let toolbar = storage.content[toolbarID] {
- for (index, content) in toolbar.enumerated() {
- self.toolbar()[safe: index]?.updateStorage(content, modifiers: modifiers)
- }
- }
- if let view = storage.view as? Libadwaita.ToolbarView {
- if bottom {
- view.setRevealBottomBar(visible)
- } else {
- view.setRevealTopBar(visible)
- }
- }
- }
-
-}
-
-extension View {
-
- /// Add a top toolbar to the view.
- /// - Parameters:
- /// - toolbar: The toolbar's content.
- /// - visible: Whether the toolbar is visible.
- /// - Returns: A view.
- public func topToolbar(visible: Bool = true, @ViewBuilder _ toolbar: @escaping () -> Body) -> View {
- ToolbarView(content: self, toolbar: toolbar, bottom: false, visible: visible)
- }
-
- /// Add a bottom toolbar to the view.
- /// - Parameters:
- /// - toolbar: The toolbar's content.
- /// - visible: Whether the toolbar is visible.
- /// - Returns: A view.
- public func bottomToolbar(visible: Bool = true, @ViewBuilder _ toolbar: @escaping () -> Body) -> View {
- ToolbarView(content: self, toolbar: toolbar, bottom: true, visible: visible)
- }
-
-}
diff --git a/Sources/Adwaita/View/Modifiers/View+.swift b/Sources/Adwaita/View/Modifiers/View+.swift
index e2ebd3e..b43ada2 100644
--- a/Sources/Adwaita/View/Modifiers/View+.swift
+++ b/Sources/Adwaita/View/Modifiers/View+.swift
@@ -21,4 +21,38 @@ extension View {
.halign(.center)
}
+ /// Add a top toolbar to the view.
+ /// - Parameters:
+ /// - toolbar: The toolbar's content.
+ /// - visible: Whether the toolbar is visible.
+ /// - Returns: A view.
+ public func topToolbar(visible: Bool = true, @ViewBuilder _ toolbar: @escaping () -> Body) -> View {
+ ToolbarView()
+ .content { self }
+ .top(toolbar)
+ .revealTopBars(visible)
+ }
+
+ /// Add a bottom toolbar to the view.
+ /// - Parameters:
+ /// - toolbar: The toolbar's content.
+ /// - visible: Whether the toolbar is visible.
+ /// - Returns: A view.
+ public func bottomToolbar(visible: Bool = true, @ViewBuilder _ toolbar: @escaping () -> Body) -> View {
+ ToolbarView()
+ .content { self }
+ .bottom(toolbar)
+ .revealBottomBars(visible)
+ }
+
+ /// Add an overlay view.
+ /// - Parameters:
+ /// - overlay: The overlay view.
+ /// - Returns: A view.
+ public func overlay(@ViewBuilder _ overlay: @escaping () -> Body) -> View {
+ Overlay()
+ .child { self }
+ .overlay(overlay)
+ }
+
}
diff --git a/Sources/Adwaita/View/NavigationSplitView.swift b/Sources/Adwaita/View/NavigationSplitView.swift
index 5a1b449..a3f2016 100644
--- a/Sources/Adwaita/View/NavigationSplitView.swift
+++ b/Sources/Adwaita/View/NavigationSplitView.swift
@@ -5,7 +5,7 @@
// Created by david-swift on 24.09.23.
//
-import Libadwaita
+import CAdw
/// A navigation split view widget.
public struct NavigationSplitView: Widget {
@@ -33,20 +33,22 @@ public struct NavigationSplitView: Widget {
/// - Parameter modifiers: Modify views before being updated.
/// - Returns: The view storage.
public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let splitView: Libadwaita.NavigationSplitView = .init()
+ let splitView = adw_navigation_split_view_new()
var content: [String: [ViewStorage]] = [:]
let sidebar = sidebar().widget(modifiers: modifiers).container(modifiers: modifiers)
- let label = sidebar.view.fields[.navigationLabel] as? String ?? ""
- _ = splitView.sidebar(sidebar.view, title: label)
+ let label = sidebar.fields[.navigationLabel] as? String ?? ""
+ let sidebarPage = adw_navigation_page_new(sidebar.pointer?.cast(), label)
+ adw_navigation_split_view_set_sidebar(.init(splitView), sidebarPage?.cast())
content[sidebarID] = [sidebar]
let mainContent = self.content().widget(modifiers: modifiers).container(modifiers: modifiers)
- let mainLabel = mainContent.view.fields[.navigationLabel] as? String ?? ""
- _ = splitView.content(mainContent.view, title: mainLabel)
+ let mainLabel = mainContent.fields[.navigationLabel] as? String ?? ""
+ let mainPage = adw_navigation_page_new(mainContent.pointer?.cast(), mainLabel)
+ adw_navigation_split_view_set_content(.init(splitView), mainPage?.cast())
content[contentID] = [mainContent]
- return .init(splitView, content: content)
+ return .init(.init(splitView), content: content)
}
/// Update the view storage of the navigation split view widget.
diff --git a/Sources/Adwaita/View/OverlaySplitView+.swift b/Sources/Adwaita/View/OverlaySplitView+.swift
new file mode 100644
index 0000000..e0d1ac3
--- /dev/null
+++ b/Sources/Adwaita/View/OverlaySplitView+.swift
@@ -0,0 +1,39 @@
+//
+// OverlaySplitView+.swift
+// Adwaita
+//
+// Created by david-swift on 21.01.23.
+//
+
+import CAdw
+
+extension OverlaySplitView {
+
+ /// Initialize an overlay split view.
+ /// - Parameters:
+ /// - visible: Whether the sidebar is visible.
+ /// - sidebar: The sidebar content.
+ /// - content: The main content.
+ public init(
+ visible: Binding = .constant(true),
+ @ViewBuilder sidebar: @escaping () -> Body,
+ @ViewBuilder content: @escaping () -> Body
+ ) {
+ self.init()
+ self = self.sidebar(sidebar)
+ self = self.content(content)
+ self = self.showSidebar(visible)
+ }
+
+ /// The position of the sidebar.
+ /// - Parameter trailing: Whether the sidebar is at the trailing position.
+ /// - Returns: The navigation split view.
+ public func trailingSidebar(_ trailing: Bool = true) -> Self {
+ var newSelf = self
+ newSelf.updateFunctions.append { storage in
+ adw_overlay_split_view_set_sidebar_position(storage.pointer, trailing ? GTK_PACK_END : GTK_PACK_START)
+ }
+ return newSelf
+ }
+
+}
diff --git a/Sources/Adwaita/View/OverlaySplitView.swift b/Sources/Adwaita/View/OverlaySplitView.swift
deleted file mode 100644
index 678eda0..0000000
--- a/Sources/Adwaita/View/OverlaySplitView.swift
+++ /dev/null
@@ -1,98 +0,0 @@
-//
-// OverlaySplitView.swift
-// Adwaita
-//
-// Created by david-swift on 19.12.23.
-//
-
-import Libadwaita
-
-/// An overlay split view widget.
-public struct OverlaySplitView: Widget {
-
- /// The sidebar's content.
- var sidebar: () -> Body
- /// The split view's main content.
- var content: () -> Body
- /// Whether the sidebar is at the trailing position.
- var trailing = false
- /// Whether the sidebar is visible.
- var visible: Bool
-
- /// The sidebar content's id.
- let sidebarID = "sidebar"
- /// The main content's id.
- let contentID = "content"
-
- /// Initialize an overlay split view.
- /// - Parameters:
- /// - visible: Whether the sidebar is visible.
- /// - sidebar: The sidebar content.
- /// - content: The main content.
- public init(
- visible: Bool = true,
- @ViewBuilder sidebar: @escaping () -> Body,
- @ViewBuilder content: @escaping () -> Body
- ) {
- self.sidebar = sidebar
- self.content = content
- self.visible = visible
- }
-
- /// The position of the sidebar.
- /// - Parameter trailing: Whether the sidebar is at the trailing position.
- /// - Returns: The navigation split view.
- public func trailingSidebar(_ trailing: Bool = true) -> Self {
- var newSelf = self
- newSelf.trailing = trailing
- return newSelf
- }
-
- /// Get the container of the overlay split view widget.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let splitView: Libadwaita.OverlaySplitView = .init()
- var content: [String: [ViewStorage]] = [:]
-
- let sidebar = sidebar().widget(modifiers: modifiers).container(modifiers: modifiers)
- _ = splitView.sidebar(sidebar.view)
- content[sidebarID] = [sidebar]
-
- let mainContent = self.content().widget(modifiers: modifiers).container(modifiers: modifiers)
- _ = splitView.content(mainContent.view)
- content[contentID] = [mainContent]
-
- updatePosition(splitView)
-
- return .init(splitView, content: content)
- }
-
- /// Update the view storage of the overlay split view widget.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let storage = storage.content[contentID]?[safe: 0] {
- content().widget(modifiers: modifiers).update(storage, modifiers: modifiers)
- }
- if let storage = storage.content[sidebarID]?[safe: 0] {
- sidebar().widget(modifiers: modifiers).update(storage, modifiers: modifiers)
- }
- if let splitView = storage.view as? Libadwaita.OverlaySplitView {
- updatePosition(splitView)
- }
- }
-
- /// Update the sidebar's position in the split view.
- /// - Parameter splitView: The overlay split view.
- func updatePosition(_ splitView: Libadwaita.OverlaySplitView) {
- _ = splitView.position(trailing: trailing)
- if visible {
- splitView.showSidebar()
- } else {
- splitView.hideSidebar()
- }
- }
-
-}
diff --git a/Sources/Adwaita/View/ProgressBar+.swift b/Sources/Adwaita/View/ProgressBar+.swift
new file mode 100644
index 0000000..ee5166e
--- /dev/null
+++ b/Sources/Adwaita/View/ProgressBar+.swift
@@ -0,0 +1,23 @@
+//
+// Progressbar+.swift
+// Adwaita
+//
+// Created by david-swift on 15.01.24.
+//
+
+extension ProgressBar {
+
+ /// Initialize a progress bar widget.
+ /// - Parameters:
+ /// - value: The value.
+ /// - total: The maximum value.
+ public init(value: Double, total: Double) {
+ self.init()
+ if total != 0 {
+ self = self.fraction(value / total)
+ } else {
+ self = self.fraction(0)
+ }
+ }
+
+}
diff --git a/Sources/Adwaita/View/ProgressBar.swift b/Sources/Adwaita/View/ProgressBar.swift
deleted file mode 100644
index 4367c3e..0000000
--- a/Sources/Adwaita/View/ProgressBar.swift
+++ /dev/null
@@ -1,46 +0,0 @@
-//
-// Progressbar.swift
-// Adwaita
-//
-// Created by david-swift on 03.01.24.
-//
-
-import Libadwaita
-
-/// A progress bar widget.
-public struct ProgressBar: Widget {
-
- /// The value.
- var value: Double
-
- /// Initialize a progress bar widget.
- /// - Parameters:
- /// - value: The value.
- /// - total: The maximum value.
- public init(value: Double, total: Double) {
- if total != 0 {
- self.value = value / total
- } else {
- self.value = 0
- }
- }
-
- /// Update the view storage of the progress bar widget.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let bar = storage.view as? Libadwaita.ProgressBar {
- _ = bar.fraction(value)
- }
- }
-
- /// Get the container of the progress bar widget.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let bar = Libadwaita.ProgressBar().fraction(value)
- _ = bar.sensitive(false)
- return .init(bar)
- }
-
-}
diff --git a/Sources/Adwaita/View/ScrollView.swift b/Sources/Adwaita/View/ScrollView.swift
index d4482a8..3815667 100644
--- a/Sources/Adwaita/View/ScrollView.swift
+++ b/Sources/Adwaita/View/ScrollView.swift
@@ -5,36 +5,16 @@
// Created by david-swift on 26.09.23.
//
-import Libadwaita
-
/// A GtkScrolledWindow equivalent.
-public struct ScrollView: Widget {
+public typealias ScrollView = ScrolledWindow
- /// The content.
- var content: () -> Body
+extension ScrollView {
/// Initialize a `ScrollView`.
/// - Parameter content: The view content.
public init(@ViewBuilder content: @escaping () -> Body) {
- self.content = content
- }
-
- /// Update a view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let first = storage.content[.mainContent]?.first {
- content().widget(modifiers: modifiers).update(first, modifiers: modifiers)
- }
- }
-
- /// Get a view storage.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let container = content().widget(modifiers: modifiers).container(modifiers: modifiers)
- return .init(Scrolled().setChild(container.view), content: [.mainContent: [container]])
+ self.init()
+ self = self.child(content)
}
}
diff --git a/Sources/Adwaita/View/StateWrapper.swift b/Sources/Adwaita/View/StateWrapper.swift
index aa2350d..317d011 100644
--- a/Sources/Adwaita/View/StateWrapper.swift
+++ b/Sources/Adwaita/View/StateWrapper.swift
@@ -5,8 +5,6 @@
// Created by david-swift on 26.09.23.
//
-import Libadwaita
-
/// A storage for `@State` properties.
public struct StateWrapper: Widget {
@@ -50,7 +48,7 @@ public struct StateWrapper: Widget {
/// - Returns: The view storage.
public func container(modifiers: [(View) -> View]) -> ViewStorage {
let content = content().widget(modifiers: modifiers).container(modifiers: modifiers)
- return .init(content.view, content: [.mainContent: [content]], state: state)
+ return .init(content.pointer, content: [.mainContent: [content]], state: state)
}
}
diff --git a/Sources/Adwaita/View/StatusPage+.swift b/Sources/Adwaita/View/StatusPage+.swift
new file mode 100644
index 0000000..472e9ae
--- /dev/null
+++ b/Sources/Adwaita/View/StatusPage+.swift
@@ -0,0 +1,29 @@
+//
+// StatusPage+.swift
+// Adwaita
+//
+// Created by david-swift on 17.01.23.
+//
+
+extension StatusPage {
+
+ /// Initialize a status page widget.
+ /// - Parameters:
+ /// - title: The title.
+ /// - icon: The icon.
+ /// - description: Additional details.
+ /// - content: Additional content.
+ public init(
+ _ title: String,
+ icon: Icon? = nil,
+ description: String = "",
+ @ViewBuilder content: @escaping () -> Body = { [] }
+ ) {
+ self.init()
+ self = self.title(title)
+ self = self.description(description)
+ self = self.iconName(icon?.string ?? "")
+ self = self.child(content)
+ }
+
+}
diff --git a/Sources/Adwaita/View/StatusPage.swift b/Sources/Adwaita/View/StatusPage.swift
deleted file mode 100644
index 9b598df..0000000
--- a/Sources/Adwaita/View/StatusPage.swift
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-// StatusPage.swift
-// Adwaita
-//
-// Created by david-swift on 25.09.23.
-//
-
-import Libadwaita
-
-/// A status page widget.
-public struct StatusPage: Widget {
-
- /// The title.
- var title: String
- /// The description.
- var description: String
- /// The icon.
- var icon: Icon
- /// Additional content.
- var content: Body
-
- /// Initialize a status page widget.
- /// - Parameters:
- /// - title: The title.
- /// - icon: The icon.
- /// - description: Additional details.
- /// - content: Additional content.
- public init(
- _ title: String,
- icon: Icon? = nil,
- description: String = "",
- @ViewBuilder content: () -> Body = { [] }
- ) {
- self.title = title
- self.description = description
- self.icon = icon ?? .custom(name: "")
- self.content = content()
- }
-
- /// Update the view storage of the text widget.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let statusPage = storage.view as? Libadwaita.StatusPage {
- _ = statusPage.title(title).description(description).icon(icon)
- }
- if let storage = storage.content[.mainContent]?.first {
- content.widget(modifiers: modifiers).update(storage, modifiers: modifiers)
- }
- }
-
- /// Get the container of the text widget.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let child = content.widget(modifiers: []).container(modifiers: modifiers)
- return .init(
- Libadwaita.StatusPage().title(title).description(description).icon(icon).child(child.view),
- content: [.mainContent: [child]]
- )
- }
-
-}
diff --git a/Sources/Adwaita/View/Text.swift b/Sources/Adwaita/View/Text.swift
index ee6e5d8..f723212 100644
--- a/Sources/Adwaita/View/Text.swift
+++ b/Sources/Adwaita/View/Text.swift
@@ -5,46 +5,15 @@
// Created by david-swift on 23.08.23.
//
-import Libadwaita
-
/// A text widget.
-public struct Text: Widget {
+public typealias Text = Label
- /// The content.
- var text: String
- /// Whether line wrapping is allowed.
- var lineWrapping = false
+extension Text {
/// Initialize a text widget.
/// - Parameter text: The content.
public init(_ text: String) {
- self.text = text
- }
-
- /// Update the view storage of the text widget.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let label = storage.view as? MarkupLabel {
- label.setText(text)
- _ = label.wrap(lineWrapping)
- }
- }
-
- /// Get the container of the text widget.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- .init(MarkupLabel(self.text).wrap(lineWrapping))
- }
-
- /// Line wrapping allows the text view to span multiple lines if the width is narrow.
- /// - Parameter wrap: Whether to allow line wrapping.
- /// - Returns: The text.
- public func wrap(_ wrap: Bool = true) -> Self {
- var newSelf = self
- newSelf.lineWrapping = wrap
- return newSelf
+ self.init(label: text)
}
}
diff --git a/Sources/Adwaita/View/Toggle.swift b/Sources/Adwaita/View/Toggle.swift
index 2660c08..5a6112a 100644
--- a/Sources/Adwaita/View/Toggle.swift
+++ b/Sources/Adwaita/View/Toggle.swift
@@ -5,19 +5,11 @@
// Created by david-swift on 19.12.23.
//
-import Libadwaita
-
/// A toggle button widget.
-public struct Toggle: Widget {
+public typealias Toggle = ToggleButton
- /// The button's label.
- var label: String?
- /// The button's icon.
- var icon: Icon?
- /// Whether the toggle is on.
- @Binding var isOn: Bool
- /// Whether to use GtkCheckButton instead of GtkToggleButton
- var isCheckButton = false
+/// A toggle button widget.
+extension Toggle {
// swiftlint:disable function_default_parameter_at_end
/// Initialize a toggle button.
@@ -26,9 +18,12 @@ public struct Toggle: Widget {
/// - icon: The button's icon.
/// - isOn: Whether the toggle is on.
public init(_ label: String? = nil, icon: Icon? = nil, isOn: Binding) {
- self.label = label
- self.icon = icon
- self._isOn = isOn
+ self = self.child {
+ ButtonContent()
+ .label(label)
+ .iconName(icon?.string)
+ }
+ self.active = isOn
}
// swiftlint:enable function_default_parameter_at_end
@@ -38,68 +33,21 @@ public struct Toggle: Widget {
/// - isOn: Whether the toggle is on.
public init(_ label: String, isOn: Binding) {
self.label = label
- self._isOn = isOn
- }
-
- /// Update a toggle button's view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let toggle = storage.view as? Libadwaita.ToggleButton {
- updateState(toggle: toggle)
- } else if let toggle = storage.view as? Libadwaita.CheckButton {
- updateState(toggle: toggle)
- }
- }
-
- /// Get a button's view storage.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The button's view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- if isCheckButton {
- let toggle: Libadwaita.CheckButton = .init(label ?? "")
- updateState(toggle: toggle)
- _ = toggle.handler {
- self.isOn.toggle()
- }
- return .init(toggle)
- } else {
- let toggle: Libadwaita.ToggleButton = .init(label ?? "")
- updateState(toggle: toggle)
- _ = toggle.handler {
- self.isOn.toggle()
- }
- return .init(toggle)
- }
- }
-
- /// Update the toggle's state.
- /// - Parameter toggle: The toggle.
- func updateState(toggle: Libadwaita.ToggleButton) {
- if let icon {
- toggle.setLabel(icon: icon, label: label)
- } else if let label {
- toggle.setLabel(label)
- }
- toggle.setActive(isOn)
- }
-
- /// Update the check button's state.
- /// - Parameter toggle: The toggle.
- func updateState(toggle: Libadwaita.CheckButton) {
- if let label {
- toggle.setLabel(label)
- }
- toggle.setActive(isOn)
+ self.active = isOn
}
/// Use the check button style.
/// - Returns: The toggle.
- public func checkButton() -> Self {
- var newSelf = self
- newSelf.isCheckButton = true
- return newSelf
+ public func checkButton() -> CheckButton {
+ if let child {
+ return .init()
+ .active(active)
+ .child(child)
+ } else {
+ return .init()
+ .active(active)
+ .label(label)
+ }
}
}
diff --git a/Sources/Adwaita/View/VStack.swift b/Sources/Adwaita/View/VStack.swift
index ad0b7be..4018267 100644
--- a/Sources/Adwaita/View/VStack.swift
+++ b/Sources/Adwaita/View/VStack.swift
@@ -5,40 +5,27 @@
// Created by david-swift on 23.08.23.
//
-import Libadwaita
+import CAdw
/// A GtkBox equivalent.
-public struct VStack: Widget {
+public typealias VStack = Box
- /// The content.
- var content: () -> Body
+extension VStack {
/// Initialize a `VStack`.
/// - Parameter content: The view content.
public init(@ViewBuilder content: @escaping () -> Body) {
- self.content = content
+ self.init(horizontal: false, content: content)
}
- /// Update a view storage.
- /// - Parameters:
- /// - storage: The view storage.
- /// - modifiers: Modify views before being updated.
- public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- content().update(storage.content[.mainContent] ?? [], modifiers: modifiers)
- }
-
- /// Get a view storage.
- /// - Parameter modifiers: Modify views before being updated.
- /// - Returns: The view storage.
- public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let box: Box = .init(horizontal: false)
- var content: [ViewStorage] = []
- for element in self.content() {
- let widget = element.storage(modifiers: modifiers)
- _ = box.append(widget.view)
- content.append(widget)
+ init(horizontal: Bool, @ViewBuilder content: @escaping () -> Body) {
+ self.init(spacing: 0)
+ self = self.append(content)
+ if horizontal {
+ appearFunctions.append { storage in
+ gtk_orientable_set_orientation(storage.pointer, GTK_ORIENTATION_HORIZONTAL)
+ }
}
- return .init(box, content: [.mainContent: content])
}
}
diff --git a/Sources/Adwaita/View/ViewStack.swift b/Sources/Adwaita/View/ViewStack.swift
index 745d45a..2abdebc 100644
--- a/Sources/Adwaita/View/ViewStack.swift
+++ b/Sources/Adwaita/View/ViewStack.swift
@@ -5,7 +5,7 @@
// Created by david-swift on 30.12.23.
//
-import Libadwaita
+import CAdw
/// A widget holding multiple children but only displaying one.
public struct ViewStack: Widget {
@@ -43,8 +43,8 @@ public struct ViewStack: Widget {
/// - Parameter modifiers: Modify views before being updated.
/// - Returns: The stack's view storage.
public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let stack = Stack()
- let storage = ViewStorage(stack)
+ let stack = gtk_stack_new()
+ let storage = ViewStorage(.init(stack))
update(storage, modifiers: modifiers)
return storage
}
@@ -54,18 +54,19 @@ public struct ViewStack: Widget {
/// - storage: The view storage.
/// - modifiers: Modify views before being updated.
public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let stack = storage.view as? Stack {
- let widget = content.widget(modifiers: modifiers)
- if let view = storage.content[id.description]?.first {
- widget.updateStorage(view, modifiers: modifiers)
- } else {
- let view = widget.storage(modifiers: modifiers)
- _ = stack.append(view.view)
- storage.content[id.description] = [view]
- }
- if let visibleView = storage.content[id.description]?.first?.view {
- _ = stack.setVisible(visibleView, transition: visibleView.fields[.transition] as? Transition)
+ let widget = content.widget(modifiers: modifiers)
+ if let view = storage.content[id.description]?.first {
+ widget.updateStorage(view, modifiers: modifiers)
+ } else {
+ let view = widget.storage(modifiers: modifiers)
+ gtk_stack_add_named(storage.pointer, view.pointer?.cast(), id.description)
+ storage.content[id.description] = [view]
+ }
+ if let visibleView = storage.content[id.description]?.first {
+ if let transition = visibleView.fields[.transition] as? Transition {
+ gtk_stack_set_transition_type(storage.pointer, transition.cTransition)
}
+ gtk_stack_set_visible_child_name(storage.pointer, id.description)
}
}
diff --git a/Sources/Adwaita/View/ViewSwitcher.swift b/Sources/Adwaita/View/ViewSwitcher.swift
index f7fd1e8..36711bb 100644
--- a/Sources/Adwaita/View/ViewSwitcher.swift
+++ b/Sources/Adwaita/View/ViewSwitcher.swift
@@ -5,7 +5,7 @@
// Created by david-swift on 03.01.24.
//
-import Libadwaita
+import CAdw
/// A picker used for indicating multiple views.
///
@@ -27,18 +27,27 @@ public struct ViewSwitcher: Widget where Element: ViewSwitcherOption {
/// - Parameter modifiers: Modify views before being updated.
/// - Returns: The view storage.
public func container(modifiers: [(View) -> View]) -> ViewStorage {
- let switcher = Libadwaita.ViewSwitcher()
+ let switcher = ViewStorage(.init(adw_view_switcher_new()))
+ let stack = ViewStorage(.init(adw_view_stack_new()))
+ adw_view_switcher_set_stack(switcher.pointer, stack.pointer)
for option in Element.allCases {
- _ = switcher.addOption(title: option.title, icon: option.icon)
+ adw_view_stack_add_titled_with_icon(
+ stack.pointer,
+ gtk_label_new(""),
+ option.title,
+ option.title,
+ option.icon.string
+ )
}
- _ = switcher.onSelect {
- let selection = switcher.getSelection()
- if let element = Element(title: selection) {
- self.selection = element
+ stack.notify(name: "visible-child") {
+ if let title = adw_view_stack_get_visible_child_name(stack.pointer),
+ let option = Element(title: .init(cString: title)) {
+ selection = option
}
}
updateSwitcher(switcher: switcher)
- return .init(switcher)
+ switcher.fields["stack"] = stack
+ return switcher
}
/// Update a view switcher's view storage.
@@ -46,16 +55,18 @@ public struct ViewSwitcher: Widget where Element: ViewSwitcherOption {
/// - storage: The view storage.
/// - modifiers: Modify views before being updated.
public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let switcher = storage.view as? Libadwaita.ViewSwitcher {
- updateSwitcher(switcher: switcher)
- }
+ updateSwitcher(switcher: storage)
}
/// Update a view switcher's style and selection.
/// - Parameter switcher: The view switcher.
- func updateSwitcher(switcher: Libadwaita.ViewSwitcher) {
- _ = switcher.wideDesign(wide)
- switcher.select(title: selection.title)
+ func updateSwitcher(switcher: ViewStorage) {
+ adw_view_switcher_set_policy(
+ switcher.pointer,
+ wide ? ADW_VIEW_SWITCHER_POLICY_WIDE : ADW_VIEW_SWITCHER_POLICY_NARROW
+ )
+ let stack = adw_view_switcher_get_stack(switcher.pointer)
+ adw_view_stack_set_visible_child_name(stack, selection.title)
}
/// Set whether to use the wide design.
diff --git a/Sources/Adwaita/Window/AboutWindow.swift b/Sources/Adwaita/Window/AboutWindow.swift
index 3ce0243..8a08e8d 100644
--- a/Sources/Adwaita/Window/AboutWindow.swift
+++ b/Sources/Adwaita/Window/AboutWindow.swift
@@ -5,8 +5,8 @@
// Created by david-swift on 05.12.23.
//
+import CAdw
import Foundation
-import Libadwaita
/// A structure representing an about window.
public struct AboutWindow: WindowScene {
@@ -20,15 +20,17 @@ public struct AboutWindow: WindowScene {
/// The keyboard shortcuts on the app level.
public var appShortcuts: [String: (GTUIApp) -> Void] = [:]
/// The app's name.
- var appName: String
+ var appName: String?
/// The developer's name.
- var developer: String
+ var developer: String?
/// The app version.
- var version: String
+ var version: String?
/// The app icon.
var icon: Icon?
/// The app's website.
var website: URL?
+ /// The path to the app data file.
+ var path: URL?
/// Create a window type with a certain identifier and content.
/// - Parameters:
@@ -43,6 +45,15 @@ public struct AboutWindow: WindowScene {
self.version = version
}
+ /// Create a window type with a certain identifier and content.
+ /// - Parameters:
+ /// - id: The identifier.
+ /// - path: The path to the app data file.
+ public init(id: String, path: URL) {
+ self.id = id
+ self.path = path
+ }
+
/// Set the app icon.
/// - Parameter icon: The app icon.
/// - Returns: The window.
@@ -74,8 +85,13 @@ public struct AboutWindow: WindowScene {
/// Get the window.
/// - Parameter app: The application.
/// - Returns: The window.
- func createGTUIWindow(app: GTUIApp) -> Libadwaita.AboutWindow {
- let window = Libadwaita.AboutWindow()
+ func createGTUIWindow(app: GTUIApp) -> GTUIAboutWindow {
+ let window: GTUIAboutWindow
+ if let path {
+ window = .init(filePath: path.path)
+ } else {
+ window = .init()
+ }
updateAppShortcuts(app: app)
updateData(window: window)
window.show()
@@ -93,9 +109,11 @@ public struct AboutWindow: WindowScene {
/// Update the data for a window.
/// - Parameter window: The window.
- func updateData(window: Libadwaita.AboutWindow) {
- _ = window.generalData(title: appName, icon: icon ?? .custom(name: ""), developer: developer, version: version)
- if let website { _ = window.website(url: website.absoluteString) }
+ func updateData(window: GTUIAboutWindow) {
+ if let appName, let icon, let developer, let version {
+ window.generalData(title: appName, icon: icon, developer: developer, version: version)
+ }
+ if let website { window.website(url: website.absoluteString) }
}
}
diff --git a/Sources/Adwaita/Window/FileDialog.swift b/Sources/Adwaita/Window/FileDialog.swift
index d088f1a..89fad76 100644
--- a/Sources/Adwaita/Window/FileDialog.swift
+++ b/Sources/Adwaita/Window/FileDialog.swift
@@ -8,7 +8,6 @@
// swiftlint:disable discouraged_optional_collection
import Foundation
-import Libadwaita
/// A structure representing a file dialog window.
public struct FileDialog: WindowScene {
@@ -29,8 +28,6 @@ public struct FileDialog: WindowScene {
var initialName: String?
/// The accepted extensions for the file importer.
var extensions: [String]?
- /// Whether folders are accepted in the file importer.
- var folders = false
/// The closure to run when the import or export is successful.
var result: ((URL) -> Void)?
/// The closure to run when the import or export is not successful.
@@ -48,14 +45,12 @@ public struct FileDialog: WindowScene {
importer: String,
initialFolder: URL? = nil,
extensions: [String]? = nil,
- folders: Bool = false,
onOpen: @escaping (URL) -> Void,
onClose: @escaping () -> Void
) {
self.id = importer
self.initialFolder = initialFolder
self.extensions = extensions
- self.folders = folders
self.result = onOpen
self.cancel = onClose
}
@@ -86,7 +81,7 @@ public struct FileDialog: WindowScene {
/// - Parameter app: The application.
/// - Returns: The storage.
public func createWindow(app: GTUIApp) -> WindowStorage {
- let window = Libadwaita.FileDialog(nil)
+ let window = GTUIFileDialog()
let windowStorage = WindowStorage(id: id, window: window, view: nil)
windowStorage.parentID = parentID
update(window: window)
@@ -99,7 +94,7 @@ public struct FileDialog: WindowScene {
/// - app: The application.
public func update(_ storage: WindowStorage, app: GTUIApp) {
updateAppShortcuts(app: app)
- if let window = storage.window as? Libadwaita.FileDialog {
+ if let window = storage.window as? GTUIFileDialog {
update(window: window)
}
storage.destroy = true
@@ -107,13 +102,13 @@ public struct FileDialog: WindowScene {
/// Update the window.
/// - Parameter window: The window.
- func update(window: Libadwaita.FileDialog) {
+ func update(window: GTUIFileDialog) {
window.isImporter = importer
window.folder = initialFolder
if let initialName {
window.setInitialName(initialName)
}
- window.setExtensions(extensions, folders: folders)
+ window.setExtensions(extensions)
if let result {
window.onResult = result
}
diff --git a/Sources/Adwaita/Window/Window.swift b/Sources/Adwaita/Window/Window.swift
index e8e3060..403b584 100644
--- a/Sources/Adwaita/Window/Window.swift
+++ b/Sources/Adwaita/Window/Window.swift
@@ -8,7 +8,6 @@
// swiftlint:disable discouraged_optional_collection
import Foundation
-import Libadwaita
/// A structure representing an application window type.
///
@@ -58,7 +57,6 @@ public struct Window: WindowScene {
let windowStorage = WindowStorage(id: id, window: window, view: storage)
window.observeHide {
windowStorage.destroy = true
- return false
}
windowStorage.parentID = parentID
return windowStorage
@@ -80,7 +78,7 @@ public struct Window: WindowScene {
func getViewStorage(window: GTUIApplicationWindow) -> ViewStorage {
let content = content(window)
let storage = content.widget(modifiers: []).container(modifiers: [])
- window.setChild(storage.view)
+ window.setChild(storage.pointer)
window.setDefaultSize(width: defaultSize?.0, height: defaultSize?.1)
setProperties(window: window)
updateShortcuts(window: window)
@@ -139,7 +137,6 @@ public struct Window: WindowScene {
_ signal: Signal,
initialFolder: URL? = nil,
extensions: [String]? = nil,
- folders: Bool = false,
onOpen: @escaping (URL) -> Void,
onClose: @escaping () -> Void
) -> Scene {
@@ -151,7 +148,6 @@ public struct Window: WindowScene {
importer: signal.id.uuidString,
initialFolder: initialFolder,
extensions: extensions,
- folders: folders,
onOpen: onOpen,
onClose: onClose
)
diff --git a/Sources/CAdw/module.modulemap b/Sources/CAdw/module.modulemap
new file mode 100644
index 0000000..7c9ce0b
--- /dev/null
+++ b/Sources/CAdw/module.modulemap
@@ -0,0 +1,6 @@
+module CAdw [system] {
+ header "shim.h"
+ link "adwaita-1"
+ link "gtk-4"
+ export *
+}
\ No newline at end of file
diff --git a/Sources/CAdw/shim.h b/Sources/CAdw/shim.h
new file mode 100644
index 0000000..0ea21e3
--- /dev/null
+++ b/Sources/CAdw/shim.h
@@ -0,0 +1,37 @@
+#include
+#include
+
+static void
+filedialog_on_open_cb (void *, void *, void *);
+static void
+filedialog_on_save_cb (void *, void *, void *);
+
+static void
+gtui_filedialog_save_finish (uint64_t dialog, uint64_t result, uint64_t data)
+{
+ GFile *file = gtk_file_dialog_save_finish (dialog, result, NULL);
+ const char *path = g_file_get_path (file);
+ filedialog_on_save_cb (dialog, path, data);
+}
+
+static void
+gtui_filedialog_save (uint64_t dialog, uint64_t data, uint64_t window)
+{
+ swift_retain (data);
+ gtk_file_dialog_save (dialog, window, NULL, G_CALLBACK (gtui_filedialog_save_finish), (void *)data);
+}
+
+static void
+gtui_filedialog_open_finish (uint64_t dialog, uint64_t result, uint64_t data)
+{
+ GFile *file = gtk_file_dialog_open_finish (dialog, result, NULL);
+ const char *path = g_file_get_path (file);
+ filedialog_on_open_cb (dialog, path, data);
+}
+
+static void
+gtui_filedialog_open (uint64_t dialog, uint64_t data, uint64_t window)
+{
+ swift_retain (data);
+ gtk_file_dialog_open (dialog, window, NULL, G_CALLBACK (gtui_filedialog_open_finish), (void *)data);
+}
diff --git a/Sources/Generation/Extensions/String.swift b/Sources/Generation/Extensions/String.swift
new file mode 100644
index 0000000..5a9e791
--- /dev/null
+++ b/Sources/Generation/Extensions/String.swift
@@ -0,0 +1,80 @@
+//
+// String.swift
+// Adwaita
+//
+// Created by david-swift on 14.01.24.
+//
+
+extension String: CodingKey {
+
+ /// The string.
+ public var stringValue: String {
+ self
+ }
+
+ /// A string cannot be represented as an integer.
+ public var intValue: Int? {
+ nil
+ }
+
+ /// Initialize from an int value.
+ /// - Parameter intValue: The int value.
+ public init?(intValue: Int) {
+ nil
+ }
+
+ /// Initialize from a string value.
+ /// - Parameter stringValue: The string value.
+ public init?(stringValue: String) {
+ self = stringValue
+ }
+
+ /// Generate a doc comment out of the string.
+ /// - Parameter indent: Indentation added at the beginning of every line.
+ /// - Returns: The comment.
+ func docComment(indent: String = "") -> String {
+ split(separator: "\n", omittingEmptySubsequences: false)
+ .map { $0.trimmingCharacters(in: .whitespaces) }
+ .enumerated()
+ .map { $0.offset == 0 ? $0.element.prefix(1).capitalized + $0.element.dropFirst() : $0.element }
+ .map { "\(indent)/// \($0)" }
+ .joined(separator: "\n")
+ }
+
+ /// Convert delimited to camel casing.
+ /// - Parameters:
+ /// - delimiter: The demiliter.
+ /// - unshorten: Whether to unshorten.
+ /// - configuration: The generation configuration.
+ /// - Returns: The string using camel casing.
+ func convertDelimitedCasingToCamel(
+ delimiter: Character,
+ configuration: GenerationConfiguration,
+ unshorten: Bool = false
+ ) -> String {
+ var parts = split(separator: delimiter).map(String.init)
+ for (index, part) in parts.enumerated() {
+ if let replacement = configuration.unshorteningMap[part] {
+ parts[index] = replacement
+ }
+ }
+ let first = parts.removeFirst()
+ return first + parts.map(\.capitalized).joined()
+ }
+
+ /// Convert a C type to its Swift equivalent using the generation configuration.
+ /// - Parameter configuration: The generation configuration.
+ /// - Returns: The Swift type.
+ func convertCType(configuration: GenerationConfiguration) -> String {
+ if let replacement = configuration.cTypeReplacements[self] {
+ return replacement
+ }
+ var type = self
+ if type.last == "*" {
+ let pointeeType = String(type.dropLast()).convertCType(configuration: configuration)
+ type = "UnsafeMutablePointer<\(pointeeType)>!"
+ }
+ return type
+ }
+
+}
diff --git a/Sources/Generation/GIR/Class+.swift b/Sources/Generation/GIR/Class+.swift
new file mode 100644
index 0000000..2da2856
--- /dev/null
+++ b/Sources/Generation/GIR/Class+.swift
@@ -0,0 +1,304 @@
+//
+// Class+.swift
+// Adwaita
+//
+// Created by david-swift on 22.01.24.
+//
+
+extension Class {
+
+ /// Generate the Swift initializer.
+ /// - Parameters:
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - classes: The available classes.
+ /// - configs: The available widget configurations.
+ /// - Returns: The code.
+ func generateAdwaitaInitializer(
+ config: WidgetConfiguration,
+ genConfig: GenerationConfiguration,
+ classes: [Class],
+ configs: [WidgetConfiguration]
+ ) -> String {
+ let requiredProperties = properties(classes: classes, configurations: configs)
+ .filter { config.requiredProperties.contains($0.name) }
+ var initializer = "public init("
+ if config.dynamicWidget != nil {
+ initializer.append("_ elements: [Element], ")
+ }
+ for property in requiredProperties {
+ initializer.append("\(property.parameter(config: config, genConfig: genConfig)), ")
+ }
+ if config.dynamicWidget != nil {
+ initializer.append("@ViewBuilder content: @escaping (Element) -> Body, ")
+ }
+ if !requiredProperties.isEmpty || config.dynamicWidget != nil {
+ initializer.removeLast(", ".count)
+ }
+ initializer.append(") {")
+ for property in requiredProperties {
+ let name = property.convertPropertyName(configuration: genConfig)
+ initializer.append("\n self.\(name) = \(name)")
+ }
+ if config.dynamicWidget != nil {
+ initializer.append(
+ """
+
+ self.elements = elements
+ self.content = content
+ """
+ )
+ }
+ initializer.append("\n }")
+ return initializer
+ }
+
+ /// Generate the call of the C initializer.
+ /// - Parameters:
+ /// - name: The class name.
+ /// - config: The widget configuration.
+ /// - classes: The available classes.
+ /// - configs: The available widget configurations.
+ /// - Returns: The code.
+ func generateInitializer(
+ name: String,
+ config: WidgetConfiguration,
+ classes: [Class],
+ configs: [WidgetConfiguration]
+ ) -> String {
+ if let initializer = config.initializer {
+ return initializer
+ }
+ if let initializer = constructors.first(where: { ($0.parameters?.parameters.count ?? 0) == 0 }) {
+ return initializer.cIdentifier + "()"
+ }
+ // swiftlint:disable fatal_error
+ fatalError("No initializer with no parameters for \(name). Configure manually.")
+ // swiftlint:enable fatal_error
+ }
+
+ /// Generate the assignments for the widgets and menus.
+ /// - Parameters:
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - classes: The available classes.
+ /// - configs: The available widget configurations.
+ /// - Returns: The code.
+ func generateWidgetAssignments(
+ config: WidgetConfiguration,
+ genConfig: GenerationConfiguration,
+ classes: [Class],
+ configs: [WidgetConfiguration]
+ ) -> String {
+ var content = ""
+ for property in properties(classes: classes, configurations: configs)
+ where !config.excludeProperties.contains(property.name) && (property.type?.isWidget ?? false) {
+ content += property.generateWidgetAssignment(prefix: prefix(), config: config, genConfig: genConfig)
+ }
+ for property in properties(classes: classes, configurations: configs)
+ where !config.excludeProperties.contains(property.name) && (property.type?.isMenu ?? false) {
+ content += property.generateMenuAssignment(prefix: prefix(), config: config, genConfig: genConfig)
+ }
+ content += staticWidgets(classes: classes, configs: configs)
+ return content
+ }
+
+ /// Generate the assignments for bindings.
+ /// - Parameters:
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - classes: The available classes.
+ /// - configs: The available widget configurations.
+ /// - Returns: The code.
+ func generateBindingAssignments(
+ config: WidgetConfiguration,
+ genConfig: GenerationConfiguration,
+ classes: [Class],
+ configs: [WidgetConfiguration]
+ ) -> String {
+ var content = ""
+ for property in properties(classes: classes, configurations: configs) {
+ if let binding = config.bindings.first(where: { $0.property == property.name }) {
+ content += property.generateBindingAssignment(
+ prefix: prefix(),
+ signal: binding.signal,
+ config: config,
+ genConfig: genConfig
+ )
+ }
+ }
+ return content
+ }
+
+ /// Generate the properties.
+ /// - Parameters:
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - classes: The available classes.
+ /// - configs: The available widget configurations.
+ /// - Returns: The code.
+ func generateProperties(
+ config: WidgetConfiguration,
+ genConfig: GenerationConfiguration,
+ classes: [Class],
+ configs: [WidgetConfiguration]
+ ) -> String {
+ var content = ""
+ for property in properties(classes: classes, configurations: configs)
+ where !config.excludeProperties.contains(property.name) || config.requiredProperties.contains(property.name) {
+ content += property.generate(config: config, genConfig: genConfig)
+ }
+ for signal in signals(classes: classes) where !config.excludeSignals.contains(signal.name) {
+ content += signal.generateProperty(config: config, genConfig: genConfig)
+ }
+ if config.dynamicWidget != nil {
+ content += """
+
+ /// The dynamic widget elements.
+ var elements: [Element]
+ /// The dynamic widget content.
+ var content: (Element) -> Body
+ """
+ }
+ content += staticWidgetProperties(classes: classes, configs: configs)
+ content += """
+
+ /// The application.
+ var app: GTUIApp?
+ /// The window.
+ var window: GTUIApplicationWindow?
+ """
+ return content
+ }
+
+ /// Generate the property modifications for updating.
+ /// - Parameters:
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - classes: The available classes.
+ /// - configs: The available widget configurations.
+ /// - Returns: The code.
+ func generateModifications(
+ config: WidgetConfiguration,
+ genConfig: GenerationConfiguration,
+ classes: [Class],
+ configs: [WidgetConfiguration]
+ ) -> String {
+ var content = ""
+ for property in properties(classes: classes, configurations: configs)
+ where !config.excludeProperties.contains(property.name) {
+ content += property.generateModification(config: config, genConfig: genConfig, prefix: prefix())
+ }
+ for widget in config.staticWidgets {
+ content += """
+
+ if let \(widget.name)Storage = storage.content["\(widget.name)"] {
+ for (index, view) in \(widget.name)().enumerated() {
+ if let storage = \(widget.name)Storage[safe: index] {
+ view.updateStorage(storage, modifiers: modifiers)
+ }
+ }
+ }
+ """
+ }
+ return content
+ }
+
+ /// Generate the content of the update closure for dynamic widgets.
+ /// - Parameters:
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - Returns: The code.
+ func generateDynamicWidgetUpdate(config: WidgetConfiguration, genConfig: GenerationConfiguration) -> String {
+ let child = "let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers)"
+ let pointer = "child.pointer?.cast()"
+ let widget = "widget" + (config.cast ? "?.cast()" : "")
+ if let dynamicWidget = config.dynamicWidget {
+ // swiftlint:disable line_length
+ return """
+ var contentStorage: [ViewStorage] = storage.content[.mainContent] ?? []
+ let old = storage.fields["element"] as? [Element] ?? []
+ old.identifiableTransform(
+ to: elements,
+ functions: .init { index, element in
+ \(child)
+ \(dynamicWidget.remove)(\(widget), \(dynamicWidget.getElement))
+ \(dynamicWidget.insert)(\(widget), \(pointer), index.cInt)
+ contentStorage.remove(at: index)
+ contentStorage.insert(child, at: index)
+ } delete: { index in
+ \(dynamicWidget.remove)(\(widget), \(dynamicWidget.getElement))
+ contentStorage.remove(at: index)
+ } insert: { index, element in
+ \(child)
+ \(dynamicWidget.insert)(\(widget), \(pointer), index.cInt)
+ contentStorage.insert(child, at: index)
+ }
+ )
+ storage.fields["element"] = elements
+ storage.content[.mainContent] = contentStorage
+ for (index, element) in elements.enumerated() {
+ content(element).widget(modifiers: modifiers).update(contentStorage[index], modifiers: modifiers)
+ }
+ """
+ // swiftlint:enable line_length
+ } else {
+ return ""
+ }
+ }
+
+ /// Generate the modifications for the signals.
+ /// - Parameters:
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - classes: The available classes.
+ /// - Returns: The code.
+ func generateSignalModifications(
+ config: WidgetConfiguration,
+ genConfig: GenerationConfiguration,
+ classes: [Class]
+ ) -> String {
+ var content = ""
+ for signal in signals(classes: classes) where !config.excludeSignals.contains(signal.name) {
+ content += signal.generateModification(config: config, genConfig: genConfig)
+ }
+ return content
+ }
+
+ /// Generate the modifiers.
+ /// - Parameters:
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - classes: The available classes.
+ /// - configs: The available widget configurations.
+ /// - Returns: The code.
+ func generateModifiers(
+ config: WidgetConfiguration,
+ genConfig: GenerationConfiguration,
+ classes: [Class],
+ configs: [WidgetConfiguration]
+ ) -> String {
+ var content = ""
+ for property in properties(classes: classes, configurations: configs)
+ where !config.excludeProperties.contains(property.name) {
+ content += property.generateModifier(config: config, genConfig: genConfig)
+ }
+ for signal in signals(classes: classes) where !config.excludeSignals.contains(signal.name) {
+ content += signal.generateModifier(config: config, genConfig: genConfig)
+ }
+ for widget in config.staticWidgets {
+ content += """
+
+ /// Set the body for "\(widget.name)".
+ /// - Parameter body: The body.
+ /// - Returns: The widget.
+ public func \(widget.name)(@ViewBuilder _ body: @escaping () -> Body) -> Self {
+ var newSelf = self
+ newSelf.\(widget.name) = body
+ return newSelf
+ }
+ """
+ }
+ return content
+ }
+}
diff --git a/Sources/Generation/GIR/Class.swift b/Sources/Generation/GIR/Class.swift
new file mode 100644
index 0000000..3932b16
--- /dev/null
+++ b/Sources/Generation/GIR/Class.swift
@@ -0,0 +1,227 @@
+//
+// Class.swift
+// Adwaita
+//
+// Created by david-swift on 14.01.24.
+//
+
+import Foundation
+
+/// A class.
+struct Class: Decodable {
+
+ /// The name of the class.
+ var name: String
+ /// The prefix for C symbols.
+ var cSymbolPrefix: String
+ /// The type in C.
+ var cType: String?
+ /// The parent class.
+ var parent: String?
+
+ /// The doc string.
+ var doc: String
+ /// The available initializers.
+ var constructors: [Constructor]
+ /// The available properties.
+ var properties: [Property]
+ /// The available signals (callbacks).
+ var signals: [Signal]
+
+ /// The coding keys for the class
+ enum CodingKeys: String, CodingKey {
+
+ /// Coding key
+ case name, cSymbolPrefix, cType, parent, doc
+ /// Coding key
+ case constructors = "constructor"
+ /// Coding key
+ case properties = "property"
+ /// Coding key
+ case signals = "glibSignal"
+
+ }
+
+ /// Get the properties of the class and its parent classes.
+ /// - Parameters:
+ /// - classes: The classes in the namespace.
+ /// - configurations: The configurations for the classes in the namespace.
+ /// - Returns: The properties.
+ func properties(classes: [Self], configurations: [WidgetConfiguration]) -> [Property] {
+ if let parentClass = parentClass(classes: classes) {
+ return properties + parentClass
+ .properties(classes: classes, configurations: configurations)
+ .map { property in
+ var property = property
+ if property.prefix == nil {
+ property.prefix = parentClass.prefix()
+ property.cast = configurations.first { $0.class == parentClass.name }?.cast ?? false
+ }
+ return property
+ }
+ }
+ return properties
+ }
+
+ /// Get the signals of the class and its parent classes.
+ /// - Parameter classes: The classes in the namespace.
+ /// - Returns: The signals.
+ func signals(classes: [Self]) -> [Signal] {
+ if let parentClass = parentClass(classes: classes) {
+ return signals + parentClass.signals(classes: classes)
+ }
+ return signals
+ }
+
+ /// Get the assignments for the static widgets of the class and its parent classes.
+ /// - Parameters:
+ /// - classes: The classes in the namespace.
+ /// - configs: The configurations.
+ /// - Returns: The code.
+ func staticWidgets(classes: [Self], configs: [WidgetConfiguration]) -> String {
+ var content = ""
+ if let parentClass = parentClass(classes: classes) {
+ content += parentClass.staticWidgets(classes: classes, configs: configs)
+ }
+ guard let config = configs.first(where: { $0.class == name }) else {
+ return content
+ }
+ let widgetPointer = config.cast ? "storage.pointer?.cast()" : "storage.pointer"
+ for widget in config.staticWidgets {
+ content += """
+
+ var \(widget.name)Storage: [ViewStorage] = []
+ for view in \(widget.name)() {
+ \(widget.name)Storage.append(view.storage(modifiers: modifiers))
+ \(widget.add)(\(widgetPointer), \(widget.name)Storage.last?.pointer?.cast())
+ }
+ storage.content["\(widget.name)"] = \(widget.name)Storage
+ """
+ }
+ return content
+ }
+
+ /// Get the code for the properties of the static widgets.
+ /// - Parameters:
+ /// - classes: The classes in the namespace.
+ /// - configs: The configurations.
+ /// - Returns: The code.
+ func staticWidgetProperties(classes: [Self], configs: [WidgetConfiguration]) -> String {
+ var content = ""
+ if let parentClass = parentClass(classes: classes) {
+ content += parentClass.staticWidgetProperties(classes: classes, configs: configs)
+ }
+ guard let config = configs.first(where: { $0.class == name }) else {
+ return content
+ }
+ for staticWidget in config.staticWidgets {
+ content += """
+
+ /// The body for the widget "\(staticWidget.name)".
+ var \(staticWidget.name): () -> Body = { [] }
+ """
+ }
+ return content
+ }
+
+ /// Get the parent class.
+ /// - Parameter classes: The classes in the namespace.
+ /// - Returns: The class.
+ func parentClass(classes: [Self]) -> Self? {
+ if let parent = classes.first(where: { $0.name == parent }), parent.name != "Widget" {
+ return parent
+ }
+ return nil
+ }
+
+ /// Get the widget's prefix (e.g. "adw_preferences_row").
+ /// - Returns: The prefix.
+ func prefix() -> String {
+ if cType?.hasPrefix("Adw") ?? false {
+ return "adw_\(cSymbolPrefix)"
+ } else {
+ return "gtk_\(cSymbolPrefix)"
+ }
+ }
+
+ // swiftlint:disable function_body_length line_length
+ /// Generate the code for the class.
+ /// - Parameters:
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - classes: The available classes.
+ /// - configs: The available widget configurations.
+ /// - Returns: The code.
+ func generate(
+ config: WidgetConfiguration,
+ genConfig: GenerationConfiguration,
+ classes: [Self],
+ configs: [WidgetConfiguration]
+ ) -> String {
+ let dateFormatter = DateFormatter()
+ dateFormatter.dateFormat = "dd.MM.yy"
+ let widgetName = config.name ?? config.class
+ let definition: String
+ if config.dynamicWidget == nil {
+ definition = "\(widgetName): Widget"
+ } else {
+ definition = "\(widgetName): Widget where Element: Identifiable"
+ }
+ return """
+ //
+ // \(widgetName).swift
+ // Adwaita
+ //
+ // Created by auto-generation on \(dateFormatter.string(from: .now)).
+ //
+
+ import CAdw
+ import LevenshteinTransformations
+
+ \(doc.docComment())
+ public struct \(definition) {
+
+ /// Additional update functions for type extensions.
+ var updateFunctions: [(ViewStorage) -> Void] = []
+ /// Additional appear functions for type extensions.
+ var appearFunctions: [(ViewStorage) -> Void] = []
+ \(generateProperties(config: config, genConfig: genConfig, classes: classes, configs: configs))
+
+ /// Initialize `\(widgetName)`.
+ \(generateAdwaitaInitializer(config: config, genConfig: genConfig, classes: classes, configs: configs))
+
+ /// Get the widget's view storage.
+ /// - Parameter modifiers: The view modifiers.
+ /// - Returns: The view storage.
+ public func container(modifiers: [(View) -> View]) -> ViewStorage {
+ let storage = ViewStorage(\(generateInitializer(name: widgetName, config: config, classes: classes, configs: configs))?.opaque())
+ update(storage, modifiers: modifiers)
+ \(generateWidgetAssignments(config: config, genConfig: genConfig, classes: classes, configs: configs))
+ \(generateBindingAssignments(config: config, genConfig: genConfig, classes: classes, configs: configs))
+ for function in appearFunctions {
+ function(storage)
+ }
+ return storage
+ }
+
+ /// Update the widget's view storage.
+ /// - Parameters:
+ /// - storage: The view storage.
+ /// - modifiers: The view modifiers.
+ public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {\(generateSignalModifications(config: config, genConfig: genConfig, classes: classes))
+ storage.modify { widget in
+ \(generateModifications(config: config, genConfig: genConfig, classes: classes, configs: configs))
+ \(generateDynamicWidgetUpdate(config: config, genConfig: genConfig))
+ }
+ for function in updateFunctions {
+ function(storage)
+ }
+ }
+ \(generateModifiers(config: config, genConfig: genConfig, classes: classes, configs: configs))
+ }
+
+ """
+ }
+ // swiftlint:enable function_body_length line_length
+
+}
diff --git a/Sources/Generation/GIR/Constructor.swift b/Sources/Generation/GIR/Constructor.swift
new file mode 100644
index 0000000..c1d3e11
--- /dev/null
+++ b/Sources/Generation/GIR/Constructor.swift
@@ -0,0 +1,16 @@
+//
+// Constructor.swift
+// Adwaita
+//
+// Created by david-swift on 14.01.24.
+//
+
+/// An initializer.
+struct Constructor: Decodable {
+
+ /// The identifier of the C constructor.
+ var cIdentifier: String
+ /// The parameters.
+ var parameters: Parameters?
+
+}
diff --git a/Sources/Generation/GIR/GIR.swift b/Sources/Generation/GIR/GIR.swift
new file mode 100644
index 0000000..4820c43
--- /dev/null
+++ b/Sources/Generation/GIR/GIR.swift
@@ -0,0 +1,60 @@
+//
+// GIR.swift
+// Adwaita
+//
+// Created by david-swift on 14.01.24.
+//
+
+import Foundation
+import XMLCoder
+
+/// The result of decoding a GIR file.
+struct GIR: Decodable {
+
+ /// The namespace.
+ var namespace: Namespace
+
+ /// Decode a GIR file.
+ /// - Returns: The GIR data.
+ static func decodeGIR(_ data: Data) throws -> Self {
+ let decoder = XMLDecoder()
+ decoder.keyDecodingStrategy = .custom { path in
+ let codingKey = path[path.count - 1]
+ let containsColon = codingKey.stringValue.contains(":")
+ let containsHyphen = codingKey.stringValue.contains("-")
+ if containsColon || containsHyphen {
+ var input = codingKey.stringValue
+ var output = ""
+
+ // Remove namespace
+ if containsColon {
+ let parts = input.split(separator: ":").map(String.init)
+ output = parts[0]
+ input = parts[1]
+ }
+
+ // Convert kebab-case to camelCase
+ if containsHyphen {
+ var parts = input.split(separator: "-")
+ let firstPart = String(parts.removeFirst())
+ if containsColon {
+ output += firstPart.capitalized
+ } else {
+ output += firstPart
+ }
+
+ for part in parts {
+ output += part.capitalized
+ }
+ } else {
+ output += input.capitalized
+ }
+ return output
+ } else {
+ return codingKey
+ }
+ }
+ return try decoder.decode(Self.self, from: data)
+ }
+
+}
diff --git a/Sources/Generation/GIR/GIRType.swift b/Sources/Generation/GIR/GIRType.swift
new file mode 100644
index 0000000..95659b9
--- /dev/null
+++ b/Sources/Generation/GIR/GIRType.swift
@@ -0,0 +1,38 @@
+//
+// GIRType.swift
+// Adwaita
+//
+// Created by david-swift on 14.01.24.
+//
+
+/// A GIR type.
+struct GIRType: Decodable {
+
+ /// The type's name.
+ var name: String?
+ /// The C type.
+ var cType: String?
+
+ /// Whether the type is `GtkWidget`.
+ var isWidget: Bool { name == "Widget" || name == "Gtk.Widget" }
+ /// Whether the type is `GioMenuModel`.
+ var isMenu: Bool { name == "MenuModel" || name == "Gio.MenuModel" }
+
+ /// Generate the Swift representation of the type.
+ /// - Parameter configuration: The generation configuration.
+ /// - Returns: The code.
+ func generate(configuration: GenerationConfiguration) throws -> String {
+ if let cType {
+ return cType.convertCType(configuration: configuration)
+ }
+ return "String"
+ }
+
+ /// Generate the Swift type as a binding.
+ /// - Parameter configuration: The generation configuration.
+ /// - Returns: The code.
+ func binding(configuration: GenerationConfiguration) throws -> String {
+ "Binding<\(try generate(configuration: configuration))>"
+ }
+
+}
diff --git a/Sources/Generation/GIR/Namespace.swift b/Sources/Generation/GIR/Namespace.swift
new file mode 100644
index 0000000..5bfac5d
--- /dev/null
+++ b/Sources/Generation/GIR/Namespace.swift
@@ -0,0 +1,22 @@
+//
+// Namespace.swift
+// Adwaita
+//
+// Created by david-swift on 14.01.24.
+//
+
+/// A namespace.
+struct Namespace: Decodable {
+
+ /// The classes.
+ var classes: [Class]
+
+ /// The coding keys.
+ enum CodingKeys: String, CodingKey {
+
+ /// Coding key.
+ case classes = "class"
+
+ }
+
+}
diff --git a/Sources/Generation/GIR/Parameter.swift b/Sources/Generation/GIR/Parameter.swift
new file mode 100644
index 0000000..f5f2551
--- /dev/null
+++ b/Sources/Generation/GIR/Parameter.swift
@@ -0,0 +1,9 @@
+//
+// Parameter.swift
+// Adwaita
+//
+// Created by david-swift on 14.01.24.
+//
+
+/// A parameter.
+struct Parameter: Decodable { }
diff --git a/Sources/Generation/GIR/Parameters.swift b/Sources/Generation/GIR/Parameters.swift
new file mode 100644
index 0000000..2751a02
--- /dev/null
+++ b/Sources/Generation/GIR/Parameters.swift
@@ -0,0 +1,22 @@
+//
+// Parameters.swift
+// Adwaita
+//
+// Created by david-swift on 14.01.24.
+//
+
+/// Parameters.
+struct Parameters: Decodable {
+
+ /// The parameters.
+ var parameters: [Parameter]
+
+ /// The coding keys.
+ enum CodingKeys: String, CodingKey {
+
+ /// Coding key.
+ case parameters = "parameter"
+
+ }
+
+}
diff --git a/Sources/Generation/GIR/Property.swift b/Sources/Generation/GIR/Property.swift
new file mode 100644
index 0000000..390df9d
--- /dev/null
+++ b/Sources/Generation/GIR/Property.swift
@@ -0,0 +1,268 @@
+//
+// Property.swift
+// Adwaita
+//
+// Created by david-swift on 14.01.24.
+//
+
+/// A property.
+struct Property: Decodable {
+
+ /// The property's name.
+ var name: String
+ /// The docs.
+ var doc: String?
+ /// The property's getter.
+ var getter: String?
+ /// The property's setter.
+ var setter: String?
+ /// The property's type.
+ var type: GIRType?
+ /// This overwrites the prefix provided by the caller of a function.
+ var prefix: String?
+ // swiftlint:disable discouraged_optional_boolean
+ /// This overwrites the cast information provided by the caller of a function.
+ var cast: Bool?
+ // swiftlint:enable discouraged_optional_boolean
+
+ /// Get the Swift property name.
+ /// - Parameter configuration: The generation configuration.
+ /// - Returns: The name.
+ func convertPropertyName(configuration: GenerationConfiguration) -> String {
+ name.convertDelimitedCasingToCamel(delimiter: "-", configuration: configuration, unshorten: true)
+ }
+
+ /// Get the property's name and type for parameters or as part of a property definition.
+ /// - Parameters:
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - modifier: Whether it is used as a parameter.
+ /// - defaultValue: Whether to set the default value.
+ /// - Returns: The code.
+ func parameter(
+ config: WidgetConfiguration,
+ genConfig: GenerationConfiguration,
+ modifier: Bool = false,
+ defaultValue: Bool = false
+ ) -> String {
+ var type: String
+ if config.bindings.contains { $0.property == name } {
+ type = (try? self.type?.binding(configuration: genConfig)) ?? "Binding"
+ } else {
+ type = (try? self.type?.generate(configuration: genConfig)) ?? "String"
+ }
+ if self.type?.isWidget ?? false {
+ type = "\(modifier ? "@escaping" : "") (() -> Body)"
+ } else if self.type?.isMenu ?? false {
+ type = "\(modifier ? "@escaping" : "") (() -> MenuContent)"
+ }
+ if !config.requiredProperties.contains(name)
+ && !(((self.type?.isWidget ?? false) || (self.type?.isMenu ?? false)) && modifier) {
+ type += "?"
+ }
+ if defaultValue, let value = genConfig.defaultModifierValues[type] {
+ type += " = \(value)"
+ }
+ return "\(convertPropertyName(configuration: genConfig)): \(type)"
+ }
+
+ /// Generate the property as a Swift property.
+ /// - Parameters:
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - Returns: The code.
+ func generate(config: WidgetConfiguration, genConfig: GenerationConfiguration) -> String {
+ """
+
+ \(doc?.docComment(indent: " ") ?? "/// \(name)")
+ var \(parameter(config: config, genConfig: genConfig))
+ """
+ }
+
+ // swiftlint:disable line_length
+ /// Generate the property's modifier.
+ /// - Parameters:
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - Returns: The code.
+ func generateModifier(config: WidgetConfiguration, genConfig: GenerationConfiguration) -> String {
+ let property = convertPropertyName(configuration: genConfig)
+ let builder = (type?.isWidget ?? false) ? "@ViewBuilder " : ((type?.isMenu ?? false) ? "@MenuBuilder " : "")
+ let mainParameter = parameter(config: config, genConfig: genConfig, modifier: true, defaultValue: true)
+ var sideParameters = ""
+ var sideAssignments = ""
+ if type?.isMenu ?? false {
+ sideParameters += "app: GTUIApp, window: GTUIApplicationWindow? = nil, "
+ sideAssignments += "newSelf.app = app; newSelf.window = window"
+ }
+ return """
+
+ \(doc?.docComment(indent: " ") ?? "/// \(name)")
+ public func \(convertPropertyName(configuration: genConfig))(\(sideParameters)\(builder)_ \(mainParameter)) -> Self {
+ var newSelf = self
+ newSelf.\(property) = \(property)
+ \(sideAssignments)
+ return newSelf
+ }
+
+ """
+ }
+
+ /// Generate the property's modification when being updated.
+ /// - Parameters:
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - prefix: The widget's prefix.
+ /// - Returns: The code.
+ func generateModification(
+ config: WidgetConfiguration,
+ genConfig: GenerationConfiguration,
+ prefix: String
+ ) -> String {
+ let name = convertPropertyName(configuration: genConfig)
+ guard !(type?.isWidget ?? false) else {
+ return """
+ if let widget = storage.content["\(name)"]?.first {
+ \(name)?().widget(modifiers: modifiers).update(widget, modifiers: modifiers)
+ }
+
+ """
+ }
+ guard !(type?.isMenu ?? false) else {
+ return ""
+ }
+ guard var setter else {
+ return ""
+ }
+ setter = (self.prefix ?? prefix) + "_" + setter
+ let type = try? type?.generate(configuration: genConfig)
+ let cProperty = genConfig.cTypeProperties.first { $0.key == type }
+ let propertyString = cProperty == nil ? "" : ".\(cProperty?.value ?? "")"
+ let widget = (self.cast ?? config.cast) ? "widget?.cast()" : "widget"
+ var setConditions = ""
+ var onlySetConditions = ""
+ var onlySetConditionsIndentation = ""
+ var onlySetConditionsEnd = ""
+ if let conditions = config.setConditions[name] {
+ setConditions = ", \(conditions)"
+ onlySetConditions = "if \(conditions) {\n "
+ onlySetConditionsIndentation = " "
+ onlySetConditionsEnd = "\n }"
+ }
+ if config.bindings.contains { $0.property == self.name } {
+ return """
+ if let \(name)\(setConditions) {
+ \(setter)(\(widget), \(name).wrappedValue\(propertyString))
+ }
+
+ """
+ } else if config.requiredProperties.contains(self.name) {
+ return """
+ \(onlySetConditions)\(onlySetConditionsIndentation)\(setter)(\(widget), \(name)\(propertyString))\(onlySetConditionsEnd)
+
+ """
+ } else {
+ return """
+ if let \(name)\(setConditions) {
+ \(setter)(\(widget), \(name)\(propertyString))
+ }
+
+ """
+ }
+ }
+ // swiftlint:enable line_length
+
+ /// Generate the widget assignments.
+ /// - Parameters:
+ /// - prefix: The widget's prefix.
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - Returns: The code.
+ func generateWidgetAssignment(
+ prefix: String,
+ config: WidgetConfiguration,
+ genConfig: GenerationConfiguration
+ ) -> String {
+ guard var setter else {
+ return ""
+ }
+ setter = (self.prefix ?? prefix) + "_" + setter
+ let name = convertPropertyName(configuration: genConfig)
+ let view = (self.cast ?? config.cast) ? "storage.pointer?.cast()" : "storage.pointer"
+ return """
+ if let \(name)Storage = \(name)?().widget(modifiers: modifiers).storage(modifiers: modifiers) {
+ storage.content["\(name)"] = [\(name)Storage]
+ \(setter)(\(view), \(name)Storage.pointer?.cast())
+ }
+
+ """
+ }
+
+ /// Generate the menu assignments.
+ /// - Parameters:
+ /// - prefix: The widget's prefix.
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - Returns: The code.
+ func generateMenuAssignment(
+ prefix: String,
+ config: WidgetConfiguration,
+ genConfig: GenerationConfiguration
+ ) -> String {
+ guard var setter else {
+ return ""
+ }
+ setter = (self.prefix ?? prefix) + "_" + setter
+ let name = convertPropertyName(configuration: genConfig)
+ let view = (self.cast ?? config.cast) ? "storage.pointer?.cast()" : "storage.pointer"
+ return """
+ if let declarative = \(name)?(), let app {
+ let menu = g_menu_new()
+ \(setter)(\(view), menu?.cast())
+ for item in declarative {
+ item.addMenuItems(menu: menu, app: app, window: window)
+ }
+ }
+
+ """
+ }
+
+ /// Generate a binding assignment.
+ /// - Parameters:
+ /// - prefix: The widget's prefix.
+ /// - signal: The signal's name, if there is one.
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - Returns: The code.
+ func generateBindingAssignment(
+ prefix: String,
+ signal: String?,
+ config: WidgetConfiguration,
+ genConfig: GenerationConfiguration
+ ) -> String {
+ guard var getter else {
+ return ""
+ }
+ let widget = (self.cast ?? config.cast) ? "storage.pointer?.cast()" : "storage.pointer"
+ getter = "\((self.prefix ?? prefix) + "_" + getter)(\(widget))"
+ let name = convertPropertyName(configuration: genConfig)
+ let finalGetter = genConfig.getterTypeConversions[type?.name ?? ""]?(getter) ?? getter
+ let setter = "\(name)?.wrappedValue = \(finalGetter)"
+ if let signal {
+ return """
+
+ storage.connectSignal(name: "\(signal)", id: "\(self.name)") {
+ \(setter)
+ }
+ """
+ } else {
+ return """
+
+ storage.notify(name: "\(self.name)") {
+ \(setter)
+ }
+ """
+ }
+ }
+
+}
diff --git a/Sources/Generation/GIR/Signal.swift b/Sources/Generation/GIR/Signal.swift
new file mode 100644
index 0000000..7232313
--- /dev/null
+++ b/Sources/Generation/GIR/Signal.swift
@@ -0,0 +1,66 @@
+//
+// Signal.swift
+// Adwaita
+//
+// Created by david-swift on 14.01.24.
+//
+
+/// A signal.
+struct Signal: Decodable {
+
+ /// The signal's name.
+ var name: String
+ /// The signal's documentation.
+ var doc: String?
+
+ /// Generate the signal's property.
+ /// - Parameters:
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - Returns: The code.
+ func generateProperty(config: WidgetConfiguration, genConfig: GenerationConfiguration) -> String {
+ let name = name.convertDelimitedCasingToCamel(delimiter: "-", configuration: genConfig, unshorten: true)
+ return """
+
+ \(doc?.docComment(indent: " ") ?? " /// \(name)")
+ var \(name): (() -> Void)?
+ """
+ }
+
+ /// Generate the signal's modifier.
+ /// - Parameters:
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - Returns: The code.
+ func generateModifier(config: WidgetConfiguration, genConfig: GenerationConfiguration) -> String {
+ let name = name.convertDelimitedCasingToCamel(delimiter: "-", configuration: genConfig, unshorten: true)
+ return """
+
+ \(doc?.docComment(indent: " ") ?? " /// \(name)")
+ public func \(name)(_ \(name): @escaping () -> Void) -> Self {
+ var newSelf = self
+ newSelf.\(name) = \(name)
+ return newSelf
+ }
+
+ """
+ }
+
+ /// Generate the signal's modification.
+ /// - Parameters:
+ /// - config: The widget configuration.
+ /// - genConfig: The generation configuration.
+ /// - Returns: The code.
+ func generateModification(config: WidgetConfiguration, genConfig: GenerationConfiguration) -> String {
+ let name = name.convertDelimitedCasingToCamel(delimiter: "-", configuration: genConfig, unshorten: true)
+ return """
+
+ if let \(name) {
+ storage.connectSignal(name: "\(self.name)") {
+ \(name)()
+ }
+ }
+ """
+ }
+
+}
diff --git a/Sources/Generation/Generation.swift b/Sources/Generation/Generation.swift
new file mode 100644
index 0000000..29e77a5
--- /dev/null
+++ b/Sources/Generation/Generation.swift
@@ -0,0 +1,103 @@
+//
+// Generation.swift
+// Adwaita
+//
+// Created by david-swift on 14.01.24.
+//
+
+import Foundation
+
+/// The generation executable.
+@main
+struct Generation {
+
+ /// The generation configuration.
+ var configuration: GenerationConfiguration = .init()
+
+ /// The static main function.
+ static func main() throws {
+ let generation = Self()
+ try generation.run()
+ }
+
+ /// The main function.
+ func run() throws {
+ removeOldFiles()
+ let gtkDefinitions = getDefinitions(path: configuration.gtkGirFilePath)
+ let adwDefinitions = getDefinitions(path: configuration.adwGirFilePath)
+ for `class` in gtkDefinitions?.namespace.classes ?? [] {
+ if let config = configuration.gtkWidgets.first(where: { $0.class == `class`.name }) {
+ createFile(
+ class: `class`,
+ config: config,
+ classes: gtkDefinitions?.namespace.classes ?? [],
+ configs: configuration.gtkWidgets
+ )
+ }
+ }
+ for `class` in adwDefinitions?.namespace.classes ?? [] {
+ if let config = configuration.adwWidgets.first(where: { $0.class == `class`.name }) {
+ createFile(
+ class: `class`,
+ config: config,
+ classes: adwDefinitions?.namespace.classes ?? [],
+ configs: configuration.adwWidgets
+ )
+ }
+ }
+ }
+
+ /// Remove the old files in the generation directory.
+ func removeOldFiles() {
+ if let fileURLs = try? FileManager.default.contentsOfDirectory(
+ at: .init(fileURLWithPath: configuration.folder),
+ includingPropertiesForKeys: nil,
+ options: .skipsHiddenFiles
+ ) {
+ for fileURL in fileURLs where fileURL.pathExtension == "swift" {
+ print("Removing \(fileURL.lastPathComponent)")
+ try? FileManager.default.removeItem(at: fileURL)
+ }
+ }
+ }
+
+ /// Get the definitions of a GIR file at a specified path.
+ /// - Parameter path: The path.
+ /// - Returns: The GIR data.
+ func getDefinitions(path: String) -> GIR? {
+ var path = path
+ let girDir = getGirDir()
+ path.replace(GenerationConfiguration.includeDir, with: girDir)
+ let data = (try? Data(contentsOf: .init(fileURLWithPath: path))) ?? .init()
+ return try? GIR.decodeGIR(data)
+ }
+
+ /// Get the directory of the gir files.
+ /// - Returns: The path.
+ func getGirDir() -> String {
+ let process = Process()
+ process.executableURL = .init(fileURLWithPath: "/bin/bash")
+ process.arguments = ["-c", "pkg-config --variable=includedir gtk4"]
+ let pipe = Pipe()
+ process.standardOutput = pipe
+ try? process.run()
+ let data = pipe.fileHandleForReading.readDataToEndOfFile()
+ return .init(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
+ }
+
+ /// Create a file containing a class.
+ /// - Parameters:
+ /// - class: The class.
+ /// - config: The widget configuration.
+ /// - classes: All the available classes.
+ /// - configs: All the available configs.
+ func createFile(`class`: Class, config: WidgetConfiguration, classes: [Class], configs: [WidgetConfiguration]) {
+ print("Generating \(config.name ?? `class`.name).swift")
+ let path = "\(configuration.folder)\(config.name ?? config.class).swift"
+ let data = `class`
+ .generate(config: config, genConfig: configuration, classes: classes, configs: configs)
+ .data(using: .utf8)
+ try? data?.write(to: .init(fileURLWithPath: path))
+ }
+
+}
diff --git a/Sources/Generation/GenerationConfiguration.swift b/Sources/Generation/GenerationConfiguration.swift
new file mode 100644
index 0000000..c4c1818
--- /dev/null
+++ b/Sources/Generation/GenerationConfiguration.swift
@@ -0,0 +1,266 @@
+//
+// GenerationConfiguration.swift
+// Adwaita
+//
+// Created by david-swift on 14.01.24.
+//
+
+import Foundation
+
+/// The configuration for the generation.
+struct GenerationConfiguration {
+
+ /// The include directory.
+ static let includeDir = "$(pkg-config --variable=includedir gtk4)"
+ /// The directory containing the GIR files.
+ static let girFilePath = "\(includeDir)/../share/gir-1.0/"
+
+ /// The Gtk GIR file.
+ var gtkGirFilePath = girFilePath + "Gtk-4.0.gir"
+ /// The Adw GIR file.
+ var adwGirFilePath = girFilePath + "Adw-1.gir"
+
+ /// The folder containing the generated Swift files.
+ var folder = "Sources/Adwaita/View/Generated/"
+
+ /// The Libadwaita widgets.
+ var adwWidgets: [WidgetConfiguration] = [
+ .init(class: "StatusPage", excludeProperties: ["paintable"]),
+ .init(class: "Banner", initializer: "adw_banner_new(title)", requiredProperties: ["title"]),
+ .init(
+ class: "Avatar",
+ initializer: "adw_avatar_new(size.cInt, text, showInitials.cBool)",
+ requiredProperties: ["size", "show-initials"],
+ excludeProperties: ["custom-image"]
+ ),
+ .init(
+ class: "WindowTitle",
+ initializer: "adw_window_title_new(title, subtitle)",
+ requiredProperties: ["title", "subtitle"]
+ ),
+ .init(class: "Bin", cast: true),
+ .init(class: "ButtonContent"),
+ .init(
+ class: "Carousel",
+ dynamicWidget: .init(
+ insert: "adw_carousel_insert",
+ remove: "adw_carousel_remove",
+ getElement: "adw_carousel_get_nth_page(widget, UInt(index).cInt)"
+ ),
+ excludeProperties: ["position", "scroll-params"]
+ ),
+ .init(class: "Clamp", excludeProperties: ["unit"]),
+ .init(class: "PreferencesRow", cast: true),
+ .init(
+ class: "ActionRow",
+ staticWidgets: [
+ .init(name: "suffix", add: "adw_action_row_add_suffix"),
+ .init(name: "prefix", add: "adw_action_row_add_prefix")
+ ],
+ cast: true
+ ),
+ .init(class: "SwitchRow", bindings: [.init(property: "active")]),
+ .init(
+ class: "ComboRow",
+ bindings: [.init(property: "selected")],
+ excludeProperties: ["expression", "factory", "list-factory", "model", "selected-item"],
+ cast: true
+ ),
+ .init(
+ class: "ExpanderRow",
+ bindings: [.init(property: "expanded"), .init(property: "enable-expansion")],
+ staticWidgets: [
+ .init(name: "rows", add: "adw_expander_row_add_row"),
+ .init(name: "suffix", add: "adw_expander_row_add_suffix"),
+ .init(name: "prefix", add: "adw_expander_row_add_prefix")
+ ],
+ cast: true
+ ),
+ .init(
+ class: "EntryRow",
+ staticWidgets: [
+ .init(name: "suffix", add: "adw_entry_row_add_suffix"),
+ .init(name: "prefix", add: "adw_entry_row_add_prefix")
+ ],
+ excludeProperties: ["attributes", "input-hints", "input-purpose"],
+ cast: true
+ ),
+ .init(
+ class: "PasswordEntryRow",
+ excludeProperties: ["attributes", "input-hints", "input-purpose"],
+ cast: true
+ ),
+ .init(
+ class: "SpinRow",
+ initializer: "adw_spin_row_new(nil, climbRate, digits.cInt)",
+ bindings: [.init(property: "value")],
+ requiredProperties: ["climb-rate", "digits"],
+ excludeProperties: ["adjustment", "update-policy"]
+ ),
+ .init(
+ class: "PreferencesGroup",
+ staticWidgets: [.init(name: "child", add: "adw_preferences_group_add")],
+ cast: true
+ ),
+ .init(
+ class: "PreferencesPage",
+ staticWidgets: [.init(name: "child", add: "adw_preferences_group_add")],
+ cast: true
+ ),
+ .init(
+ class: "OverlaySplitView",
+ bindings: [.init(property: "show-sidebar")],
+ excludeProperties: ["sidebar-position", "sidebar-width-unit"]
+ ),
+ .init(
+ class: "HeaderBar",
+ staticWidgets: [
+ .init(name: "start", add: "adw_header_bar_pack_start"),
+ .init(name: "end", add: "adw_header_bar_pack_end")
+ ],
+ excludeProperties: ["centering-policy"]
+ ),
+ .init(
+ class: "ToolbarView",
+ staticWidgets: [
+ .init(name: "bottom", add: "adw_toolbar_view_add_bottom_bar"),
+ .init(name: "top", add: "adw_toolbar_view_add_top_bar")
+ ],
+ excludeProperties: ["top-bar-style", "bottom-bar-style"]
+ ),
+ .init(class: "ToastOverlay"),
+ .init(
+ class: "SplitButton",
+ excludeProperties: ["direction", "popover"],
+ setConditions: ["label": "storage.content[\"child\"] == nil"]
+ )
+ ]
+
+ /// The Gtk widgets.
+ var gtkWidgets: [WidgetConfiguration] = [
+ .init(
+ class: "Label",
+ initializer: "gtk_label_new(label)",
+ requiredProperties: ["label"],
+ excludeProperties: [
+ "attributes",
+ "ellipsize",
+ "extra-menu",
+ "justify",
+ "natural-wrap-mode",
+ "tabs",
+ "wrap-mode"
+ ],
+ excludeSignals: ["activate-current-link", "activate-link", "move-cursor"]
+ ),
+ .init(
+ class: "Box",
+ initializer: "gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing.cInt)",
+ staticWidgets: [
+ .init(name: "append", add: "gtk_box_append"),
+ .init(name: "prepend", add: "gtk_box_prepend")
+ ],
+ requiredProperties: ["spacing"],
+ excludeProperties: ["baseline-position"],
+ cast: true
+ ),
+ .init(class: "Spinner"),
+ .init(class: "LevelBar", excludeProperties: ["mode"]),
+ .init(
+ class: "ListBox",
+ dynamicWidget: .init(
+ insert: "gtk_list_box_insert",
+ remove: "gtk_list_box_remove",
+ getElement: "gtk_list_box_get_row_at_index(widget, index.cInt)?.cast()"
+ ),
+ excludeProperties: ["selection-mode"]
+ ),
+ .init(class: "ProgressBar", excludeProperties: ["ellipsize"]),
+ .init(class: "Button", cast: true, setConditions: ["label": "storage.content[\"child\"] == nil"]),
+ .init(
+ class: "ToggleButton",
+ bindings: [.init(property: "active")],
+ excludeProperties: ["group"],
+ cast: true,
+ setConditions: ["label": "storage.content[\"child\"] == nil"]
+ ),
+ .init(
+ class: "LinkButton",
+ initializer: "gtk_link_button_new(uri)",
+ requiredProperties: ["uri"],
+ excludeSignals: ["activate-link"],
+ setConditions: ["label": "storage.content[\"child\"] == nil"]
+ ),
+ .init(
+ class: "CheckButton",
+ bindings: [.init(property: "active")],
+ excludeProperties: ["group"],
+ cast: true,
+ setConditions: ["label": "storage.content[\"child\"] == nil"]
+ ),
+ .init(
+ class: "MenuButton",
+ name: "Menu",
+ bindings: [.init(property: "active")],
+ excludeProperties: ["direction", "popover"],
+ setConditions: ["label": "storage.content[\"child\"] == nil"]
+ ),
+ .init(
+ class: "CenterBox",
+ excludeProperties: ["baseline-position"]
+ ),
+ .init(
+ class: "ScrolledWindow",
+ excludeProperties: [
+ "hadjustment",
+ "hscrollbar-policy",
+ "vadjustment",
+ "vscrollbar-policy",
+ "window-placement"
+ ]
+ ),
+ .init(class: "Overlay", staticWidgets: [.init(name: "overlay", add: "gtk_overlay_add_overlay")])
+ ]
+
+ /// The unshortening map.
+ var unshorteningMap = [
+ "char": "character",
+ "str": "string"
+ ]
+
+ /// Replacements for C types.
+ var cTypeReplacements = [
+ "const char*": "String",
+ "char*": "String",
+ "gchar*": "String",
+ "gboolean": "Bool",
+ "gdouble": "Double",
+ "guint": "UInt",
+ "gint": "Int",
+ "gfloat": "Float",
+ "double": "Double",
+ "GIcon*": "OpaquePointer",
+ "GdkPixbuf*": "OpaquePointer",
+ "GdkPaintable*": "OpaquePointer",
+ "NavigationPage": "NavigationPage"
+ ]
+
+ /// Modifications for converting a Swift into a C type.
+ var cTypeProperties = [
+ "Bool": "cBool",
+ "Int": "cInt",
+ "UInt": "cInt"
+ ]
+
+ /// Default values for modifiers with a certain type.
+ var defaultModifierValues = [
+ "Bool?": "true"
+ ]
+
+ /// Modifications for converting a C into a Swift type.
+ var getterTypeConversions: [String: (String) -> String] = [
+ "gboolean": { "\($0) != 0" },
+ "guint": { ".init(\($0))" }
+ ]
+
+}
diff --git a/Sources/Generation/WidgetConfiguration.swift b/Sources/Generation/WidgetConfiguration.swift
new file mode 100644
index 0000000..95b05f4
--- /dev/null
+++ b/Sources/Generation/WidgetConfiguration.swift
@@ -0,0 +1,68 @@
+//
+// WidgetConfiguration.swift
+// Adwaita
+//
+// Created by david-swift on 14.01.24.
+//
+
+import Foundation
+
+/// The configuration for a single widget.
+struct WidgetConfiguration {
+
+ /// The class name.
+ var `class`: String
+ /// The name of the Swift structure.
+ var name: String?
+ /// Explicitly set the C initializer.
+ var initializer: String?
+ /// The properties that should be treated as bindings.
+ var bindings: [BindingConfiguration] = []
+ /// The dynamic widget.
+ var dynamicWidget: DynamicWidget?
+ /// The static widgets.
+ var staticWidgets: [StaticWidget] = []
+ /// Non-optional properties.
+ var requiredProperties: [String] = []
+ /// Excluded properties.
+ var excludeProperties: [String] = []
+ /// Excluded signals.
+ var excludeSignals: [String] = []
+ /// Whether to cast the type.
+ var cast = false
+ /// Conditions for when to update a property.
+ var setConditions: [String: String] = [:]
+
+ /// The configuration for a binding.
+ struct BindingConfiguration {
+
+ /// The property.
+ var property: String
+ /// The signal. If not specified, use "notify::property-name".
+ var signal: String?
+
+ }
+
+ /// The configuration for a dynamic widget.
+ struct DynamicWidget {
+
+ /// The insert function.
+ var insert: String
+ /// The remove function.
+ var remove: String
+ /// Get a child to remove from its index.
+ var getElement: String = "index.cInt"
+
+ }
+
+ /// The configuration for a static widget.
+ struct StaticWidget {
+
+ /// The widget's name.
+ var name: String
+ /// The function for adding a child.
+ var add: String
+
+ }
+
+}
diff --git a/Tests/CounterDemo.swift b/Tests/CounterDemo.swift
index 4b82cf4..3cae929 100644
--- a/Tests/CounterDemo.swift
+++ b/Tests/CounterDemo.swift
@@ -8,7 +8,6 @@
// swiftlint:disable missing_docs
import Adwaita
-import Libadwaita
struct CounterDemo: View {
diff --git a/Tests/Demo.swift b/Tests/Demo.swift
index 7ad34c0..8d248d6 100644
--- a/Tests/Demo.swift
+++ b/Tests/Demo.swift
@@ -44,7 +44,7 @@ struct Demo: App {
.closeShortcut()
.defaultSize(width: 400, height: 250)
Window(id: "toolbar-demo", open: 0) { _ in
- ToolbarDemo.WindowContent()
+ ToolbarDemo.WindowContent().stopModifiers()
}
.closeShortcut()
.defaultSize(width: 400, height: 250)
@@ -77,7 +77,7 @@ struct Demo: App {
var app: GTUIApp!
var view: Body {
- OverlaySplitView(visible: sidebarVisible) {
+ OverlaySplitView(visible: $sidebarVisible) {
ScrollView {
List(Page.allCases, selection: $selection) { element in
Text(element.label)
@@ -91,9 +91,7 @@ struct Demo: App {
menu
}
.headerBarTitle {
- Text("Demo")
- .style("heading")
- .transition(.crossfade)
+ WindowTitle(subtitle: "", title: "Demo")
}
}
} content: {
@@ -119,8 +117,7 @@ struct Demo: App {
Text("")
.transition(.crossfade)
} else {
- Text("Swift Adwaita Demo")
- .style("heading")
+ WindowTitle(subtitle: "Demo", title: selection.label)
.transition(.crossfade)
}
}
@@ -145,6 +142,7 @@ struct Demo: App {
.keyboardShortcut("q".ctrl())
}
}
+ .primary()
}
}
diff --git a/Tests/FormDemo.swift b/Tests/FormDemo.swift
index 6922116..b5f7fe6 100644
--- a/Tests/FormDemo.swift
+++ b/Tests/FormDemo.swift
@@ -8,7 +8,6 @@
// swiftlint:disable missing_docs no_magic_numbers
import Adwaita
-import Libadwaita
struct FormDemo: View {
@@ -37,32 +36,30 @@ struct FormDemo: View {
var view: Body {
ScrollView {
VStack {
- Form {
- ActionRow("Rows have a title")
- .subtitle(text)
- ActionRow("Rows can have suffix widgets")
- .suffix {
- Button("Action") { }
- .verticalCenter()
- }
- }
- .padding()
+ actionRows
FormSection("Entry Rows") {
Form {
EntryRow("Entry Row", text: $text)
.suffix {
- Button(icon: .default(icon: .editCopy)) { Clipboard.copy(text) }
+ Button(icon: .default(icon: .editCopy)) { State.copy(text) }
.style("flat")
.verticalCenter()
}
- EntryRow("Password", text: $password)
- .secure()
+ EntryRow(password, text: $password)
+ .secure(text: $password)
}
}
.padding()
- rowDemo("Spin Rows", row: SpinRow("Spin Row", value: $value, min: 0, max: 100))
- rowDemo("Switch Rows", row: SwitchRow("Switch Row", isOn: $isOn))
- rowDemo("Combo Rows", row: ComboRow("Combo Row", selection: $selection, values: values))
+ rowDemo("Spin Rows", row: SpinRow("Spin Row", value: $value, min: 0, max: 100).subtitle("\(value)"))
+ rowDemo("Switch Rows", row: SwitchRow("Switch Row", isOn: $isOn).subtitle(isOn ? "On" : "Off"))
+ rowDemo(
+ "Combo Rows",
+ row: ComboRow("Combo Row", selection: $selection, values: values).subtitle(selection)
+ )
+ rowDemo("Expander Rows", row: ExpanderRow().title("Expander Row").rows {
+ ActionRow("Hello")
+ ActionRow("World")
+ })
}
.padding()
.frame(maxSize: 400)
@@ -72,6 +69,19 @@ struct FormDemo: View {
}
}
+ var actionRows: Form {
+ .init {
+ ActionRow("Rows have a title")
+ .subtitle(text)
+ ActionRow("Rows can have suffix widgets")
+ .suffix {
+ Button("Action") { }
+ .verticalCenter()
+ }
+ }
+ .padding()
+ }
+
func rowDemo(_ title: String, row: View) -> View {
FormSection(title) {
Form {
diff --git a/Tests/Page.swift b/Tests/Page.swift
index 4902a7a..d6bf9de 100644
--- a/Tests/Page.swift
+++ b/Tests/Page.swift
@@ -8,7 +8,6 @@
// swiftlint:disable missing_docs implicitly_unwrapped_optional
import Adwaita
-import Libadwaita
enum Page: String, Identifiable, CaseIterable, Codable {
@@ -40,7 +39,7 @@ enum Page: String, Identifiable, CaseIterable, Codable {
}
}
- var icon: Libadwaita.Icon? {
+ var icon: Icon? {
switch self {
case .welcome:
return .default(icon: .emojiNature)
diff --git a/Tests/ToolbarDemo.swift b/Tests/ToolbarDemo.swift
index f1d3250..632396f 100644
--- a/Tests/ToolbarDemo.swift
+++ b/Tests/ToolbarDemo.swift
@@ -55,10 +55,4 @@ struct ToolbarDemo: View {
}
-extension Int: Identifiable {
-
- public var id: Self { self }
-
-}
-
// swiftlint:enable missing_docs no_magic_numbers
diff --git a/Tests/ViewSwitcherDemo.swift b/Tests/ViewSwitcherDemo.swift
index a6b5166..c35d56f 100644
--- a/Tests/ViewSwitcherDemo.swift
+++ b/Tests/ViewSwitcherDemo.swift
@@ -8,7 +8,6 @@
// swiftlint:disable missing_docs
import Adwaita
-import Libadwaita
struct ViewSwitcherDemo: View {
diff --git a/user-manual/Advanced/CreatingWidgets.md b/user-manual/Advanced/CreatingWidgets.md
index f3fed67..eb35edb 100644
--- a/user-manual/Advanced/CreatingWidgets.md
+++ b/user-manual/Advanced/CreatingWidgets.md
@@ -2,7 +2,7 @@
Widgets are special views that do not provide a collection of other views as a content,
but have functions that are called when creating or updating the view.
-Normally, a widget manages a GTK or Libadwaita widget using [Libadwaita][1].
+Normally, a widget manages a GTK or Libadwaita widget using the C API.
## Recreate the `Text` widget
In this tutorial, we will recreate the text widget.
@@ -21,6 +21,8 @@ struct CustomText: Widget {
This widget can be called in a view body using `CustomText(text: "Hello, world!")`.
Now, add the two functions required by the protocol:
```swift
+import CAdw
+
struct CustomText: Widget {
var text: String
@@ -30,6 +32,7 @@ struct CustomText: Widget {
}
```
+Import CAdw which exposes the whole C Libadwaita and Gtk API to Swift.
## The `container(modifiers:)` Function
This function initializes the widget when the widget appears for the first time.
@@ -37,10 +40,9 @@ It expects a `ViewStorage` as the return type.
In our case, this function is very simple:
```swift
func container(modifiers: [(View) -> View]) -> ViewStorage {
- .init(MarkupLabel(self.text))
+ .init(gtk_label_new(text)?.opaque())
}
```
-`MarkupLabel` is defined in [Libadwaita][1].
## The `update(_:modifiers:)` Function
Whenever a state of the app changes, the `update(_:)` function of the widget gets called.
@@ -48,9 +50,7 @@ You get the view storage that you have previously initialized as a parameter.
Update the storage to reflect the current state of the widget:
```swift
func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
- if let label = storage.view as? MarkupLabel {
- label.setText(text)
- }
+ gtk_label_set_label(storage.pointer, text)
}
```
@@ -58,7 +58,6 @@ func update(_ storage: ViewStorage, modifiers: [(View) -> View]) {
Some widgets act as containers that accept other widgets as children.
In that case, use the `ViewStorage`'s `content` property for storing their view storages.
In the `update(_:modifiers:)` function, update the children's storages.
-An example showcasing how to implement containers is the [VStack][2].
+An example showcasing how to implement containers is the [Box][1] (it is auto-generated).
-[1]: https://github.com/AparokshaUI/Libadwaita
-[2]: ../../Sources/Adwaita/View/VStack.swift
+[1]: ../../Sources/Adwaita/View/Generated/Box.swift
diff --git a/user-manual/Information/AutoGeneratedWidgets.md b/user-manual/Information/AutoGeneratedWidgets.md
new file mode 100644
index 0000000..3064288
--- /dev/null
+++ b/user-manual/Information/AutoGeneratedWidgets.md
@@ -0,0 +1,89 @@
+# Auto-Generated Widgets
+
+Visit the [Libadwaita](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1-latest/) or [Gtk](https://docs.gtk.org/gtk4/index.html) docs
+and find the widget you want to implement.
+
+There is an auto-generated interface available for many Libadwaita and Gtk widgets.
+This page shows how to use them, using [AdwSplitButton](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1-latest/class.SplitButton.html) as an example.
+
+## The Initializer
+The type name is the original type name without the prefix, in that case `SplitButton`.
+
+Most of the widgets enable initialization with an empty initializer.
+Exceptions will cause a helpful error message when trying with an empty initializer.
+
+```swift
+var view: Body {
+ SplitButton()
+}
+```
+
+## Simple Properties
+Properties with "simple" types, such as strings (and most other types), have
+a Swift equivalent with the same name in camel case.
+For example, you will find the properties `label` and `dropdown-tooltip` in the docs.
+They can be used in the following way:
+
+```swift
+var view: Body {
+ SplitButton()
+ .label("Hello")
+ .dropdownTooltip("World")
+}
+```
+
+Properties that are booleans can be set implicitly to `true`:
+
+```swift
+var view: Body {
+ SplitButton()
+ .label("Hello")
+ .canShrink()
+}
+```
+
+## Signals
+You can connect to signals using their names.
+
+```swift
+var view: Body {
+ SplitButton()
+ .label("Hello")
+ .clicked {
+ print("Clicked")
+ }
+}
+```
+
+## Views and Menu Models
+Properties of the type `GtkWidget` can be used in the following way:
+```swift
+ SplitButton()
+ .child {
+ ButtonContent()
+ .label("Hello")
+ .iconName("zoom-original-symbolic")
+ }
+}
+```
+
+`GMenuModel` is treated in a similarly:
+```swift
+var view: Body {
+ SplitButton()
+ .label("Hello")
+ .menuModel(app: app) {
+ MenuButton("Test") {
+ print("Test")
+ }
+ MenuButton("World") {
+ print("World")
+ }
+ }
+}
+```
+
+## Bindings
+Some properties can be changed by a user action (e.g. toggles).
+They expect a binding instead of a "normal" value.
+Compilation error messages will be helpful in spotting those properties.
diff --git a/user-manual/Information/Widgets.md b/user-manual/Information/Widgets.md
index a0d05ca..5a0a288 100644
--- a/user-manual/Information/Widgets.md
+++ b/user-manual/Information/Widgets.md
@@ -1,6 +1,7 @@
# Widgets
-This is an overview of the available widgets and other components in _Adwaita_.
+This is an overview of the available widgets and other components in _Adwaita_ that are not auto-generated.
+There are many more widgets available using auto-generation. Learn [how to use them.](AutoGeneratedWidgets.md)
| Name | Description | Widget |
| -------------------- | ------------------------------------------------------------------- | ---------------------- |
@@ -35,20 +36,21 @@ This is an overview of the available widgets and other components in _Adwaita_.
| Syntax | Description |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
-| `inspect(_:)` | Edit the underlying [Libadwaita](https://github.com/AparokshaUI/Libadwaita) widget. |
+| `inspect(_:)` | Edit the underlying Gtk or Libadwaita widget. |
| `padding(_:_:)` | Add empty space around a view. |
| `hexpand(_:)` | Enable or disable the horizontal expansion of a view. |
| `vexpand(_:)` | Enable or disable the vertical expansion of a view. |
| `halign(_:)` | Set the horizontal alignment of a view. |
| `valign(_:)` | Set the vertical alignment of a view. |
-| `frame(minWidth:minHeight:)` | Set the view’s minimal width or height. |
-| `frame(maxSize:)` | Set the view’s maximal size. |
+| `frame(minWidth:minHeight:)` | Set the view’s minimum width or height. |
+| `frame(maxWidth:)` | Set the view’s maximum width. |
+| `frame(maxHeight:)` | Set the view’s maximum height. |
| `transition(_:)` | Assign a transition with the view that is used if it is a direct child of an EitherView. |
| `onUpdate(_:)` | Run a function every time a view gets updated. |
| `navigationTitle(_:)` | Add a title that is used if the view is a direct child of a NavigationView. |
| `style(_:)` | Add a style class to the view. |
| `onAppear(_:)` | Run when the view is rendered for the first time. |
-| `inspectOnAppear(_:)` | Edit the underlying [Libadwaita](https://github.com/AparokshaUI/Libadwaita) class when the view is rendered for the first time.|
+| `inspectOnAppear(_:)` | Edit the underlying Gtk or Libadwaita class when the view is rendered for the first time. |
| `topToolbar(visible:_:)` | Add a native toolbar to the view. Normally, it contains a HeaderBar. |
| `bottomToolbar(visible:_:)` | Add a native bottom toolbar to the view. |
| `modifyContent(_:modify:)` | Replace all occurrences of a certain view type with another view. |