Add support for GtkPicture

This commit is contained in:
david-swift 2024-04-21 11:52:34 +02:00
parent 7748668f4b
commit b7aabb0966
50 changed files with 343 additions and 49 deletions

1
.gitignore vendored
View File

@ -10,3 +10,4 @@ DerivedData/
/Package.resolved
.Ulysses-Group.plist
/.docc-build
/io.github.AparokshaUI.Generation.json

52
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,52 @@
{
"configurations": [
{
"type": "lldb",
"request": "launch",
"sourceLanguages": [
"swift"
],
"args": [],
"cwd": "${workspaceFolder:adwaita-swift}",
"name": "Debug Generation",
"program": "${workspaceFolder:adwaita-swift}/.build/debug/Generation",
"preLaunchTask": "swift: Build Debug Generation"
},
{
"type": "lldb",
"request": "launch",
"sourceLanguages": [
"swift"
],
"args": [],
"cwd": "${workspaceFolder:adwaita-swift}",
"name": "Release Generation",
"program": "${workspaceFolder:adwaita-swift}/.build/release/Generation",
"preLaunchTask": "swift: Build Release Generation"
},
{
"type": "lldb",
"request": "launch",
"sourceLanguages": [
"swift"
],
"args": [],
"cwd": "${workspaceFolder:adwaita-swift}",
"name": "Debug Demo",
"program": "${workspaceFolder:adwaita-swift}/.build/debug/Demo",
"preLaunchTask": "swift: Build Debug Demo"
},
{
"type": "lldb",
"request": "launch",
"sourceLanguages": [
"swift"
],
"args": [],
"cwd": "${workspaceFolder:adwaita-swift}",
"name": "Release Demo",
"program": "${workspaceFolder:adwaita-swift}/.build/release/Demo",
"preLaunchTask": "swift: Build Release Demo"
}
]
}

View File

