Add support for carousels
Add support for any Libadwaita insertable container
This commit is contained in:
parent
1c50b3b923
commit
8eb004c9ea
@ -18,7 +18,9 @@
|
||||
- [AppearObserver](structs/AppearObserver.md)
|
||||
- [Binding](structs/Binding.md)
|
||||
- [Button](structs/Button.md)
|
||||
- [Carousel](structs/Carousel.md)
|
||||
- [Clamp](structs/Clamp.md)
|
||||
- [Container](structs/Container.md)
|
||||
- [ContentModifier](structs/ContentModifier.md)
|
||||
- [FileDialog](structs/FileDialog.md)
|
||||
- [HStack](structs/HStack.md)
|
||||
|
||||
26
Documentation/Reference/structs/Carousel.md
Normal file
26
Documentation/Reference/structs/Carousel.md
Normal file
@ -0,0 +1,26 @@
|
||||
**STRUCT**
|
||||
|
||||
# `Carousel`
|
||||
|
||||
A carousel view.
|
||||
|
||||
## Properties
|
||||
### `elements`
|
||||
|
||||
The elements.
|
||||
|
||||
### `content`
|
||||
|
||||
The content.
|
||||
|
||||
### `view`
|
||||
|
||||
The view.
|
||||
|
||||
## Methods
|
||||
### `init(_:content:)`
|
||||
|
||||
Initialize `Carousel`.
|
||||
- Parameters:
|
||||
- elements: The elements.
|
||||
- content: The view for an element.
|
||||
60
Documentation/Reference/structs/Container.md
Normal file
60
Documentation/Reference/structs/Container.md
Normal file
@ -0,0 +1,60 @@
|
||||
**STRUCT**
|
||||
|
||||
# `Container`
|
||||
|
||||
A container widget.
|
||||
|
||||
## Properties
|
||||
### `elements`
|
||||
|
||||
The elements.
|
||||
|
||||
### `content`
|
||||
|
||||
The content.
|
||||
|
||||
### `container`
|
||||
|
||||
Get the container for initialization.
|
||||
|
||||
### `elementsID`
|
||||
|
||||
The identifier of the elements storage.
|
||||
|
||||
## Methods
|
||||
### `init(_:content:container:)`
|
||||
|
||||
Initialize `Container`.
|
||||
- Parameters:
|
||||
- elements: The elements.
|
||||
- content: The view for an element.
|
||||
- container: Get the initial Libadwaita container widget.
|
||||
|
||||
### `update(_:modifiers:)`
|
||||
|
||||
Update a view storage.
|
||||
- Parameters:
|
||||
- storage: The view storage.
|
||||
- modifiers: Modify views before being updated.
|
||||
|
||||
### `container(modifiers:)`
|
||||
|
||||
Get a view storage.
|
||||
- Parameter modifiers: Modify views before being updated.
|
||||
- Returns: The view storage.
|
||||
|
||||
### `updateContainer(_:content:modifiers:)`
|
||||
|
||||
Update the container's content.
|
||||
- Parameters:
|
||||
- container: The container.
|
||||
- content: The content's view storage.
|
||||
- modifiers: The view modifiers.
|
||||
|
||||
### `getWidget(element:modifiers:)`
|
||||
|
||||
Get the view storage of an element.
|
||||
- Parameters:
|
||||
- element: The element.
|
||||
- modifiers: The modifiers.
|
||||
- Returns: The view storage.
|
||||
37
Sources/Adwaita/View/Carousel.swift
Normal file
37
Sources/Adwaita/View/Carousel.swift
Normal file
@ -0,0 +1,37 @@
|
||||
//
|
||||
// Carousel.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 01.01.24.
|
||||
//
|
||||
|
||||
import Libadwaita
|
||||
|
||||
/// A carousel view.
|
||||
public struct Carousel<Element>: View where Element: Identifiable {
|
||||
|
||||
/// The elements.
|
||||
var elements: [Element]
|
||||
/// The content.
|
||||
var content: (Element) -> Body
|
||||
|
||||
/// The view.
|
||||
public var view: Body {
|
||||
Container(elements, content: content) {
|
||||
Libadwaita.Carousel()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
}
|
||||
101
Sources/Adwaita/View/Container.swift
Normal file
101
Sources/Adwaita/View/Container.swift
Normal file
@ -0,0 +1,101 @@
|
||||
//
|
||||
// Container.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 01.01.24.
|
||||
//
|
||||
|
||||
import LevenshteinTransformations
|
||||
import Libadwaita
|
||||
|
||||
/// A container widget.
|
||||
public struct Container<Type, Element>: 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)
|
||||
}
|
||||
|
||||
}
|
||||
44
Tests/CarouselDemo.swift
Normal file
44
Tests/CarouselDemo.swift
Normal file
@ -0,0 +1,44 @@
|
||||
//
|
||||
// CarouselDemo.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 01.01.24.
|
||||
//
|
||||
|
||||
// swiftlint:disable missing_docs no_magic_numbers
|
||||
|
||||
import Adwaita
|
||||
import Foundation
|
||||
|
||||
struct CarouselDemo: View {
|
||||
|
||||
@State private var items: [ListDemo.Element] = [.init(id: "Hello"), .init(id: "World")]
|
||||
|
||||
var view: Body {
|
||||
Button("Add Card") {
|
||||
let element = ListDemo.Element(id: UUID().uuidString)
|
||||
items.append(element)
|
||||
}
|
||||
.padding()
|
||||
.halign(.center)
|
||||
Carousel(items) { element in
|
||||
VStack {
|
||||
Text(element.id)
|
||||
.vexpand()
|
||||
Button("Delete") {
|
||||
items = items.filter { $0.id != element.id }
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.vexpand()
|
||||
.hexpand()
|
||||
.style("card")
|
||||
.padding(20)
|
||||
.frame(minWidth: 300, minHeight: 200)
|
||||
.frame(maxSize: 500)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// swiftlint:enable missing_docs no_magic_numbers
|
||||
@ -21,6 +21,7 @@ enum Page: String, Identifiable, CaseIterable {
|
||||
case overlayWindow
|
||||
case toast
|
||||
case list
|
||||
case carousel
|
||||
|
||||
var id: Self {
|
||||
self
|
||||
@ -64,6 +65,8 @@ enum Page: String, Identifiable, CaseIterable {
|
||||
return "Show a notification inside of your app."
|
||||
case .list:
|
||||
return "Organize content in multiple rows."
|
||||
case .carousel:
|
||||
return "Scroll horizontally on a touchpad or touchscreen, or scroll down on your mouse wheel."
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,6 +91,8 @@ enum Page: String, Identifiable, CaseIterable {
|
||||
ToastDemo(toast: toast)
|
||||
case .list:
|
||||
ListDemo()
|
||||
case .carousel:
|
||||
CarouselDemo()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,22 +2,24 @@
|
||||
|
||||
This is an overview of the available widgets and other components in _Adwaita_.
|
||||
|
||||
| Name | Description | Widget |
|
||||
| -------------------- | ----------------------------------------------------------------- | ---------------------- |
|
||||
| Button | A widget that triggers a function when being clicked. | GtkButton |
|
||||
| ViewStack | A widget that displays one of its child views based on an id. | GtkStack |
|
||||
| HeaderBar | A widget for creating custom title bars for windows. | GtkHeaderBar |
|
||||
| Text | A widget for displaying a small amount of text. | GtkLabel |
|
||||
| VStack | A widget which arranges child widgets into a single column. | GtkBox |
|
||||
| HStack | A widget which arranges child widgets into a single row. | GtkBox |
|
||||
| Toggle | A button with two possible states, on and off. | GtkToggleButton |
|
||||
| List | A widget which arranges child widgets vertically into rows. | GtkListBox |
|
||||
| Menu | A widget showing a button that toggles the appearance of a menu. | GtkMenuButton |
|
||||
| NavigationSplitView | A widget presenting sidebar and content side by side. | AdwNavigationSplitView |
|
||||
| OverlaySplitView | A widget presenting sidebar and content side by side. | AdwOverlaySplitView |
|
||||
| ScrollView | A container that makes its child scrollable. | GtkScrolledWindow |
|
||||
| StatusPage | A page with an icon, title, and optionally description and widget.| AdwStatusPage |
|
||||
| StateWrapper | A wrapper not affecting the UI which stores state information. | - |
|
||||
| Name | Description | Widget |
|
||||
| -------------------- | ------------------------------------------------------------------- | ---------------------- |
|
||||
| Button | A widget that triggers a function when being clicked. | GtkButton |
|
||||
| ViewStack | A widget that displays one of its child views based on an id. | GtkStack |
|
||||
| HeaderBar | A widget for creating custom title bars for windows. | GtkHeaderBar |
|
||||
| Text | A widget for displaying a small amount of text. | GtkLabel |
|
||||
| VStack | A widget which arranges child widgets into a single column. | GtkBox |
|
||||
| HStack | A widget which arranges child widgets into a single row. | GtkBox |
|
||||
| Toggle | A button with two possible states, on and off. | GtkToggleButton |
|
||||
| List | A widget which arranges child widgets vertically into rows. | GtkListBox |
|
||||
| Menu | A widget showing a button that toggles the appearance of a menu. | GtkMenuButton |
|
||||
| NavigationSplitView | A widget presenting sidebar and content side by side. | AdwNavigationSplitView |
|
||||
| OverlaySplitView | A widget presenting sidebar and content side by side. | AdwOverlaySplitView |
|
||||
| ScrollView | A container that makes its child scrollable. | GtkScrolledWindow |
|
||||
| StatusPage | A page with an icon, title, and optionally description and widget. | AdwStatusPage |
|
||||
| Container | Supports any widget conforming to `Libadwaita.InsertableContainer`. | Multiple widgets |
|
||||
| Carousel | A paginated scrolling widget. | AdwCarousel |
|
||||
| StateWrapper | A wrapper not affecting the UI which stores state information. | - |
|
||||
|
||||
### View Modifiers
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user