Exemples de Fonctionnalités de Bureau macOS Swift

Exemples de fonctionnalités spécifiques au bureau macOS Swift incluant les boîtes de dialogue de fichiers, les boîtes de messages et la barre d'état

Key Facts

Category
Swift
Items
3
Format Families
sample

Sample Overview

Exemples de fonctionnalités spécifiques au bureau macOS Swift incluant les boîtes de dialogue de fichiers, les boîtes de messages et la barre d'état This sample set belongs to Swift and can be used to test related workflows inside Elysia Tools.

💻 Boîtes de Message swift

🟢 simple ⭐⭐

Afficher des boîtes de dialogue d'alerte, de confirmation et des boîtes de message personnalisées en utilisant NSAlert

⏱️ 25 min 🏷️ swift, macos, desktop, gui
Prerequisites: Basic Swift, Cocoa/AppKit
// macOS Swift Message Boxes Examples
// Using NSAlert for various dialog types

import Cocoa
import Foundation

// 1. Simple Alert Dialog
class SimpleAlertDialog {

    func showAlert(title: String, message: String) {
        print("\n--- Simple Alert ---")
        print("Title: \(title)")
        print("Message: \(message)")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.alertStyle = .informational
        alert.addButton(withTitle: "OK")

        let response = alert.runModal()
        print("Response: \(response.rawValue)")
    }

    func showError(title: String, message: String) {
        print("\n--- Error Alert ---")
        print("Title: \(title)")
        print("Message: \(message)")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.alertStyle = .critical
        alert.addButton(withTitle: "OK")

        alert.runModal()
    }

    func showWarning(title: String, message: String) {
        print("\n--- Warning Alert ---")
        print("Title: \(title)")
        print("Message: \(message)")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.alertStyle = .warning
        alert.addButton(withTitle: "OK")

        alert.runModal()
    }
}

// 2. Confirmation Dialog
class ConfirmationDialog {

    enum Response {
        case yes
        case no
        case cancelled

        init(_ modalResponse: NSApplication.ModalResponse) {
            switch modalResponse {
            case .alertFirstButtonReturn:
                self = .yes
            case .alertSecondButtonReturn:
                self = .no
            default:
                self = .cancelled
            }
        }
    }

    func confirm(title: String, message: String) -> Response {
        print("\n--- Confirmation Dialog ---")
        print("Question: \(message)")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.alertStyle = .informational
        alert.addButton(withTitle: "Yes")
        alert.addButton(withTitle: "No")

        let response = Response(alert.runModal())
        print("User response: \(response)")

        return response
    }

    func confirmDestructive(title: String, message: String, item: String) -> Response {
        print("\n--- Destructive Confirmation ---")
        print("Warning: This action cannot be undone")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message + "\n\nAre you sure you want to delete '\(item)'?"
        alert.alertStyle = .critical
        alert.addButton(withTitle: "Delete")
        alert.addButton(withTitle: "Cancel")

        let response = Response(alert.runModal())
        return response
    }

    func confirmWithThirdOption(title: String, message: String) -> Int {
        print("\n--- Three-Option Confirmation ---")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.alertStyle = .informational
        alert.addButton(withTitle: "Yes")
        alert.addButton(withTitle: "No")
        alert.addButton(withTitle: "Cancel")

        let response = alert.runModal()
        print("Response button: \(response.rawValue - NSApplication.ModalResponse.alertFirstButtonReturn.rawValue)")

        return response.rawValue - NSApplication.ModalResponse.alertFirstButtonReturn.rawValue
    }
}

// 3. Custom Alert with Icon
class CustomIconAlert {

    func showWithIcon(title: String, message: String, icon: NSImage?) {
        print("\n--- Custom Icon Alert ---")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.alertStyle = .informational

        if let icon = icon {
            alert.icon = icon
            print("Custom icon set: \(icon.size)")
        }

        alert.addButton(withTitle: "OK")
        alert.runModal()
    }

    func showWithSystemIcon(title: String, message: String, iconName: String) {
        print("\n--- System Icon Alert ---")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message

        if let icon = NSImage(systemSymbolName: iconName, accessibilityDescription: nil) {
            alert.icon = icon
            print("Using system icon: \(iconName)")
        }

        alert.alertStyle = .informational
        alert.addButton(withTitle: "OK")
        alert.runModal()
    }
}

// 4. Input Dialog
class InputDialog {

    func showTextInput(title: String, message: String, defaultValue: String = "") -> String? {
        print("\n--- Text Input Dialog ---")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.alertStyle = .informational
        alert.addButton(withTitle: "OK")
        alert.addButton(withTitle: "Cancel")

        let input = NSTextField(frame: NSRect(x: 0, y: 0, width: 300, height: 24))
        input.stringValue = defaultValue
        alert.accessoryView = input

        alert.window.initialFirstResponder = input

        let response = alert.runModal()

        if response == .alertFirstButtonReturn {
            let value = input.stringValue
            print("Input: \(value)")
            return value
        }

        print("Input cancelled")
        return nil
    }

    func showPasswordInput(title: String, message: String) -> String? {
        print("\n--- Password Input Dialog ---")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.alertStyle = .informational
        alert.addButton(withTitle: "OK")
        alert.addButton(withTitle: "Cancel")

        let input = NSSecureTextField(frame: NSRect(x: 0, y: 0, width: 300, height: 24))
        alert.accessoryView = input

        alert.window.initialFirstResponder = input

        let response = alert.runModal()

        if response == .alertFirstButtonReturn {
            let password = input.stringValue
            print("Password received: \(password.count) characters")
            return password
        }

        return nil
    }

    func showMultiLineInput(title: String, message: String, defaultValue: String = "") -> String? {
        print("\n--- Multi-line Input Dialog ---")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.alertStyle = .informational
        alert.addButton(withTitle: "OK")
        alert.addButton(withTitle: "Cancel")

        let scrollView = NSScrollView(frame: NSRect(x: 0, y: 0, width: 400, height: 100))
        let textView = NSTextView()
        textView.textContainer?.containerSize = NSSize(width: 400, height: 100)
        textView.textContainer?.widthTracksTextView = true
        textView.isHorizontallyResizable = false
        textView.isVerticallyResizable = true
        textView.autoresizingMask = .height
        textView.string = defaultValue

        scrollView.documentView = textView
        scrollView.hasVerticalScroller = true
        alert.accessoryView = scrollView

        alert.window.initialFirstResponder = textView

        let response = alert.runModal()

        if response == .alertFirstButtonReturn {
            let value = textView.string
            print("Multi-line input: \(value.count) characters")
            return value
        }

        return nil
    }
}

