Compare commits

...

4 Commits

4 changed files with 162 additions and 77 deletions

View File

@ -12,30 +12,15 @@ extension AnyView {
/// Add an about dialog to the parent window.
/// - Parameters:
/// - visible: Whether the dialog is presented.
/// - app: The app's name.
/// - developer: The developer's name.
/// - version: The version string.
/// - icon: The app icon.
/// - website: The app's website.
/// - issues: Website for reporting issues.
/// - configure: A closure that mutates the dialog configuration.
public func aboutDialog(
visible: Binding<Bool>,
app: String? = nil,
developer: String? = nil,
version: String? = nil,
icon: Icon? = nil,
website: URL? = nil,
issues: URL? = nil
configure: (inout AdwaitaAboutDialogConfig) -> Void
) -> AnyView {
AboutDialog(
visible: visible,
child: self,
appName: app,
developer: developer,
version: version,
icon: icon,
website: website,
issues: issues
configure: configure
)
}

View File

@ -0,0 +1,136 @@
//
// AdwaitaAboutDialogConfig.swift
// Adwaita
//
// Created by lambdaclan on 09.01.2026.
//
import CAdw
import Foundation
/// Initialization options for the about dialog wrapper.
public struct AdwaitaAboutDialogConfig {
/// Example HTML release notes used for demonstrating how the About dialog
/// renders structured content such as paragraphs, lists, and inline markup.
/// This sample is intended for testing, previews, and documentation.
public static let demoReleaseNotes = """
<p>This template supports three structures: paragraphs using <code>&lt;p&gt;</code>, ordered lists using
<code>&lt;ol&gt;</code>, and unordered lists using <code>&lt;ul&gt;</code>.
Both list types must contain list items marked with <code>&lt;li&gt;</code>.</p>
<p>Within paragraphs and list items, you may use <code>&lt;em&gt;</code> to apply
<em>emphasis</em>(italic text) and <code>&lt;code&gt;</code> to mark <code>inline code</code>
for monospaced text.
These inline styles are supported only inside those elements.</p>
<p>Any text placed outside <code>&lt;p&gt;</code>, <code>&lt;ol&gt;</code>,
<code>&lt;ul&gt;</code>, or <code>&lt;li&gt;</code> tags is ignored by the template
processor.</p>
<ol>
<li>Ordered list items represent numbered content and may
include <code>&lt;em&gt;</code> for <em>emphasis</em> or <code>&lt;code&gt;</code> for inline code.</li>
<li>They follow the same rules as paragraphs regarding allowed inline styles.</li>
</ol>
<ul>
<li>Unordered list items represent bullet points and support the same inline styles.</li>
<li>They must contain only text and allowed inline formatting.</li>
</ul>
"""
/// Example Pangomarkup comments showcasing how styled text, links, and
/// formatting behave inside the About dialogs Details section.
/// Useful for previews, testing, and as a reference for developers who want
/// to embed formatted text in their own dialogs.
public static let demoComments = """
This text demonstrates basic Pango markup along with helpful documentation links.
Comments shown in an Adwaita AboutDialog will appear on the Details page.
They can be long and detailed, and they may include links and Pango markup for
formatting.
Pango markup supports tags like:
<b>bold</b>
<i>italic</i>
<span foreground="steelblue">colored text</span>
Full reference: <a href="https://docs.gtk.org/Pango/pango_markup.html">Pango Markup
Reference</a>
Example markup:
<span font="14pt" weight="bold">Demo Title</span>
<span foreground="tomato">Highlighted text</span>
<u>Underlined text</u>
Useful links:
<a href="https://adwaita-swift.aparoksha.dev/documentation/adwaita">AdwaitaSwift Documentation</a>
You can embed these links directly in your UI using Pango markup.
"""
/// The app's name.
public var appName: String?
/// The developer's name.
public var developer: String?
/// The app version.
public var version: String?
/// The app icon.
public var icon: Icon?
/// The app's website.
public var website: URL?
/// The link for opening issues.
public var issues: URL?
/// The app's release notes
public var releaseNotes: String?
/// The comments about the application
public var comments: String?
/// Initialize the about dialog wrapper.
/// - Parameters:
/// - appName: The app's name.
/// - developer: The developer's name.
/// - version: The version string.
/// - icon: The app icon.
/// - website: The app's website.
/// - issues: Website for reporting issues.
/// - releaseNotes: The app's release notes.
/// - comments: The comments about the application.
public init(
appName: String? = nil,
developer: String? = nil,
version: String? = nil,
icon: Icon? = nil,
website: URL? = nil,
issues: URL? = nil,
releaseNotes: String? = nil,
comments: String? = nil
) {
self.appName = appName
self.developer = developer
self.version = version
self.icon = icon
self.website = website
self.issues = issues
self.releaseNotes = releaseNotes
self.comments = comments
}
/// Apply the configuration values to the given dialog.
/// - Parameters:
/// - dialog: The underlying Adwaita dialog instance to update with the configuration.
func apply(to dialog: OpaquePointer) {
let handlers: [(String?, (OpaquePointer, UnsafePointer<CChar>?) -> Void)] = [
(appName, adw_about_dialog_set_application_name),
(developer, adw_about_dialog_set_developer_name),
(version, adw_about_dialog_set_version),
(icon?.string, adw_about_dialog_set_application_icon),
(website?.absoluteString, adw_about_dialog_set_website),
(issues?.absoluteString, adw_about_dialog_set_issue_url),
(releaseNotes, adw_about_dialog_set_release_notes),
(comments, adw_about_dialog_set_comments)
]
for (value, action) in handlers {
value.map { action(dialog, $0) }
}
}
}

View File

@ -15,19 +15,8 @@ struct AboutDialog: AdwaitaWidget {
@Binding var visible: Bool
/// The wrapped view.
var child: AnyView
/// The app's name.
var appName: String?
/// The developer's name.
var developer: String?
/// The app version.
var version: String?
/// The app icon.
var icon: Icon?
/// The app's website.
var website: URL?
/// The link for opening issues.
var issues: URL?
/// The dialog configuration options.
var config: AdwaitaAboutDialogConfig
/// The ID for the dialog's storage.
let dialogID = "dialog"
@ -36,30 +25,18 @@ struct AboutDialog: AdwaitaWidget {
/// - Parameters:
/// - visible: The visibility.
/// - child: The child view.
/// - appName: The app's name.
/// - developer: The developer's name.
/// - version: The version.
/// - icon: The icon.
/// - website: The website's URL.
/// - issues: The link for opening issues.
/// - configure: A closure that mutates the dialog configuration.
init(
visible: Binding<Bool>,
child: AnyView,
appName: String? = nil,
developer: String? = nil,
version: String? = nil,
icon: Icon? = nil,
website: URL? = nil,
issues: URL? = nil
configure: (inout AdwaitaAboutDialogConfig) -> Void
) {
self._visible = visible
self.child = child
self.appName = appName
self.developer = developer
self.version = version
self.icon = icon
self.website = website
self.issues = issues
var cfg = AdwaitaAboutDialogConfig()
configure(&cfg)
self.config = cfg
}
/// The view storage.
@ -97,26 +74,12 @@ struct AboutDialog: AdwaitaWidget {
storage.opaquePointer?.cast()
)
}
let dialog = storage.content[dialogID]?.first?.opaquePointer
if let appName {
adw_about_dialog_set_application_name(dialog, appName)
guard let dialog = storage.content[dialogID]?.first?.opaquePointer else {
return
}
if let developer {
adw_about_dialog_set_developer_name(dialog, developer)
}
if let version {
adw_about_dialog_set_version(dialog, version)
}
if let icon {
adw_about_dialog_set_application_icon(dialog, icon.string)
}
if let website {
adw_about_dialog_set_website(dialog, website.absoluteString)
}
if let issues {
adw_about_dialog_set_issue_url(dialog, issues.absoluteString)
}
adw_dialog_set_content_height(dialog?.cast(), -1)
config.apply(to: dialog)
adw_dialog_set_content_height(dialog.cast(), -1)
} else {
if storage.content[dialogID]?.first != nil {
let dialog = storage.content[dialogID]?.first?.opaquePointer

View File

@ -150,15 +150,16 @@ struct Demo: App {
}
.collapsed(!wide)
.breakpoint(minWidth: 550, matches: $wide)
.aboutDialog(
visible: $about,
app: "Demo",
developer: "david-swift",
version: "Test",
icon: .default(icon: .applicationXExecutable),
website: .init(string: "https://adwaita-swift.aparoksha.dev/"),
issues: .init(string: "https://git.aparoksha.dev/aparoksha/adwaita-swift/issues")
)
.aboutDialog(visible: $about) { cfg in
cfg.appName = "Demo"
cfg.developer = "david-swift"
cfg.version = "Test"
cfg.icon = .default(icon: .applicationXExecutable)
cfg.website = URL(string: "https://adwaita-swift.aparoksha.dev/")
cfg.issues = URL(string: "https://git.aparoksha.dev/aparoksha/adwaita-swift/issues")
cfg.releaseNotes = AdwaitaAboutDialogConfig.demoReleaseNotes
cfg.comments = AdwaitaAboutDialogConfig.demoComments
}
.preferencesDialog(visible: $preferences)
.preferencesPage("Page 1", icon: .default(icon: .audioHeadset)) { page in
page