forked from aparoksha/adwaita-swift
Add support for AdwViewSwitcher
This commit is contained in:
parent
03d259811b
commit
e94885f48f
@ -7,6 +7,7 @@
|
|||||||
- [MenuItemGroup](protocols/MenuItemGroup.md)
|
- [MenuItemGroup](protocols/MenuItemGroup.md)
|
||||||
- [StateProtocol](protocols/StateProtocol.md)
|
- [StateProtocol](protocols/StateProtocol.md)
|
||||||
- [View](protocols/View.md)
|
- [View](protocols/View.md)
|
||||||
|
- [ViewSwitcherOption](protocols/ViewSwitcherOption.md)
|
||||||
- [Widget](protocols/Widget.md)
|
- [Widget](protocols/Widget.md)
|
||||||
- [WindowScene](protocols/WindowScene.md)
|
- [WindowScene](protocols/WindowScene.md)
|
||||||
- [WindowSceneGroup](protocols/WindowSceneGroup.md)
|
- [WindowSceneGroup](protocols/WindowSceneGroup.md)
|
||||||
@ -45,6 +46,7 @@
|
|||||||
- [ToolbarView](structs/ToolbarView.md)
|
- [ToolbarView](structs/ToolbarView.md)
|
||||||
- [VStack](structs/VStack.md)
|
- [VStack](structs/VStack.md)
|
||||||
- [ViewStack](structs/ViewStack.md)
|
- [ViewStack](structs/ViewStack.md)
|
||||||
|
- [ViewSwitcher](structs/ViewSwitcher.md)
|
||||||
- [Window](structs/Window.md)
|
- [Window](structs/Window.md)
|
||||||
|
|
||||||
## Classes
|
## Classes
|
||||||
|
|||||||
19
Documentation/Reference/protocols/ViewSwitcherOption.md
Normal file
19
Documentation/Reference/protocols/ViewSwitcherOption.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
**PROTOCOL**
|
||||||
|
|
||||||
|
# `ViewSwitcherOption`
|
||||||
|
|
||||||
|
The protocol an element type for view switcher has to conform to.
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
### `title`
|
||||||
|
|
||||||
|
The title displayed in the switcher and used for identification.
|
||||||
|
|
||||||
|
### `icon`
|
||||||
|
|
||||||
|
A symbolic representation in the view switcher.
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
### `init(title:)`
|
||||||
|
|
||||||
|
Get the element from the title.
|
||||||
46
Documentation/Reference/structs/ViewSwitcher.md
Normal file
46
Documentation/Reference/structs/ViewSwitcher.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
**STRUCT**
|
||||||
|
|
||||||
|
# `ViewSwitcher`
|
||||||
|
|
||||||
|
A picker used for indicating multiple views.
|
||||||
|
|
||||||
|
It normally controls a `ViewStack` (e.g. via `switch` statements).
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
### `selection`
|
||||||
|
|
||||||
|
The selected element.
|
||||||
|
|
||||||
|
### `wide`
|
||||||
|
|
||||||
|
Whether the wide style is used, that means the icons and titles are on the same line.
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
### `init(selection:)`
|
||||||
|
|
||||||
|
Initialize a view switcher.
|
||||||
|
- Parameter selection: The selected element.
|
||||||
|
|
||||||
|
### `container(modifiers:)`
|
||||||
|
|
||||||
|
Get a view switcher's view storage.
|
||||||
|
- Parameter modifiers: Modify views before being updated.
|
||||||
|
- Returns: The view storage.
|
||||||
|
|
||||||
|
### `update(_:modifiers:)`
|
||||||
|
|
||||||
|
Update a view switcher's view storage.
|
||||||
|
- Parameters:
|
||||||
|
- storage: The view storage.
|
||||||
|
- modifiers: Modify views before being updated.
|
||||||
|
|
||||||
|
### `updateSwitcher(switcher:)`
|
||||||
|
|
||||||
|
Update a view switcher's style and selection.
|
||||||
|
- Parameter switcher: The view switcher.
|
||||||
|
|
||||||
|
### `wideDesign(_:)`
|
||||||
|
|
||||||
|
Set whether to use the wide design.
|
||||||
|
- Parameter wide: Whether to use the wide design.
|
||||||
|
- Returns: The view switcher.
|
||||||
83
Sources/Adwaita/View/ViewSwitcher.swift
Normal file
83
Sources/Adwaita/View/ViewSwitcher.swift
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
//
|
||||||
|
// ViewSwitcher.swift
|
||||||
|
// Adwaita
|
||||||
|
//
|
||||||
|
// Created by david-swift on 03.01.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Libadwaita
|
||||||
|
|
||||||
|
/// A picker used for indicating multiple views.
|
||||||
|
///
|
||||||
|
/// It normally controls a `ViewStack` (e.g. via `switch` statements).
|
||||||
|
public struct ViewSwitcher<Element>: Widget where Element: ViewSwitcherOption {
|
||||||
|
|
||||||
|
/// The selected element.
|
||||||
|
@Binding var selection: Element
|
||||||
|
/// Whether the wide style is used, that means the icons and titles are on the same line.
|
||||||
|
var wide = false
|
||||||
|
|
||||||
|
/// Initialize a view switcher.
|
||||||
|
/// - Parameter selection: The selected element.
|
||||||
|
public init(selection: Binding<Element>) {
|
||||||
|
self._selection = selection
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a view switcher's view storage.
|
||||||
|
/// - Parameter modifiers: Modify views before being updated.
|
||||||
|
/// - Returns: The view storage.
|
||||||
|
public func container(modifiers: [(View) -> View]) -> ViewStorage {
|
||||||
|
let switcher = Libadwaita.ViewSwitcher()
|
||||||
|
for option in Element.allCases {
|
||||||
|
_ = switcher.addOption(title: option.title, icon: option.icon)
|
||||||
|
}
|
||||||
|
_ = switcher.onSelect {
|
||||||
|
let selection = switcher.getSelection()
|
||||||
|
if let element = Element(title: selection) {
|
||||||
|
self.selection = element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateSwitcher(switcher: switcher)
|
||||||
|
return .init(switcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update a view switcher's view storage.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set whether to use the wide design.
|
||||||
|
/// - Parameter wide: Whether to use the wide design.
|
||||||
|
/// - Returns: The view switcher.
|
||||||
|
public func wideDesign(_ wide: Bool = true) -> Self {
|
||||||
|
var newSelf = self
|
||||||
|
newSelf.wide = wide
|
||||||
|
return newSelf
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The protocol an element type for view switcher has to conform to.
|
||||||
|
public protocol ViewSwitcherOption: CaseIterable {
|
||||||
|
|
||||||
|
/// The title displayed in the switcher and used for identification.
|
||||||
|
var title: String { get }
|
||||||
|
/// A symbolic representation in the view switcher.
|
||||||
|
var icon: Icon { get }
|
||||||
|
|
||||||
|
/// Get the element from the title.
|
||||||
|
init?(title: String)
|
||||||
|
|
||||||
|
}
|
||||||
@ -49,6 +49,13 @@ struct Demo: App {
|
|||||||
.closeShortcut()
|
.closeShortcut()
|
||||||
.defaultSize(width: 400, height: 250)
|
.defaultSize(width: 400, height: 250)
|
||||||
.title("Toolbar Demo")
|
.title("Toolbar Demo")
|
||||||
|
Window(id: "switcher-demo", open: 0) { _ in
|
||||||
|
ViewSwitcherDemo.WindowContent()
|
||||||
|
}
|
||||||
|
.closeShortcut()
|
||||||
|
.defaultSize(width: 600, height: 400)
|
||||||
|
.resizable(false)
|
||||||
|
.title("View Switcher Demo")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ enum Page: String, Identifiable, CaseIterable, Codable {
|
|||||||
case toast
|
case toast
|
||||||
case list
|
case list
|
||||||
case carousel
|
case carousel
|
||||||
|
case viewSwitcher
|
||||||
|
|
||||||
var id: Self {
|
var id: Self {
|
||||||
self
|
self
|
||||||
@ -31,6 +32,8 @@ enum Page: String, Identifiable, CaseIterable, Codable {
|
|||||||
switch self {
|
switch self {
|
||||||
case .overlayWindow:
|
case .overlayWindow:
|
||||||
return "Overlay Window"
|
return "Overlay Window"
|
||||||
|
case .viewSwitcher:
|
||||||
|
return "View Switcher"
|
||||||
default:
|
default:
|
||||||
return rawValue.capitalized
|
return rawValue.capitalized
|
||||||
}
|
}
|
||||||
@ -67,9 +70,12 @@ enum Page: String, Identifiable, CaseIterable, Codable {
|
|||||||
return "Organize content in multiple rows."
|
return "Organize content in multiple rows."
|
||||||
case .carousel:
|
case .carousel:
|
||||||
return "Scroll horizontally on a touchpad or touchscreen, or scroll down on your mouse wheel."
|
return "Scroll horizontally on a touchpad or touchscreen, or scroll down on your mouse wheel."
|
||||||
|
case .viewSwitcher:
|
||||||
|
return "Switch the window's view."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// swiftlint:disable cyclomatic_complexity
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
func view(app: GTUIApp!, window: GTUIApplicationWindow, toast: Signal) -> Body {
|
func view(app: GTUIApp!, window: GTUIApplicationWindow, toast: Signal) -> Body {
|
||||||
switch self {
|
switch self {
|
||||||
@ -93,8 +99,11 @@ enum Page: String, Identifiable, CaseIterable, Codable {
|
|||||||
ListDemo()
|
ListDemo()
|
||||||
case .carousel:
|
case .carousel:
|
||||||
CarouselDemo()
|
CarouselDemo()
|
||||||
|
case .viewSwitcher:
|
||||||
|
ViewSwitcherDemo(app: app)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// swiftlint:enable cyclomatic_complexity
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
101
Tests/ViewSwitcherDemo.swift
Normal file
101
Tests/ViewSwitcherDemo.swift
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
//
|
||||||
|
// ToolbarDemo.swift
|
||||||
|
// Adwaita
|
||||||
|
//
|
||||||
|
// Created by david-swift on 03.01.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
// swiftlint:disable missing_docs
|
||||||
|
|
||||||
|
import Adwaita
|
||||||
|
import Libadwaita
|
||||||
|
|
||||||
|
struct ViewSwitcherDemo: View {
|
||||||
|
|
||||||
|
var app: GTUIApp
|
||||||
|
|
||||||
|
var view: Body {
|
||||||
|
VStack {
|
||||||
|
Button("View Demo") {
|
||||||
|
app.showWindow("switcher-demo")
|
||||||
|
}
|
||||||
|
.style("suggested-action")
|
||||||
|
.frame(maxSize: 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WindowContent: View {
|
||||||
|
|
||||||
|
@State private var selection: ViewSwitcherView = .albums
|
||||||
|
@State private var bottom = false
|
||||||
|
|
||||||
|
var view: Body {
|
||||||
|
VStack {
|
||||||
|
Text(selection.title)
|
||||||
|
.padding()
|
||||||
|
HStack {
|
||||||
|
Button(bottom ? "Show Top Bar" : "Show Bottom Bar") {
|
||||||
|
bottom.toggle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.halign(.center)
|
||||||
|
}
|
||||||
|
.valign(.center)
|
||||||
|
.topToolbar {
|
||||||
|
if bottom {
|
||||||
|
HeaderBar
|
||||||
|
.empty()
|
||||||
|
} else {
|
||||||
|
toolbar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bottomToolbar(visible: bottom) {
|
||||||
|
toolbar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var toolbar: View {
|
||||||
|
HeaderBar(titleButtons: !bottom) { } end: { }
|
||||||
|
.headerBarTitle {
|
||||||
|
ViewSwitcher(selection: $selection)
|
||||||
|
.wideDesign(!bottom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ViewSwitcherView: String, ViewSwitcherOption {
|
||||||
|
|
||||||
|
case albums
|
||||||
|
case artists
|
||||||
|
case songs
|
||||||
|
case playlists
|
||||||
|
|
||||||
|
var title: String {
|
||||||
|
rawValue.capitalized
|
||||||
|
}
|
||||||
|
|
||||||
|
var icon: Icon {
|
||||||
|
.default(icon: {
|
||||||
|
switch self {
|
||||||
|
case .albums:
|
||||||
|
return .mediaOpticalCdAudio
|
||||||
|
case .artists:
|
||||||
|
return .avatarDefault
|
||||||
|
case .songs:
|
||||||
|
return .emblemMusic
|
||||||
|
case .playlists:
|
||||||
|
return .viewList
|
||||||
|
}
|
||||||
|
}())
|
||||||
|
}
|
||||||
|
|
||||||
|
init?(title: String) {
|
||||||
|
self.init(rawValue: title.lowercased())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// swiftlint:enable missing_docs
|
||||||
@ -19,6 +19,7 @@ This is an overview of the available widgets and other components in _Adwaita_.
|
|||||||
| StatusPage | A page with an icon, title, and optionally description and widget. | AdwStatusPage |
|
| StatusPage | A page with an icon, title, and optionally description and widget. | AdwStatusPage |
|
||||||
| Container | Supports any widget conforming to `Libadwaita.InsertableContainer`. | Multiple widgets |
|
| Container | Supports any widget conforming to `Libadwaita.InsertableContainer`. | Multiple widgets |
|
||||||
| Carousel | A paginated scrolling widget. | AdwCarousel |
|
| Carousel | A paginated scrolling widget. | AdwCarousel |
|
||||||
|
| ViewSwitcher | A control for switching between different views. | AdwViewSwitcher |
|
||||||
| StateWrapper | A wrapper not affecting the UI which stores state information. | - |
|
| StateWrapper | A wrapper not affecting the UI which stores state information. | - |
|
||||||
|
|
||||||
### View Modifiers
|
### View Modifiers
|
||||||
@ -67,6 +68,11 @@ This is an overview of the available widgets and other components in _Adwaita_.
|
|||||||
| ---------------------------- | --------------------------------------------------------------------------------------- |
|
| ---------------------------- | --------------------------------------------------------------------------------------- |
|
||||||
| `trailingSidebar(_:)` | Whether the sidebar is trailing to the content view. |
|
| `trailingSidebar(_:)` | Whether the sidebar is trailing to the content view. |
|
||||||
|
|
||||||
|
### `ViewSwitcher` Modifiers
|
||||||
|
| Syntax | Description |
|
||||||
|
| ---------------------------- | --------------------------------------------------------------------------------------- |
|
||||||
|
| `wideDesign(_:)` | Whether the wide view switcher design is used. |
|
||||||
|
|
||||||
### Window Types
|
### Window Types
|
||||||
| Name | Description | Widget |
|
| Name | Description | Widget |
|
||||||
| -------------------- | ----------------------------------------------------------------- | ---------------------- |
|
| -------------------- | ----------------------------------------------------------------- | ---------------------- |
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user