// 5. Selection Dialog
class SelectionDialog {

    func showChoice(title: String, message: String, options: [String]) -> Int? {
        print("\n--- Selection Dialog ---")
        print("Options: \(options)")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.alertStyle = .informational

        for option in options {
            alert.addButton(withTitle: option)
        }

        let response = alert.runModal()
        let selectedIndex = Int(response.rawValue - NSApplication.ModalResponse.alertFirstButtonReturn.rawValue)

        if selectedIndex >= 0 && selectedIndex < options.count {
            print("Selected: \(options[selectedIndex]) (index: \(selectedIndex))")
            return selectedIndex
        }

        return nil
    }

    func showRadioGroup(title: String, message: String, options: [(label: String, tag: Int)]) -> Int? {
        print("\n--- Radio Group Dialog ---")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.alertStyle = .informational
        alert.addButton(withTitle: "OK")
        alert.addButton(withTitle: "Cancel")

        let radioGroup = NSStackView()
        radioGroup.orientation = .vertical
        radioGroup.spacing = 8

        var buttons: [NSButton] = []

        for (index, option) in options.enumerated() {
            let button = NSButton(radioButtonWithTitle: option.label, target: nil, action: nil)
            button.tag = option.tag
            if index == 0 { button.state = .on }
            buttons.append(button)
            radioGroup.addArrangedSubview(button)
        }

        alert.accessoryView = radioGroup

        let response = alert.runModal()

        if response == .alertFirstButtonReturn,
           let selected = buttons.first(where: { $0.state == .on }) {
            print("Selected: \(selected.title) (tag: \(selected.tag))")
            return selected.tag
        }

        return nil
    }
}

// 6. Progress Dialog
class ProgressDialog {

    func showProgress(title: String, message: String, maxValue: Double = 100) {
        print("\n--- Progress Dialog ---")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.alertStyle = .informational

        let progress = NSProgressIndicator(frame: NSRect(x: 0, y: 0, width: 300, height: 20))
        progress.minValue = 0
        progress.maxValue = maxValue
        progress.isIndeterminate = false
        alert.accessoryView = progress

        progress.startAnimation(nil)

        // Show as sheet or non-modal
        alert.window.level = .floating
        alert.runModal()

        progress.stopAnimation(nil)
    }

    func showIndeterminateProgress(title: String, message: String) {
        print("\n--- Indeterminate Progress Dialog ---")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.alertStyle = .informational

        let progress = NSProgressIndicator(frame: NSRect(x: 0, y: 0, width: 300, height: 20))
        progress.style = .spinning
        progress.isIndeterminate = true
        alert.accessoryView = progress

        progress.startAnimation(nil)
        alert.runModal()
        progress.stopAnimation(nil)
    }
}

// 7. Sheet Dialog (Attached to Window)
class SheetDialog {

    func showSheet(window: NSWindow, title: String, message: String) {
        print("\n--- Sheet Dialog ---")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.alertStyle = .informational
        alert.addButton(withTitle: "OK")

        alert.beginSheetModal(for: window) { response in
            print("Sheet closed with response: \(response.rawValue)")
        }
    }

    func showSheetConfirm(window: NSWindow, title: String, message: String, completion: @escaping (Bool) -> Void) {
        print("\n--- Sheet Confirmation ---")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.alertStyle = .warning
        alert.addButton(withTitle: "Yes")
        alert.addButton(withTitle: "No")

        alert.beginSheetModal(for: window) { response in
            let confirmed = response == .alertFirstButtonReturn
            print("Sheet confirm: \(confirmed)")
            completion(confirmed)
        }
    }
}

// 8. Toast Notification
class ToastNotification {

    func show(message: String, duration: TimeInterval = 3) {
        print("\n--- Toast Notification ---")
        print("Message: \(message)")

        let alert = NSAlert()
        alert.messageText = ""
        alert.informativeText = message
        alert.alertStyle = .informational
        alert.showsSuppressionButton = true
        alert.suppressionButton?.title = ""

        // Auto-dismiss after duration
        DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
            alert.window.close()
        }

        alert.runModal()
    }

    func showInWindow(window: NSWindow, message: String, position: NSRect) {
        print("\n--- Toast in Window ---")

        let toast = NSPanel(
            contentRect: NSRect(x: 0, y: 0, width: 300, height: 50),
            styleMask: [.borderless],
            backing: .buffered,
            defer: false
        )

        let label = NSTextField(labelWithString: message)
        label.frame = NSRect(x: 10, y: 10, width: 280, height: 30)
        label.alignment = .center
        toast.contentView?.addSubview(label)

        toast.level = .floating
        toast.alphaValue = 0.9

        window.addChildWindow(toast, ordered: .above)

        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            window.removeChildWindow(toast)
            toast.close()
        }
    }
}

// 9. Login Dialog
class LoginDialog {

    func showLogin() -> (username: String, password: String)? {
        print("\n--- Login Dialog ---")

        let alert = NSAlert()
        alert.messageText = "Login"
        alert.informativeText = "Enter your credentials"
        alert.alertStyle = .informational
        alert.addButton(withTitle: "Login")
        alert.addButton(withTitle: "Cancel")

        let view = NSView(frame: NSRect(x: 0, y: 0, width: 250, height: 70))

        let usernameLabel = NSTextField(labelWithString: "Username:")
        usernameLabel.frame = NSRect(x: 0, y: 45, width: 80, height: 20)
        view.addSubview(usernameLabel)

        let usernameField = NSTextField(frame: NSRect(x: 90, y: 45, width: 150, height: 20))
        view.addSubview(usernameField)

        let passwordLabel = NSTextField(labelWithString: "Password:")
        passwordLabel.frame = NSRect(x: 0, y: 15, width: 80, height: 20)
        view.addSubview(passwordLabel)

        let passwordField = NSSecureTextField(frame: NSRect(x: 90, y: 15, width: 150, height: 20))
        view.addSubview(passwordField)

        alert.accessoryView = view
        alert.window.initialFirstResponder = usernameField

        let response = alert.runModal()

        if response == .alertFirstButtonReturn {
            let username = usernameField.stringValue
            let password = passwordField.stringValue
            print("Login attempt for: \(username)")
            return (username, password)
        }

        return nil
    }
}

// 10. Complex Dialog with Multiple Controls
class ComplexDialog {