@ -2,7 +2,7 @@
// ActionRow.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// Avatar.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// Banner.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// Bin.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// Box.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// Button.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// ButtonContent.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// Carousel.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// CenterBox.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// CheckButton.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// Clamp.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// ComboRow.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// EntryRow.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// ExpanderRow.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// FlowBox.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// HeaderBar.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// Label.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// LevelBar.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// LinkButton.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// ListBox.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// Menu.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// NavigationView.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// Overlay.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// OverlaySplitView.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// PasswordEntryRow.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -0,0 +1,157 @@
//
// Picture.swift
// Adwaita
//
// Created by auto-generation on 21.04.24.
//
import CAdw
import LevenshteinTransformations
/// The `GtkPicture` widget displays a `GdkPaintable`.
///
/// ![An example GtkPicture](picture.png)
///
/// Many convenience functions are provided to make pictures simple to use.
/// For example, if you want to load an image from a file, and then display
/// it, theres a convenience function to do this:
///
/// ```c
/// GtkWidget *widget = gtk_picture_new_for_filename ("myfile.png");
/// ```
///
/// If the file isnt loaded successfully, the picture will contain a
/// broken image icon similar to that used in many web browsers.
/// If you want to handle errors in loading the file yourself,
/// for example by displaying an error message, then load the image with
/// [ctor@Gdk.Texture.new_from_file], then create the `GtkPicture` with
/// [ctor@Gtk.Picture.new_for_paintable].
///
/// Sometimes an application will want to avoid depending on external data
/// files, such as image files. See the documentation of `GResource` for details.
/// In this case, [ctor@Gtk.Picture.new_for_resource] and
/// [method@Gtk.Picture.set_resource] should be used.
///
/// `GtkPicture` displays an image at its natural size. See [class@Gtk.Image]
/// if you want to display a fixed-size image, such as an icon.
///
/// ## Sizing the paintable
///
/// You can influence how the paintable is displayed inside the `GtkPicture`
/// by changing [property@Gtk.Picture:content-fit]. See [enum@Gtk.ContentFit]
/// for details. [property@Gtk.Picture:can-shrink] can be unset to make sure
/// that paintables are never made smaller than their ideal size - but
/// be careful if you do not know the size of the paintable in use (like
/// when displaying user-loaded images). This can easily cause the picture to
/// grow larger than the screen. And [property@Gtk.Widget:halign] and
/// [property@Gtk.Widget:valign] can be used to make sure the paintable doesn't
/// fill all available space but is instead displayed at its original size.
///
/// ## CSS nodes
///
/// `GtkPicture` has a single CSS node with the name `picture`.
///
/// ## Accessibility
///
/// `GtkPicture` uses the `GTK_ACCESSIBLE_ROLE_IMG` role.
public struct Picture: Widget {
/// Additional update functions for type extensions.
var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = []
/// Additional appear functions for type extensions.
var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = []
/// The accessible role of the given `GtkAccessible` implementation.
///
/// The accessible role cannot be changed once set.
var accessibleRole: String?
/// The alternative textual description for the picture.
var alternativeText: String?
/// If the `GtkPicture` can be made smaller than the natural size of its contents.
var canShrink: Bool?
/// Whether the GtkPicture will render its contents trying to preserve the aspect
/// ratio.
var keepAspectRatio: Bool?
/// The application.
var app: GTUIApp?
/// The window.
var window: GTUIApplicationWindow?
/// Initialize `Picture`.
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_picture_new()?.opaque())
update(storage, modifiers: modifiers, updateProperties: true)
for function in appearFunctions {
function(storage, modifiers)
}
return storage
}
/// Update the widget's view storage.
/// - Parameters:
/// - storage: The view storage.
/// - modifiers: The view modifiers.
/// - updateProperties: Whether to update the view's properties.
public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) {
storage.modify { widget in
if let alternativeText, updateProperties {
gtk_picture_set_alternative_text(widget, alternativeText)
}
if let canShrink, updateProperties {
gtk_picture_set_can_shrink(widget, canShrink.cBool)
}
if let keepAspectRatio, updateProperties {
gtk_picture_set_keep_aspect_ratio(widget, keepAspectRatio.cBool)
}
}
for function in updateFunctions {
function(storage, modifiers, updateProperties)
}
}
/// The accessible role of the given `GtkAccessible` implementation.
///
/// The accessible role cannot be changed once set.
public func accessibleRole(_ accessibleRole: String?) -> Self {
var newSelf = self
newSelf.accessibleRole = accessibleRole
return newSelf
}
/// The alternative textual description for the picture.
public func alternativeText(_ alternativeText: String?) -> Self {
var newSelf = self
newSelf.alternativeText = alternativeText
return newSelf
}
/// If the `GtkPicture` 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
}
/// Whether the GtkPicture will render its contents trying to preserve the aspect
/// ratio.
public func keepAspectRatio(_ keepAspectRatio: Bool? = true) -> Self {
var newSelf = self
newSelf.keepAspectRatio = keepAspectRatio
return newSelf
}
}

View File

@ -2,7 +2,7 @@
// Popover.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// PreferencesGroup.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// PreferencesPage.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// PreferencesRow.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// ProgressBar.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// ScrolledWindow.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// SearchBar.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// SearchEntry.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// SpinRow.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// Spinner.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// SplitButton.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// StatusPage.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// SwitchRow.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// ToastOverlay.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// ToggleButton.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -2,7 +2,7 @@
// ToolbarView.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw
@ -34,8 +34,8 @@ import LevenshteinTransformations
///
/// 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
/// [`.undershoot-top`](style-classes.html#undershoot-indicators) and
/// [`.undershoot-bottom`](style-classes.html#undershoot-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

View File

@ -2,7 +2,7 @@
// WindowTitle.swift
// Adwaita
//
// Created by auto-generation on 20.03.24.
// Created by auto-generation on 21.04.24.
//
import CAdw

View File

@ -0,0 +1,31 @@
//
// Picture+.swift
// Adwaita
//
// Created by david-swift on 21.04.24.
//
import CAdw
import Foundation
extension Picture {
/// Load the picture from Foundation's `Data`.
/// - Parameter data: The data.
/// - Returns: The view.
public func data(_ data: Data?) -> View {
inspect { storage in
let pointer = storage.pointer
guard let data else {
gtk_picture_set_paintable(pointer, gdk_paintable_new_empty(0, 0))
return
}
let bytes = data.withUnsafeBytes { ptr in
g_bytes_new(ptr.baseAddress, .init(data.count))
}
let texture = gdk_texture_new_from_bytes(bytes, nil)
gtk_picture_set_paintable(pointer, texture)
}
}
}

