// // 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 = """

This template supports three structures: paragraphs using <p>, ordered lists using <ol>, and unordered lists using <ul>. Both list types must contain list items marked with <li>.

Within paragraphs and list items, you may use <em> to apply emphasis(italic text) and <code> to mark inline code for monospaced text. These inline styles are supported only inside those elements.

Any text placed outside <p>, <ol>, <ul>, or <li> tags is ignored by the template processor.

  1. Ordered list items represent numbered content and may include <em> for emphasis or <code> for inline code.
  2. They follow the same rules as paragraphs regarding allowed inline styles.
""" /// 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: • bolditaliccolored text Full reference: Pango Markup Reference Example markup: Demo Title Highlighted text Underlined text Useful links: • Adwaita‑Swift Documentation 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?) -> 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) } }