    func showPreferences() -> [String: Any]? {
        print("\n--- Complex Preferences Dialog ---")

        let alert = NSAlert()
        alert.messageText = "Preferences"
        alert.alertStyle = .informational
        alert.addButton(withTitle: "Save")
        alert.addButton(withTitle: "Cancel")

        let view = NSView(frame: NSRect(x: 0, y: 0, width: 300, height: 180))

        // Checkbox 1
        let checkbox1 = NSButton(checkboxWithTitle: "Enable notifications", target: nil, action: nil)
        checkbox1.state = .on
        checkbox1.frame = NSRect(x: 10, y: 150, width: 280, height: 20)
        view.addSubview(checkbox1)

        // Checkbox 2
        let checkbox2 = NSButton(checkboxWithTitle: "Auto-save documents", target: nil, action: nil)
        checkbox2.state = .on
        checkbox2.frame = NSRect(x: 10, y: 120, width: 280, height: 20)
        view.addSubview(checkbox2)

        // Popup button
        let popupLabel = NSTextField(labelWithString: "Theme:")
        popupLabel.frame = NSRect(x: 10, y: 95, width: 80, height: 17)
        view.addSubview(popupLabel)

        let popup = NSPopUpButton(frame: NSRect(x: 100, y: 90, width: 190, height: 25))
        popup.addItem(withTitle: "Light")
        popup.addItem(withTitle: "Dark")
        popup.addItem(withTitle: "Auto")
        view.addSubview(popup)

        // Slider
        let sliderLabel = NSTextField(labelWithString: "Font size:")
        sliderLabel.frame = NSRect(x: 10, y: 65, width: 80, height: 17)
        view.addSubview(sliderLabel)

        let slider = NSSlider(frame: NSRect(x: 100, y: 60, width: 190, height: 20))
        slider.minValue = 10
        slider.maxValue = 24
        slider.integerValue = 13
        view.addSubview(slider)

        // Text field
        let textFieldLabel = NSTextField(labelWithString: "Name:")
        textFieldLabel.frame = NSRect(x: 10, y: 35, width: 80, height: 17)
        view.addSubview(textFieldLabel)

        let nameField = NSTextField(frame: NSRect(x: 100, y: 32, width: 190, height: 22))
        view.addSubview(nameField)

        alert.accessoryView = view

        let response = alert.runModal()

        if response == .alertFirstButtonReturn {
            let preferences: [String: Any] = [
                "notifications": checkbox1.state == .on,
                "autoSave": checkbox2.state == .on,
                "theme": popup.titleOfSelectedItem ?? "",
                "fontSize": slider.integerValue,
                "name": nameField.stringValue
            ]
            print("Preferences saved: \(preferences)")
            return preferences
        }

        return nil
    }
}

// 11. Alert with Checkbox
class CheckboxAlert {

    func showAlertWithCheckbox(title: String, message: String, checkboxText: String, completion: @escaping (Bool, Bool) -> Void) {
        print("\n--- Alert with Checkbox ---")

        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.alertStyle = .informational
        alert.addButton(withTitle: "OK")

        alert.showsSuppressionButton = true
        alert.suppressionButton?.title = checkboxText

        let response = alert.runModal()
        let checked = alert.suppressionButton?.state == .on
        let okPressed = response == .alertFirstButtonReturn

        print("OK pressed: \(okPressed), Checkbox: \(checked)")
        completion(okPressed, checked)
    }

    func showDontAskAgain(title: String, message: String) {
        showAlertWithCheckbox(title: title, message: message, checkboxText: "Don't ask again") { _, dontAsk in
            if dontAsk {
                print("User chose not to be asked again")
                // Save preference to UserDefaults
                UserDefaults.standard.set(true, forKey: "dontAskAgain")
            }
        }
    }
}

// 12. Modal Window Dialog
class ModalWindowDialog {

    func showModalWindow(title: String, content: String) {
        print("\n--- Modal Window Dialog ---")

        let window = NSWindow(
            contentRect: NSRect(x: 0, y: 0, width: 400, height: 300),
            styleMask: [.titled, .closable],
            backing: .buffered,
            defer: false
        )

        window.title = title

        let contentView = NSView(frame: NSRect(x: 0, y: 0, width: 400, height: 300))

        let textField = NSTextField(labelWithString: content)
        textField.frame = NSRect(x: 20, y: 150, width: 360, height: 100)
        textField.alignment = .center
        contentView.addSubview(textField)

        let closeButton = NSButton(frame: NSRect(x: 150, y: 20, width: 100, height: 30))
        closeButton.title = "Close"
        closeButton.target = window
        closeButton.action = #selector(NSWindow.close)
        contentView.addSubview(closeButton)

        window.contentView = contentView
        window.isReleasedWhenClosed = false

        let app = NSApplication.shared
        app.runModal(for: window)
        window.close()
    }
}

// Main demonstration
func demonstrateMessageBoxes() {
    print("=== macOS Swift Message Boxes Examples ===")

    // Note: These dialogs require GUI context
    print("\nNote: Message boxes require NSApplication context")
    print("The following examples show the implementation pattern")

    print("\n--- Example Alert Types ---")

    let simpleAlert = SimpleAlertDialog()
    print("Simple alert: title='Information', message='This is a message'")

    let confirmation = ConfirmationDialog()
    print("Confirmation: title='Confirm', message='Are you sure?'")

    let inputDialog = InputDialog()
    print("Text input: title='Enter Name', message='Please enter your name:'")

    let loginDialog = LoginDialog()
    print("Login dialog: requesting username and password")

    let complex = ComplexDialog()
    print("Complex dialog: multiple controls (checkboxes, popup, slider, text field)")

    print("\n=== All Message Box Examples Completed ===")
}

// Run demonstration
demonstrateMessageBoxes()

💻 Boîtes de Dialogue Fichier swift

🟡 intermediate ⭐⭐⭐

Ouvrir et enregistrer des boîtes de dialogue avec des filtres et des options personnalisées en utilisant NSOpenPanel et NSSavePanel

⏱️ 30 min 🏷️ swift, macos, desktop, gui
Prerequisites: Intermediate Swift, Cocoa/AppKit
// macOS Swift File Dialogs Examples
// Using NSOpenPanel and NSSavePanel for file selection

import Cocoa
import Foundation

// 1. Basic Open File Dialog
class BasicFileDialog {

