forked from aparoksha/adwaita-swift
339 lines
12 KiB
Swift
339 lines
12 KiB
Swift
//
|
||
// AdwaitaAboutDialogConfig.swift
|
||
// Adwaita
|
||
//
|
||
// Created by lambdaclan on 09.01.2026.
|
||
//
|
||
|
||
import CAdw
|
||
import Foundation
|
||
|
||
/// A link shown in the About dialog.
|
||
public struct AboutLink {
|
||
|
||
/// The link title.
|
||
public var title: String
|
||
|
||
/// The destination URL.
|
||
public var url: URL?
|
||
|
||
/// Create a new link.
|
||
/// - Parameters:
|
||
/// - title: The link title.
|
||
/// - url: The destination URL.
|
||
public init(title: String, url: URL?) {
|
||
self.title = title
|
||
self.url = url
|
||
}
|
||
|
||
}
|
||
|
||
/// The type of contribution made by a credited person.
|
||
public enum ContributionRole {
|
||
|
||
/// A person who contributed to development.
|
||
case developer
|
||
/// A person who contributed to design.
|
||
case designer
|
||
/// A person who contributed artwork or visuals.
|
||
case artist
|
||
/// A person who contributed translations.
|
||
case translator
|
||
|
||
}
|
||
|
||
/// A credited person and their contribution role.
|
||
public struct CreditEntry {
|
||
|
||
/// The contributor's role.
|
||
public var role: ContributionRole
|
||
|
||
/// The contributor's name.
|
||
public var name: String
|
||
|
||
/// Create a new credit entry.
|
||
/// - Parameters:
|
||
/// - role: The contributor's role.
|
||
/// - name: The contributor's name.
|
||
public init(role: ContributionRole, name: String) {
|
||
self.role = role
|
||
self.name = name
|
||
}
|
||
|
||
}
|
||
|
||
/// An acknowledgement entry.
|
||
public struct AcknowledgementEntry {
|
||
|
||
/// The acknowledgement section title.
|
||
public var title: String
|
||
|
||
/// Acknowledged person/organization name.
|
||
public var name: String
|
||
|
||
/// Create a new acknowledgement entry.
|
||
/// - Parameters:
|
||
/// - title: The acknowledgement section title.
|
||
/// - name: The acknowledged person or organization.
|
||
public init(title: String, name: String) {
|
||
self.title = title
|
||
self.name = name
|
||
}
|
||
|
||
}
|
||
|
||
/// 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><p></code>, ordered lists using
|
||
<code><ol></code>, and unordered lists using <code><ul></code>.
|
||
Both list types must contain list items marked with <code><li></code>.</p>
|
||
<p>Within paragraphs and list items, you may use <code><em></code> to apply
|
||
<em>emphasis</em>(italic text) and <code><code></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><p></code>, <code><ol></code>,
|
||
<code><ul></code>, or <code><li></code> tags is ignored by the template
|
||
processor.</p>
|
||
<ol>
|
||
<li>Ordered list items represent numbered content and may
|
||
include <code><em></code> for <em>emphasis</em> or <code><code></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 Pango‑markup comments showcasing how styled text, links, and
|
||
/// formatting behave inside the About dialog’s “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">Adwaita‑Swift 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 link for getting support.
|
||
public var support: URL?
|
||
/// Additional links related to the app.
|
||
public var links: [AboutLink]?
|
||
/// The app's copyright information.
|
||
public var copyright: String?
|
||
/// The app's license.
|
||
public var license: String?
|
||
/// The app's release notes.
|
||
public var releaseNotes: String?
|
||
/// The comments about the application.
|
||
public var comments: String?
|
||
/// Recognition by name and role of contributors.
|
||
public var credits: [CreditEntry]?
|
||
/// Acknowledgements to display in the dialog.
|
||
public var acknowledgements: [AcknowledgementEntry]?
|
||
|
||
/// 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.
|
||
/// - support: Website for getting support.
|
||
/// - links: Additional links related to the app.
|
||
/// - copyright: The app's copyright information.
|
||
/// - license: The app's license.
|
||
/// - releaseNotes: The app's release notes.
|
||
/// - comments: The comments about the application.
|
||
/// - credits: Recognition by name and role of contributors.
|
||
/// - acknowledgements: List of acknowledgements.
|
||
public init(
|
||
appName: String? = nil,
|
||
developer: String? = nil,
|
||
version: String? = nil,
|
||
icon: Icon? = nil,
|
||
website: URL? = nil,
|
||
issues: URL? = nil,
|
||
support: URL? = nil,
|
||
links: [AboutLink]? = nil,
|
||
copyright: String? = nil,
|
||
license: String? = nil,
|
||
releaseNotes: String? = nil,
|
||
comments: String? = nil,
|
||
credits: [CreditEntry]? = nil,
|
||
acknowledgements: [AcknowledgementEntry]? = nil
|
||
) {
|
||
self.appName = appName
|
||
self.developer = developer
|
||
self.version = version
|
||
self.icon = icon
|
||
self.website = website
|
||
self.issues = issues
|
||
self.support = support
|
||
self.links = links
|
||
self.copyright = copyright
|
||
self.license = license
|
||
self.releaseNotes = releaseNotes
|
||
self.comments = comments
|
||
self.credits = credits
|
||
self.acknowledgements = acknowledgements
|
||
}
|
||
|
||
/// Applies a string value to the dialog using the given setter.
|
||
/// - Parameters:
|
||
/// - value: The optional string to apply.
|
||
/// - setter: The C function that sets the value on the dialog.
|
||
/// - dialog: The dialog instance.
|
||
@inline(__always)
|
||
private func set(
|
||
_ value: String?,
|
||
using setter: (OpaquePointer, UnsafePointer<CChar>?) -> Void,
|
||
on dialog: OpaquePointer
|
||
) {
|
||
if let value {
|
||
setter(dialog, value)
|
||
}
|
||
}
|
||
|
||
/// Applies a list of links to the dialog.
|
||
/// - Parameters:
|
||
/// - links: The optional list of links.
|
||
/// - dialog: The dialog instance.
|
||
@inline(__always)
|
||
private func set(
|
||
_ links: [AboutLink]?,
|
||
on dialog: OpaquePointer
|
||
) {
|
||
links?.forEach { link in
|
||
adw_about_dialog_add_link(dialog, link.title, link.url?.absoluteString)
|
||
}
|
||
}
|
||
|
||
/// Applies credit entries to the dialog.
|
||
/// - Parameters:
|
||
/// - credits: The optional list of credit entries.
|
||
/// - dialog: The dialog instance.
|
||
@inline(__always)
|
||
private func set(
|
||
_ credits: [CreditEntry]?,
|
||
on dialog: OpaquePointer
|
||
) {
|
||
if let credits {
|
||
var developers: [String] = []
|
||
var designers: [String] = []
|
||
var artists: [String] = []
|
||
var translators: [String] = []
|
||
|
||
for entry in credits {
|
||
switch entry.role {
|
||
case .developer:
|
||
developers.append(entry.name)
|
||
case .designer:
|
||
designers.append(entry.name)
|
||
case .artist:
|
||
artists.append(entry.name)
|
||
case .translator:
|
||
translators.append(entry.name)
|
||
}
|
||
}
|
||
|
||
if let ptr = developers.cMutableArray, !developers.isEmpty {
|
||
adw_about_dialog_set_developers(dialog, ptr)
|
||
}
|
||
|
||
if let ptr = designers.cMutableArray, !designers.isEmpty {
|
||
adw_about_dialog_set_designers(dialog, ptr)
|
||
}
|
||
|
||
if let ptr = artists.cMutableArray, !artists.isEmpty {
|
||
adw_about_dialog_set_artists(dialog, ptr)
|
||
}
|
||
|
||
if !translators.isEmpty {
|
||
let joined = translators.joined(separator: "\n")
|
||
adw_about_dialog_set_translator_credits(dialog, joined)
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Applies acknowledgement entries to the dialog.
|
||
/// - Parameters:
|
||
/// - acknowledgements: The optional list of acknowledgement entries.
|
||
/// - dialog: The dialog instance.
|
||
@inline(__always)
|
||
private func set(
|
||
_ acknowledgements: [AcknowledgementEntry]?,
|
||
on dialog: OpaquePointer
|
||
) {
|
||
if let acknowledgements {
|
||
let grouped = Dictionary(grouping: acknowledgements) { $0.title }
|
||
|
||
for (title, entries) in grouped {
|
||
let names = entries.map { $0.name }
|
||
guard let people = names.cMutableArray else { continue }
|
||
|
||
adw_about_dialog_add_acknowledgement_section(dialog, title, people)
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 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) {
|
||
set(appName, using: adw_about_dialog_set_application_name, on: dialog)
|
||
set(developer, using: adw_about_dialog_set_developer_name, on: dialog)
|
||
set(version, using: adw_about_dialog_set_version, on: dialog)
|
||
set(icon?.string, using: adw_about_dialog_set_application_icon, on: dialog)
|
||
set(website?.absoluteString, using: adw_about_dialog_set_website, on: dialog)
|
||
set(issues?.absoluteString, using: adw_about_dialog_set_issue_url, on: dialog)
|
||
set(support?.absoluteString, using: adw_about_dialog_set_support_url, on: dialog)
|
||
set(copyright, using: adw_about_dialog_set_copyright, on: dialog)
|
||
set(license, using: adw_about_dialog_set_license, on: dialog)
|
||
set(releaseNotes, using: adw_about_dialog_set_release_notes, on: dialog)
|
||
set(comments, using: adw_about_dialog_set_comments, on: dialog)
|
||
set(links, on: dialog)
|
||
set(credits, on: dialog)
|
||
set(acknowledgements, on: dialog)
|
||
}
|
||
|
||
}
|