View File

@ -259,7 +259,8 @@ struct GenerationConfiguration {
bindings: [.init(property: "text")],
excludeProperties: ["input-hints", "input-purpose"]
),
.init(class: "SearchBar")
.init(class: "SearchBar"),
.init(class: "Picture", excludeProperties: ["content-fit", "file", "paintable"])
]
/// The unshortening map.

View File

@ -8,6 +8,7 @@
// swiftlint:disable missing_docs implicitly_unwrapped_optional no_magic_numbers
import Adwaita
import Foundation
@main
struct Demo: App {
@ -15,11 +16,14 @@ struct Demo: App {
let id = "io.github.AparokshaUI.Demo"
var app: GTUIApp!
@State private var pictureURL: URL?
var scene: Scene {
Window(id: "main") { window in
DemoContent(window: window, app: app)
DemoContent(window: window, app: app, pictureURL: pictureURL)
}
HelperWindows()
FileDialog(importer: "picture", extensions: ["jpg", "jpeg", "png", "svg"]) { pictureURL = $0 } onClose: { }
}
struct HelperWindows: WindowSceneGroup {
@ -75,6 +79,7 @@ struct Demo: App {
@State private var about = false
var window: GTUIApplicationWindow
var app: GTUIApp!
var pictureURL: URL?
var view: Body {
OverlaySplitView(visible: $sidebarVisible) {
@ -99,7 +104,7 @@ struct Demo: App {
selection.label,
icon: selection.icon,
description: selection.description
) { selection.view(app: app, window: window, toast: toast) }
) { selection.view(app: app, window: window, toast: toast, pictureURL: pictureURL) }
.topToolbar {
HeaderBar {
Toggle(icon: .default(icon: .sidebarShow), isOn: $sidebarVisible)

View File

@ -8,6 +8,7 @@
// swiftlint:disable missing_docs implicitly_unwrapped_optional
import Adwaita
import Foundation
enum Page: String, Identifiable, CaseIterable, Codable {
@ -27,6 +28,7 @@ enum Page: String, Identifiable, CaseIterable, Codable {
case popover
case flowBox
case navigationView
case picture
var id: Self {
self
@ -90,12 +92,14 @@ enum Page: String, Identifiable, CaseIterable, Codable {
return "Display views in a reflowing grid"
case .navigationView:
return "A page-based navigation container"
case .picture:
return "Display an image"
}
}
// swiftlint:disable cyclomatic_complexity
@ViewBuilder
func view(app: GTUIApp!, window: GTUIApplicationWindow, toast: Signal) -> Body {
func view(app: GTUIApp!, window: GTUIApplicationWindow, toast: Signal, pictureURL: URL?) -> Body {
switch self {
case .welcome:
[]
@ -129,6 +133,8 @@ enum Page: String, Identifiable, CaseIterable, Codable {
FlowBoxDemo()
case .navigationView:
NavigationViewDemo(app: app)
case .picture:
PictureDemo(url: pictureURL, app: app, window: window)
}
}
// swiftlint:enable cyclomatic_complexity

41
Tests/PictureDemo.swift Normal file
View File

@ -0,0 +1,41 @@
//
// PictureDemo.swift
// Adwaita
//
// Created by david-swift on 21.04.24.
//
// swiftlint:disable missing_docs
import Adwaita
import CAdw
import Foundation
struct PictureDemo: View {
var url: URL?
var app: GTUIApp
var window: GTUIWindow
var data: Data {
guard let url, let data = try? Data(contentsOf: url) else {
return .init()
}
return data
}
var view: Body {
Picture()
.data(data)
Button("Import") {
app.addWindow("picture", parent: window)
}
.halign(.center)
.style("pill")
.style("suggested-action")
.padding()
}
}
// swiftlint:enable missing_docs