    @discardableResult
    func openFile() -> String? {
        print("\n--- Basic Open File Dialog ---")

        let panel = NSOpenPanel()
        panel.title = "Select a file"
        panel.showsResizeIndicator = true
        panel.canChooseFiles = true
        panel.canChooseDirectories = false
        panel.allowsMultipleSelection = false

        let response = panel.runModal()

        if response == .OK {
            if let url = panel.url {
                print("Selected file: \(url.path)")
                return url.path
            }
        }

        print("No file selected")
        return nil
    }
}

// 2. Open File with Filters
class FileDialogWithFilters {

    enum FileFilter {
        case images
        case text
        case pdf
        case all
        case custom(types: [String])

        var extensions: [String] {
            switch self {
            case .images:
                return ["jpg", "jpeg", "png", "gif", "bmp", "tiff"]
            case .text:
                return ["txt", "md", "rtf"]
            case .pdf:
                return ["pdf"]
            case .all:
                return ["*"]
            case .custom(let types):
                return types
            }
        }

        var description: String {
            switch self {
            case .images:
                return "Image Files"
            case .text:
                return "Text Files"
            case .pdf:
                return "PDF Documents"
            case .all:
                return "All Files"
            case .custom(let types):
                return types.joined(separator: ", ")
            }
        }
    }

    func openFile(allowedTypes: [FileFilter]) -> [URL]? {
        print("\n--- Open File with Filters ---")

        let panel = NSOpenPanel()
        panel.title = "Select a file"
        panel.canChooseFiles = true
        panel.canChooseDirectories = false
        panel.allowsMultipleSelection = true

        // Set allowed file types
        let allTypes = allowedTypes.flatMap { $0.extensions }
        panel.allowedFileTypes = allTypes.contains("*") ? nil : allTypes

        // Display filters in title
        let filterDescriptions = allowedTypes.map { $0.description }.joined(separator: ", ")
        print("Allowed types: \(filterDescriptions)")

        let response = panel.runModal()

        if response == .OK {
            let urls = panel.urls
            print("Selected \(urls.count) file(s):")
            urls.forEach { print("  - \($0.lastPathComponent)") }
            return urls
        }

        return nil
    }
}

// 3. Save File Dialog
class SaveFileDialog {

    @discardableResult
    func saveFile(defaultName: String = "untitled.txt") -> URL? {
        print("\n--- Save File Dialog ---")

        let panel = NSSavePanel()
        panel.title = "Save File"
        panel.nameFieldStringValue = defaultName
        panel.canCreateDirectories = true
        panel.isExtensionHidden = false

        let response = panel.runModal()

        if response == .OK {
            if let url = panel.url {
                print("Save to: \(url.path)")

                // Create empty file
                if let content = "Sample content\n".data(using: .utf8) {
                    try? content.write(to: url)
                    print("File created successfully")
                }

                return url
            }
        }

        print("Save cancelled")
        return nil
    }

    func saveFileWithExtension(defaultName: String, allowedExtensions: [String]) -> URL? {
        print("\n--- Save File with Extension Validation ---")

        let panel = NSSavePanel()
        panel.title = "Save File"
        panel.nameFieldStringValue = defaultName
        panel.allowedFileTypes = allowedExtensions
        panel.allowsOtherFileTypes = false

        let response = panel.runModal()

        if response == .OK {
            if let url = panel.url {
                print("Saved to: \(url.path)")
                print("Extension: \(url.pathExtension)")
                return url
            }
        }

        return nil
    }
}

// 4. Open Directory Dialog
class DirectoryDialog {

    func selectDirectory() -> URL? {
        print("\n--- Open Directory Dialog ---")

        let panel = NSOpenPanel()
        panel.title = "Select a folder"
        panel.canChooseFiles = false
        panel.canChooseDirectories = true
        panel.allowsMultipleSelection = false

        let response = panel.runModal()

        if response == .OK {
            if let url = panel.url {
                print("Selected directory: \(url.path)")

                // List directory contents
                if let files = try? FileManager.default.contentsOfDirectory(atPath: url.path) {
                    print("Contents (\(files.count) items):")
                    files.forEach { print("  - \($0)") }
                }

                return url
            }
        }

        return nil
    }

    func selectMultipleDirectories() -> [URL]? {
        print("\n--- Select Multiple Directories ---")

        let panel = NSOpenPanel()
        panel.title = "Select folders"
        panel.canChooseFiles = false
        panel.canChooseDirectories = true
        panel.allowsMultipleSelection = true

        let response = panel.runModal()

        if response == .OK {
            let urls = panel.urls
            print("Selected \(urls.count) director(y/ies):")
            urls.forEach { print("  - \($0.path)") }
            return urls
        }

        return nil
    }
}

// 5. Open Panel with Custom Options
class CustomFileDialog {

    func openWithCustomOptions() -> [URL]? {
        print("\n--- Open Panel with Custom Options ---")

        let panel = NSOpenPanel()
        panel.title = "Select files to import"
        panel.prompt = "Import"
        panel.message = "Choose one or more files to import into the application"

        // Configure options
        panel.canChooseFiles = true
        panel.canChooseDirectories = false
        panel.allowsMultipleSelection = true

        // Set starting directory
        if let downloadsURL = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first {
            panel.directoryURL = downloadsURL
            print("Starting in Downloads folder")
        }

        // Show hidden files
        panel.showsHiddenFiles = false

        let response = panel.runModal()

        if response == .OK {
            let urls = panel.urls
            print("Selected \(urls.count) file(s) for import")
            return urls
        }

        return nil
    }

    func saveWithAccessoryView() -> URL? {
        print("\n--- Save Panel with Accessory View ---")

        let panel = NSSavePanel()
        panel.title = "Export Settings"

        // Create accessory view
        let accessoryView = NSView(frame: NSRect(x: 0, y: 0, width: 300, height: 50))

        let checkbox = NSButton(checkboxWithTitle: "Include user preferences", target: nil, action: nil)
        checkbox.state = .on
        checkbox.frame = NSRect(x: 20, y: 25, width: 260, height: 20)
        accessoryView.addSubview(checkbox)

        let label = NSTextField(labelWithString: "Export options:")
        label.frame = NSRect(x: 20, y: 5, width: 260, height: 15)
        accessoryView.addSubview(label)

        panel.accessoryView = accessoryView

        let response = panel.runModal()

        if response == .OK, let url = panel.url {
            let includePrefs = checkbox.state == .on
            print("Exported to: \(url.path)")
            print("Include preferences: \(includePrefs)")
            return url
        }

        return nil
    }
}

// 6. Async File Dialog
class AsyncFileDialog {

