waterui 0.3.0

A modern UI framework for Rust
// MainMenu.swift
// Provides a standard macOS application menu bar with Edit menu for keyboard shortcuts.
//
// Without a menu bar, keyboard shortcuts like ⌘C, ⌘V, ⌘X, ⌘A won't work because
// AppKit routes these through the Edit menu items in the responder chain.

#if os(macOS)
import AppKit

/// Creates and manages the standard macOS application menu bar.
@MainActor
public enum WaterUIMainMenu {
    /// Creates a complete main menu bar for the application.
    ///
    /// Includes:
    /// - App Menu: About, Preferences, Quit
    /// - Edit Menu: Undo, Redo, Cut, Copy, Paste, Select All
    /// - Window Menu: Minimize, Zoom, Bring All to Front
    ///
    /// Call this in your AppDelegate before `app.run()`:
    /// ```swift
    /// app.mainMenu = WaterUIMainMenu.create()
    /// ```
    public static func create() -> NSMenu {
        let mainMenu = NSMenu()
        
        // App menu
        mainMenu.addItem(createAppMenuItem())
        
        // Edit menu (required for keyboard shortcuts in text fields)
        mainMenu.addItem(createEditMenuItem())
        
        // Window menu
        mainMenu.addItem(createWindowMenuItem())
        
        return mainMenu
    }
    
    // MARK: - App Menu
    
    private static func createAppMenuItem() -> NSMenuItem {
        let menuItem = NSMenuItem()
        let submenu = NSMenu()
        
        // About
        let aboutItem = NSMenuItem(
            title: "About \(appName)",
            action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)),
            keyEquivalent: ""
        )
        submenu.addItem(aboutItem)
        
        submenu.addItem(NSMenuItem.separator())
        
        // Preferences
        let prefsItem = NSMenuItem(
            title: "Preferences…",
            action: nil,
            keyEquivalent: ","
        )
        submenu.addItem(prefsItem)
        
        submenu.addItem(NSMenuItem.separator())
        
        // Services submenu
        let servicesItem = NSMenuItem(title: "Services", action: nil, keyEquivalent: "")
        let servicesMenu = NSMenu(title: "Services")
        servicesItem.submenu = servicesMenu
        NSApp?.servicesMenu = servicesMenu
        submenu.addItem(servicesItem)
        
        submenu.addItem(NSMenuItem.separator())
        
        // Hide
        let hideItem = NSMenuItem(
            title: "Hide \(appName)",
            action: #selector(NSApplication.hide(_:)),
            keyEquivalent: "h"
        )
        submenu.addItem(hideItem)
        
        // Hide Others
        let hideOthersItem = NSMenuItem(
            title: "Hide Others",
            action: #selector(NSApplication.hideOtherApplications(_:)),
            keyEquivalent: "h"
        )
        hideOthersItem.keyEquivalentModifierMask = [.command, .option]
        submenu.addItem(hideOthersItem)
        
        // Show All
        let showAllItem = NSMenuItem(
            title: "Show All",
            action: #selector(NSApplication.unhideAllApplications(_:)),
            keyEquivalent: ""
        )
        submenu.addItem(showAllItem)
        
        submenu.addItem(NSMenuItem.separator())
        
        // Quit
        let quitItem = NSMenuItem(
            title: "Quit \(appName)",
            action: #selector(NSApplication.terminate(_:)),
            keyEquivalent: "q"
        )
        submenu.addItem(quitItem)
        
        menuItem.submenu = submenu
        return menuItem
    }
    
    // MARK: - Edit Menu
    
    private static func createEditMenuItem() -> NSMenuItem {
        let menuItem = NSMenuItem(title: "Edit", action: nil, keyEquivalent: "")
        let submenu = NSMenu(title: "Edit")
        
        // Undo - uses nil target to route through responder chain
        let undoItem = NSMenuItem(
            title: "Undo",
            action: Selector(("undo:")),
            keyEquivalent: "z"
        )
        submenu.addItem(undoItem)
        
        // Redo
        let redoItem = NSMenuItem(
            title: "Redo",
            action: Selector(("redo:")),
            keyEquivalent: "z"
        )
        redoItem.keyEquivalentModifierMask = [.command, .shift]
        submenu.addItem(redoItem)
        
        submenu.addItem(NSMenuItem.separator())
        
        // Cut
        let cutItem = NSMenuItem(
            title: "Cut",
            action: #selector(NSText.cut(_:)),
            keyEquivalent: "x"
        )
        submenu.addItem(cutItem)
        
        // Copy
        let copyItem = NSMenuItem(
            title: "Copy",
            action: #selector(NSText.copy(_:)),
            keyEquivalent: "c"
        )
        submenu.addItem(copyItem)
        
        // Paste
        let pasteItem = NSMenuItem(
            title: "Paste",
            action: #selector(NSText.paste(_:)),
            keyEquivalent: "v"
        )
        submenu.addItem(pasteItem)
        
        // Delete
        let deleteItem = NSMenuItem(
            title: "Delete",
            action: #selector(NSText.delete(_:)),
            keyEquivalent: ""
        )
        submenu.addItem(deleteItem)
        
        // Select All
        let selectAllItem = NSMenuItem(
            title: "Select All",
            action: #selector(NSText.selectAll(_:)),
            keyEquivalent: "a"
        )
        submenu.addItem(selectAllItem)
        
        menuItem.submenu = submenu
        return menuItem
    }
    
    // MARK: - Window Menu
    
    private static func createWindowMenuItem() -> NSMenuItem {
        let menuItem = NSMenuItem(title: "Window", action: nil, keyEquivalent: "")
        let submenu = NSMenu(title: "Window")
        
        // Minimize
        let minimizeItem = NSMenuItem(
            title: "Minimize",
            action: #selector(NSWindow.miniaturize(_:)),
            keyEquivalent: "m"
        )
        submenu.addItem(minimizeItem)
        
        // Zoom
        let zoomItem = NSMenuItem(
            title: "Zoom",
            action: #selector(NSWindow.zoom(_:)),
            keyEquivalent: ""
        )
        submenu.addItem(zoomItem)
        
        submenu.addItem(NSMenuItem.separator())
        
        // Bring All to Front
        let bringAllItem = NSMenuItem(
            title: "Bring All to Front",
            action: #selector(NSApplication.arrangeInFront(_:)),
            keyEquivalent: ""
        )
        submenu.addItem(bringAllItem)
        
        // Register as window menu for automatic window list
        NSApp?.windowsMenu = submenu
        
        menuItem.submenu = submenu
        return menuItem
    }
    
    // MARK: - Helpers
    
    /// What this application calls itself in its own menu.
    ///
    /// The display name comes first because that is the one an application
    /// chooses for people to read; `CFBundleName` is generated from the Xcode
    /// product name, which every WaterUI application shares. Reading it first
    /// is why they all announced themselves as the scaffold's target.
    private static var appName: String {
        Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
            ?? Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String
            ?? ProcessInfo.processInfo.processName
    }
}
#endif