    func openFileAsync(completion: @escaping (URL?) -> Void) {
        print("\n--- Async File Dialog ---")

        DispatchQueue.main.async {
            let panel = NSOpenPanel()
            panel.title = "Select a file"
            panel.canChooseFiles = true
            panel.canChooseDirectories = false

            panel.begin { response in
                if response == .OK {
                    if let url = panel.url {
                        print("File selected asynchronously: \(url.path)")
                        completion(url)
                        return
                    }
                }
                print("No file selected")
                completion(nil)
            }
        }
    }
}

// 7. Recent Files Dialog
class RecentFilesDialog {

    func showRecentDocuments() -> [URL]? {
        print("\n--- Recent Documents ---")

        let panel = NSOpenPanel()
        panel.title = "Recent Documents"

        // Use the shared document controller
        let documentController = NSDocumentController.shared

        if let recentURLs = documentController.recentDocumentURLs, !recentURLs.isEmpty {
            print("Found \(recentURLs.count) recent document(s):")
            recentURLs.prefix(10).forEach { url in
                print("  - \(url.lastPathComponent)")
            }

            // Show panel in directory of most recent
            if let mostRecent = recentURLs.first,
               let parentURL = mostRecent.deletingLastPathComponent() as URL? {
                panel.directoryURL = parentURL
            }

            let response = panel.runModal()
            if response == .OK, let url = panel.url {
                return [url]
            }
        } else {
            print("No recent documents found")

            // Show regular dialog
            let response = panel.runModal()
            if response == .OK, let url = panel.url {
                return [url]
            }
        }

        return nil
    }
}

// 8. File Dialog with Preview
class FileDialogWithPreview {

    func openWithPreview() -> URL? {
        print("\n--- File Dialog with Preview ---")

        let panel = NSOpenPanel()
        panel.title = "Select an image"
        panel.canChooseFiles = true
        panel.canChooseDirectories = false
        panel.allowedFileTypes = ["jpg", "jpeg", "png", "gif", "pdf"]

        // Enable preview
        panel.canDownloadItems = true

        // Create custom preview accessory view
        let previewView = NSImageView()
        previewView.frame = NSRect(x: 0, y: 0, width: 200, height: 200)
        previewView.imageScaling = .scaleProportionallyUpOrDown
        previewView.wantsLayer = true
        previewView.layer?.borderWidth = 1
        previewView.layer?.borderColor = NSColor.separatorColor.cgColor

        panel.accessoryView = previewView

        // Note: In a real app, you would implement a delegate to update preview
        // when selection changes

        let response = panel.runModal()

        if response == .OK, let url = panel.url {
            print("Selected: \(url.lastPathComponent)")

            // Load and display preview
            if let image = NSImage(contentsOf: url) {
                previewView.image = image
                print("Image size: \(image.size)")
            }

            return url
        }

        return nil
    }
}

// 9. File Dialog with Validation
class ValidatedFileDialog {

    func openWithValidation(minFileSize: Int = 0, maxFileSize: Int = Int.max) -> URL? {
        print("\n--- File Dialog with Validation ---")

        let panel = NSOpenPanel()
        panel.title = "Select a file"
        panel.canChooseFiles = true
        panel.canChooseDirectories = false
        panel.message = "File size must be between \(minFileSize) and \(maxFileSize) bytes"

        let response = panel.runModal()

        if response == .OK, let url = panel.url {
            // Validate file size
            if let attributes = try? FileManager.default.attributesOfItem(atPath: url.path),
               let fileSize = attributes[.size] as? UInt64 {

                print("File: \(url.lastPathComponent)")
                print("Size: \(fileSize) bytes")

                if fileSize < UInt64(minFileSize) {
                    let alert = NSAlert()
                    alert.messageText = "File too small"
                    alert.informativeText = "Selected file is smaller than minimum required size (\(minFileSize) bytes)"
                    alert.alertStyle = .warning
                    alert.runModal()
                    return nil
                }

                if fileSize > UInt64(maxFileSize) {
                    let alert = NSAlert()
                    alert.messageText = "File too large"
                    alert.informativeText = "Selected file exceeds maximum allowed size (\(maxFileSize) bytes)"
                    alert.alertStyle = .warning
                    alert.runModal()
                    return nil
                }

                return url
            }
        }

        return nil
    }
}

// 10. Comprehensive File Dialog Manager
class FileDialogManager {

    enum DialogMode {
        case open
        case save
        case selectFolder
        case selectMultiple
    }

    struct DialogConfig {
        var title: String = "Select File"
        var message: String? = nil
        var defaultName: String = "untitled"
        var allowedTypes: [String]? = nil
        var allowsMultiple: Bool = false
        var canSelectFolders: Bool = false
        var startDirectory: URL? = nil
        var prompt: String? = nil

        static func openDialog(title: String = "Open",
                               message: String? = nil,
                               types: [String]? = nil,
                               multiple: Bool = false) -> DialogConfig {
            var config = DialogConfig()
            config.title = title
            config.message = message
            config.allowedTypes = types
            config.allowsMultiple = multiple
            return config
        }

        static func saveDialog(title: String = "Save",
                               defaultName: String = "untitled",
                               types: [String]? = nil) -> DialogConfig {
            var config = DialogConfig()
            config.title = title
            config.defaultName = defaultName
            config.allowedTypes = types
            return config
        }
    }

    func show(mode: DialogMode, config: DialogConfig) -> [URL]? {
        print("\n--- File Dialog Manager ---")
        print("Mode: \(mode), Title: \(config.title)")

        switch mode {
        case .open, .selectMultiple:
            let panel = NSOpenPanel()
            panel.title = config.title
            panel.message = config.message
            panel.canChooseFiles = !config.canSelectFolders
            panel.canChooseDirectories = config.canSelectFolders
            panel.allowsMultipleSelection = config.allowsMultiple
            panel.allowedFileTypes = config.allowedTypes
            if let prompt = config.prompt {
                panel.prompt = prompt
            }
            panel.directoryURL = config.startDirectory

            let response = panel.runModal()
            if response == .OK {
                return panel.urls.isEmpty ? nil : panel.urls
            }

        case .save:
            let panel = NSSavePanel()
            panel.title = config.title
            panel.message = config.message
            panel.nameFieldStringValue = config.defaultName
            panel.allowedFileTypes = config.allowedTypes
            panel.directoryURL = config.startDirectory
            if let prompt = config.prompt {
                panel.prompt = prompt
            }

            let response = panel.runModal()
            if response == .OK, let url = panel.url {
                return [url]
            }

        case .selectFolder:
            let panel = NSOpenPanel()
            panel.title = config.title
            panel.canChooseFiles = false
            panel.canChooseDirectories = true
            panel.allowsMultipleSelection = config.allowsMultiple
            panel.directoryURL = config.startDirectory

            let response = panel.runModal()
            if response == .OK {
                return panel.urls.isEmpty ? nil : panel.urls
            }
        }

        return nil
    }
}

// Main demonstration
func demonstrateFileDialogs() {
    print("=== macOS Swift File Dialogs Examples ===")

    let manager = FileDialogManager()

    // Note: These dialogs require GUI context
    print("\nNote: Dialogs require NSApplication context")
    print("The following examples show the implementation pattern")

    // Example configurations
    print("\n--- Example Configurations ---")

    let openConfig = FileDialogManager.DialogConfig.openDialog(
        title: "Open Image",
        message: "Select an image file",
        types: ["jpg", "jpeg", "png", "gif"],
        multiple: true
    )
    print("Open config: \(openConfig.title), types: \(openConfig.allowedTypes ?? [])")

    let saveConfig = FileDialogManager.DialogConfig.saveDialog(
        title: "Save Document",
        defaultName: "document.txt",
        types: ["txt", "md"]
    )
    print("Save config: \(saveConfig.title), default: \(saveConfig.defaultName)")

    print("\n=== All File Dialog Examples Completed ===")
}

// Run demonstration
demonstrateFileDialogs()

💻 Barre d'État swift

🟡 intermediate ⭐⭐⭐

Créer et gérer des applications de barre de menus avec NSStatusItem

⏱️ 35 min 🏷️ swift, macos, desktop, gui
Prerequisites: Intermediate Swift, Cocoa/AppKit
// macOS Swift System Tray Examples
// Creating and managing menu bar applications with NSStatusItem

import Cocoa
import Foundation

// 1. Basic Status Item
class BasicStatusItem {

    private var statusItem: NSStatusItem?

    func createStatusItem() {
        print("\n--- Basic Status Item ---")

        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)

        if let button = statusItem?.button {
            button.title = "🔔"
            print("Status item created with icon")
        }
    }

    func removeStatusItem() {
        print("\n--- Removing Status Item ---")

        if let statusItem = statusItem {
            NSStatusBar.system.removeStatusItem(statusItem)
            self.statusItem = nil
            print("Status item removed")
        }
    }
}

// 2. Status Item with Menu
class StatusItemWithMenu {

    private var statusItem: NSStatusItem?

    func createWithMenu() {
        print("\n--- Status Item with Menu ---")

        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)

        if let button = statusItem?.button {
            button.title = "⚙️"
        }

        let menu = NSMenu()

        menu.addItem(NSMenuItem(title: "About", action: #selector(showAbout), keyEquivalent: ""))
        menu.addItem(NSMenuItem.separator())
        menu.addItem(NSMenuItem(title: "Preferences", action: #selector(showPreferences), keyEquivalent: ","))
        menu.addItem(NSMenuItem.separator())
        menu.addItem(NSMenuItem(title: "Quit", action: #selector(quitApplication), keyEquivalent: "q"))

        statusItem?.menu = menu

        print("Menu created with 4 items")
    }

    @objc private func showAbout() {
        print("Menu action: About")
        showAlert(message: "About this application")
    }

    @objc private func showPreferences() {
        print("Menu action: Preferences")
        showAlert(message: "Open preferences")
    }

    @objc private func quitApplication() {
        print("Menu action: Quit")
        NSApplication.shared.terminate(nil)
    }

    private func showAlert(message: String) {
        let alert = NSAlert()
        alert.messageText = message
        alert.alertStyle = .informational
        alert.runModal()
    }

    func cleanup() {
        if let statusItem = statusItem {
            NSStatusBar.system.removeStatusItem(statusItem)
        }
    }
}

// 3. Status Item with Custom View
class StatusItemWithCustomView {

    private var statusItem: NSStatusItem?
    private var clickCount = 0

    func createWithCustomView() {
        print("\n--- Status Item with Custom View ---")

        statusItem = NSStatusBar.system.statusItem(withLength: 60)

        guard let button = statusItem?.button else {
            print("Failed to get status item button")
            return
        }

        button.title = ""
        button.action = #selector(statusItemClicked)
        button.target = self

        // Create custom view
        let customView = NSView(frame: NSRect(x: 0, y: 0, width: 60, height: 20))
        customView.wantsLayer = true
        customView.layer?.backgroundColor = NSColor.controlBackgroundColor.cgColor
        customView.layer?.cornerRadius = 5

        let label = NSTextField(labelWithString: "Menu")
        label.frame = NSRect(x: 10, y: 2, width: 40, height: 16)
        label.alignment = .center
        customView.addSubview(label)

        button.addSubview(customView)

        print("Custom view status item created")
    }

    @objc private func statusItemClicked() {
        clickCount += 1
        print("Status item clicked (count: \(clickCount))")

        let menu = NSMenu()
        menu.addItem(NSMenuItem(title: "Clicked \(clickCount) times", action: nil, keyEquivalent: ""))
        menu.addItem(NSMenuItem.separator())
        menu.addItem(NSMenuItem(title: "Reset", action: #selector(resetCounter), keyEquivalent: ""))

        statusItem?.menu = menu
        statusItem?.button?.performClick(nil)
    }

    @objc private func resetCounter() {
        clickCount = 0
        print("Counter reset")
    }

    func cleanup() {
        if let statusItem = statusItem {
            NSStatusBar.system.removeStatusItem(statusItem)
        }
    }
}

// 4. Dynamic Menu
class DynamicStatusMenu {

    private var statusItem: NSStatusItem?

    func createDynamicMenu() {
        print("\n--- Dynamic Status Menu ---")

        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)

        if let button = statusItem?.button {
            button.title = "📊"
        }

        refreshMenu()
    }

    func refreshMenu() {
        let menu = NSMenu()

        // Dynamic timestamp
        let dateFormatter = DateFormatter()
        dateFormatter.timeStyle = .medium
        let timeString = dateFormatter.string(from: Date())

        menu.addItem(NSMenuItem(title: "Time: \(timeString)", action: nil, keyEquivalent: ""))
        menu.addItem(NSMenuItem.separator())

        // Dynamic system info
        let memoryUsage = getMemoryUsage()
        menu.addItem(NSMenuItem(title: "Memory: \(memoryUsage)", action: nil, keyEquivalent: ""))

        menu.addItem(NSMenuItem.separator())
        menu.addItem(NSMenuItem(title: "Refresh", action: #selector(refreshClicked), keyEquivalent: "r"))
        menu.addItem(NSMenuItem.separator())
        menu.addItem(NSMenuItem(title: "Quit", action: #selector(quitApplication), keyEquivalent: "q"))

        statusItem?.menu = menu
    }

    private func getMemoryUsage() -> String {
        let task = ProcessInfo.processInfo
        let used = UInt64(task.physicalMemory)
        let formatted = ByteCountFormatter.string(fromByteCount: Int64(used), countStyle: .memory)
        return formatted
    }

    @objc private func refreshClicked() {
        print("Refreshing menu...")
        refreshMenu()
    }

    @objc private func quitApplication() {
        NSApplication.shared.terminate(nil)
    }

    func cleanup() {
        if let statusItem = statusItem {
            NSStatusBar.system.removeStatusItem(statusItem)
        }
    }
}

// 5. Status Item with Toggle
class ToggleStatusItem {

    private var statusItem: NSStatusItem?
    private var isEnabled = false

    func createToggleItem() {
        print("\n--- Toggle Status Item ---")

        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
        updateIcon()

        let menu = NSMenu()
        let toggleItem = NSMenuItem(
            title: "Enable",
            action: #selector(toggleEnabled),
            keyEquivalent: ""
        )
        toggleItem.tag = 1
        menu.addItem(toggleItem)
        menu.addItem(NSMenuItem.separator())
        menu.addItem(NSMenuItem(title: "Quit", action: #selector(quitApplication), keyEquivalent: "q"))

        statusItem?.menu = menu
    }

    @objc private func toggleEnabled() {
        isEnabled.toggle()
        updateIcon()
        updateMenuItem()

        print("Toggled: \(isEnabled ? "Enabled" : "Disabled")")
    }

    private func updateIcon() {
        statusItem?.button?.title = isEnabled ? "🟢" : "🔴"
    }

    private func updateMenuItem() {
        guard let menu = statusItem?.menu,
              let item = menu.item(withTag: 1) else { return }

        item.title = isEnabled ? "Disable" : "Enable"
    }

    @objc private func quitApplication() {
        NSApplication.shared.terminate(nil)
    }

    func cleanup() {
        if let statusItem = statusItem {
            NSStatusBar.system.removeStatusItem(statusItem)
        }
    }
}

// 6. Status Item with Popover
class PopoverStatusItem {

    private var statusItem: NSStatusItem?
    private var popover: NSPopover?

    func createWithPopover() {
        print("\n--- Status Item with Popover ---")

        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)

        if let button = statusItem?.button {
            button.title = "ℹ️"
            button.action = #selector(togglePopover)
            button.target = self
        }

        // Create popover
        popover = NSPopover()
        popover?.contentSize = NSSize(width: 300, height: 200)
        popover?.behavior = .transient
        popover?.contentViewController = PopoverViewController()
    }

    @objc private func togglePopover() {
        guard let button = statusItem?.button else { return }

        if let popover = popover, popover.isShown {
            popover.performClose(nil)
        } else {
            popover?.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
        }
    }

    func cleanup() {
        popover?.close()
        if let statusItem = statusItem {
            NSStatusBar.system.removeStatusItem(statusItem)
        }
    }
}

// Popover View Controller
class PopoverViewController: NSViewController {
    override func loadView() {
        view = NSView(frame: NSRect(x: 0, y: 0, width: 300, height: 200))

        let label = NSTextField(labelWithString: "Popover Content")
        label.frame = NSRect(x: 20, y: 150, width: 260, height: 30)
        label.alignment = .center
        label.font = NSFont.boldSystemFont(ofSize: 16)
        view.addSubview(label)

        let info = NSTextField(labelWithString: "This is a popover window.")
        info.frame = NSRect(x: 20, y: 100, width: 260, height: 20)
        info.alignment = .center
        view.addSubview(info)

        let closeButton = NSButton(frame: NSRect(x: 100, y: 20, width: 100, height: 30))
        closeButton.title = "Close"
        closeButton.target = self
        closeButton.action = #selector(closePopover)
        view.addSubview(closeButton)
    }

    @objc private func closePopover() {
        if let popover = presentingViewController as? NSPopover {
            popover.performClose(nil)
        }
    }
}

// 7. Status Item with Badge
class BadgedStatusItem {

    private var statusItem: NSStatusItem?
    private var badgeCount = 0

    func createBadgedItem() {
        print("\n--- Badged Status Item ---")

        statusItem = NSStatusBar.system.statusItem(withLength: 40)

        updateBadge()

        let menu = NSMenu()
        menu.addItem(NSMenuItem(title: "Increment", action: #selector(incrementBadge), keyEquivalent: "+"))
        menu.addItem(NSMenuItem(title: "Decrement", action: #selector(decrementBadge), keyEquivalent: "-"))
        menu.addItem(NSMenuItem.separator())
        menu.addItem(NSMenuItem(title: "Clear", action: #selector(clearBadge), keyEquivalent: "0"))
        menu.addItem(NSMenuItem.separator())
        menu.addItem(NSMenuItem(title: "Quit", action: #selector(quitApplication), keyEquivalent: "q"))

        statusItem?.menu = menu
    }

    private func updateBadge() {
        guard let button = statusItem?.button else { return }

        if badgeCount > 0 {
            button.title = "\(badgeCount)"
            // Add red circle effect
            button.wantsLayer = true
            button.layer?.backgroundColor = NSColor.red.cgColor
            button.layer?.cornerRadius = 10
        } else {
            button.title = "📬"
            button.layer?.backgroundColor = nil
        }

        print("Badge count: \(badgeCount)")
    }

    @objc private func incrementBadge() {
        badgeCount += 1
        updateBadge()
    }

    @objc private func decrementBadge() {
        badgeCount = max(0, badgeCount - 1)
        updateBadge()
    }

    @objc private func clearBadge() {
        badgeCount = 0
        updateBadge()
    }

    @objc private func quitApplication() {
        NSApplication.shared.terminate(nil)
    }

    func cleanup() {
        if let statusItem = statusItem {
            NSStatusBar.system.removeStatusItem(statusItem)
        }
    }
}

// 8. Status Item with Tooltip
class TooltipStatusItem {

    private var statusItem: NSStatusItem?

    func createWithTooltip() {
        print("\n--- Status Item with Tooltip ---")

        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)

        if let button = statusItem?.button {
            button.title = "💡"
            button.toolTip = "Click for options"
        }

        let menu = NSMenu()

        let infoItem = NSMenuItem(title: "System Information", action: nil, keyEquivalent: "")
        infoItem.toolTip = "View detailed system info"
        menu.addItem(infoItem)

        let prefItem = NSMenuItem(title: "Settings", action: nil, keyEquivalent: "")
        prefItem.toolTip = "Open application settings"
        menu.addItem(prefItem)

        menu.addItem(NSMenuItem.separator())
        menu.addItem(NSMenuItem(title: "Quit", action: #selector(quitApplication), keyEquivalent: "q"))

        statusItem?.menu = menu

        print("Tooltip status item created")
    }

    @objc private func quitApplication() {
        NSApplication.shared.terminate(nil)
    }

    func updateTooltip(_ tooltip: String) {
        statusItem?.button?.toolTip = tooltip
        print("Tooltip updated: \(tooltip)")
    }

    func cleanup() {
        if let statusItem = statusItem {
            NSStatusBar.system.removeStatusItem(statusItem)
        }
    }
}

// 9. Animated Status Item
class AnimatedStatusItem {

    private var statusItem: NSStatusItem?
    private var frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
    private var currentFrame = 0
    private var timer: Timer?
    private var isAnimating = false

    func createAnimatedItem() {
        print("\n--- Animated Status Item ---")

        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)

        if let button = statusItem?.button {
            button.title = frames[0]
        }

        let menu = NSMenu()
        menu.addItem(NSMenuItem(title: "Start Animation", action: #selector(startAnimation), keyEquivalent: "s"))
        menu.addItem(NSMenuItem(title: "Stop Animation", action: #selector(stopAnimation), keyEquivalent: "x"))
        menu.addItem(NSMenuItem.separator())
        menu.addItem(NSMenuItem(title: "Quit", action: #selector(quitApplication), keyEquivalent: "q"))

        statusItem?.menu = menu
    }

    @objc private func startAnimation() {
        guard !isAnimating else { return }

        isAnimating = true
        timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in
            self?.updateFrame()
        }

        print("Animation started")
    }

    @objc private func stopAnimation() {
        isAnimating = false
        timer?.invalidate()
        timer = nil

        statusItem?.button?.title = "⏸"
        print("Animation stopped")
    }

    private func updateFrame() {
        currentFrame = (currentFrame + 1) % frames.count
        statusItem?.button?.title = frames[currentFrame]
    }

    @objc private func quitApplication() {
        stopAnimation()
        NSApplication.shared.terminate(nil)
    }

    func cleanup() {
        stopAnimation()
        if let statusItem = statusItem {
            NSStatusBar.system.removeStatusItem(statusItem)
        }
    }
}

// 10. Complete Menu Bar Application
class MenuBarApplication {

    private var statusItem: NSStatusItem?
    private var appState: AppState = AppState()

    func create() {
        print("\n--- Complete Menu Bar Application ---")

        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)

        if let button = statusItem?.button {
            button.title = "🚀"
        }

        buildMenu()
        setupNotifications()
    }

    private func buildMenu() {
        let menu = NSMenu()

        // App section
        menu.addItem(NSMenuItem(title: "About", action: #selector(showAbout), keyEquivalent: ""))
        menu.addItem(NSMenuItem.separator())

        // Features section
        menu.addItem(NSMenuItem(title: "Connect", action: #selector(toggleConnection), keyEquivalent: "c"))
        menu.addItem(NSMenuItem(title: "Sync", action: #selector(syncData), keyEquivalent: "s"))
        menu.addItem(NSMenuItem.separator())

        // Status section
        menu.addItem(NSMenuItem(title: "Status: \(appState.status)", action: nil, keyEquivalent: ""))
        menu.addItem(NSMenuItem.separator())

        // Preferences
        menu.addItem(NSMenuItem(title: "Preferences...", action: #selector(showPreferences), keyEquivalent: ","))

        // Quit
        menu.addItem(NSMenuItem.separator())
        menu.addItem(NSMenuItem(title: "Quit", action: #selector(quitApplication), keyEquivalent: "q"))

        statusItem?.menu = menu
    }

    private func setupNotifications() {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(statusChanged),
            name: .statusDidChange,
            object: nil
        )
    }

    @objc private func showAbout() {
        print("Menu: About")
        showAlert(message: "Menu Bar Application v1.0")
    }

    @objc private func toggleConnection() {
        appState.isConnected.toggle()
        print("Toggled connection: \(appState.isConnected)")

        updateStatusIcon()
        buildMenu()
    }

    @objc private func syncData() {
        print("Syncing data...")
        showAlert(message: "Sync completed")
    }

    @objc private func showPreferences() {
        print("Menu: Preferences")
        showAlert(message: "Open preferences window")
    }

    @objc private func quitApplication() {
        NSApplication.shared.terminate(nil)
    }

    @objc private func statusChanged() {
        print("Status changed: \(appState.status)")
        buildMenu()
    }

    private func updateStatusIcon() {
        statusItem?.button?.title = appState.isConnected ? "🟢" : "🔴"
    }

    private func showAlert(message: String) {
        let alert = NSAlert()
        alert.messageText = message
        alert.alertStyle = .informational
        alert.runModal()
    }

    func cleanup() {
        NotificationCenter.default.removeObserver(self)
        if let statusItem = statusItem {
            NSStatusBar.system.removeStatusItem(statusItem)
        }
    }
}

// Application State
class AppState {
    var isConnected = false
    var status: String {
        return isConnected ? "Connected" : "Disconnected"
    }
}

extension Notification.Name {
    static let statusDidChange = Notification.Name("statusDidChange")
}

// Main demonstration
func demonstrateSystemTray() {
    print("=== macOS Swift System Tray Examples ===")

    // Note: System tray items require NSApplication context
    print("\nNote: Status items require NSApplication context")
    print("The following examples show the implementation pattern")

    print("\n--- Example Configurations ---")

    print("1. Basic Status Item - Simple icon in menu bar")
    print("2. Status Item with Menu - Dropdown menu with actions")
    print("3. Custom View - Custom view with click handling")
    print("4. Dynamic Menu - Menu that updates dynamically")
    print("5. Toggle Status Item - Toggle between states")
    print("6. Popover - Show popover window on click")
    print("7. Badge - Show notification badge count")
    print("8. Tooltip - Show tooltip on hover")
    print("9. Animated Item - Animated status icon")
    print("10. Complete App - Full menu bar application")

    print("\n=== All System Tray Examples Completed ===")
}

// Run demonstration
demonstrateSystemTray()