From 08ff53cae4f4d65d4ed211f9211ac4920e36b626 Mon Sep 17 00:00:00 2001
From: Ariver <ar@MacBook-Air.local>
Date: Wed, 06 May 2026 18:46:05 +0800
Subject: [PATCH] checkpoint: v3.1.4 — 三个Toggle并排一行,等距排版

---
 Apptag/ApptagApp.swift |  102 ++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 77 insertions(+), 25 deletions(-)

diff --git a/Apptag/ApptagApp.swift b/Apptag/ApptagApp.swift
index d5c125a..2371333 100644
--- a/Apptag/ApptagApp.swift
+++ b/Apptag/ApptagApp.swift
@@ -1,6 +1,7 @@
 import SwiftUI
 import AppKit
 import Carbon
+import ServiceManagement
 
 // MARK: - Application Entry Point
 
@@ -24,17 +25,47 @@
     private var isInEditMode = false  // Suppress auto-dismiss during editing
 
     func applicationDidFinishLaunching(_ notification: Notification) {
-        // Use .accessory so the app appears in Force Quit (unlike LSUIElement)
-        NSApp.setActivationPolicy(.accessory)
+        L10n.setup()
+        let showDock = UserDefaults.standard.bool(forKey: "showDockIcon")
+        NSApp.setActivationPolicy(showDock ? .regular : .accessory)
         setupMenuBar()
         registerHotkey()
         observeOtherWindows()
         observeEditMode()
+        observeDockSetting()
+        setupLaunchAtLogin()
+    }
+
+    /// Observe Show in Dock changes so it takes effect immediately.
+    private func observeDockSetting() {
+        NotificationCenter.default.addObserver(
+            forName: UserDefaults.didChangeNotification,
+            object: nil, queue: .main
+        ) { _ in
+            let show = UserDefaults.standard.bool(forKey: "showDockIcon")
+            NSApp.setActivationPolicy(show ? .regular : .accessory)
+        }
+    }
+
+    /// Enable launch at login by default; prompt user if disabled.
+    private func setupLaunchAtLogin() {
+        let key = "launchAtLogin"
+        if UserDefaults.standard.object(forKey: key) == nil {
+            // First launch: enable by default
+            UserDefaults.standard.set(true, forKey: key)
+            try? SMAppService.mainApp.register()
+        } else if UserDefaults.standard.bool(forKey: key) {
+            try? SMAppService.mainApp.register()
+        }
     }
 
     // MARK: - Menu Bar
 
     private func setupMenuBar() {
+        // Remove old status item if re-creating (language switch, etc.)
+        if let existing = statusItem {
+            NSStatusBar.system.removeStatusItem(existing)
+        }
         statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
 
         if let button = statusItem.button {
@@ -50,7 +81,7 @@
         let menu = NSMenu()
         menu.addItem(
             NSMenuItem(
-                title: "Show Apptag",
+                title: tr("menu.show"),
                 action: #selector(toggleOverlay),
                 keyEquivalent: ""
             )
@@ -61,7 +92,7 @@
         let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "?"
         let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "?"
         let versionItem = NSMenuItem(
-            title: "Version \(appVersion) (\(buildNumber))",
+            title: "\(tr("menu.version")) \(appVersion) (\(buildNumber))",
             action: nil,
             keyEquivalent: ""
         )
@@ -70,16 +101,31 @@
 
         menu.addItem(.separator())
         let prefsItem = NSMenuItem(
-            title: "Preferences...",
+            title: tr("menu.preferences"),
             action: #selector(openPreferences),
             keyEquivalent: ","
         )
         prefsItem.keyEquivalentModifierMask = .command
         menu.addItem(prefsItem)
         menu.addItem(.separator())
+
+        // Language submenu
+        let langMenu = NSMenu()
+        let currentLang = L10n.currentCode
+        for (code, name) in L10n.supported {
+            let item = NSMenuItem(title: name, action: #selector(switchLanguage(_:)), keyEquivalent: "")
+            item.representedObject = code
+            item.state = (code == currentLang) ? .on : .off
+            langMenu.addItem(item)
+        }
+        let langItem = NSMenuItem(title: tr("menu.language"), action: nil, keyEquivalent: "")
+        langItem.submenu = langMenu
+        menu.addItem(langItem)
+
+        menu.addItem(.separator())
         menu.addItem(
             NSMenuItem(
-                title: "Quit",
+                title: tr("menu.quit"),
                 action: #selector(NSApplication.terminate(_:)),
                 keyEquivalent: "q"
             )
@@ -237,6 +283,13 @@
         hideOverlay()
         NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil)
     }
+
+    @objc private func switchLanguage(_ sender: NSMenuItem) {
+        guard let code = sender.representedObject as? String else { return }
+        L10n.switchTo(code)
+        // Rebuild menu to update checkmarks
+        setupMenuBar()
+    }
 }
 
 // MARK: - NSView-level backdrop dismiss (works even if SwiftUI rendering is slow)
@@ -320,6 +373,9 @@
     @AppStorage("tagPosition") private var tagPosition = "left"
     @AppStorage("defaultGroupName") private var defaultGroupName = "Other"
     @AppStorage("displayMode") private var displayMode = "flat"
+    @AppStorage("hideAppNames") private var hideAppNames = false
+    @AppStorage("showDockIcon") private var showDockIcon = false
+    @AppStorage("launchAtLogin") private var launchAtLogin = true
 
     @State private var allApps: [AppInfo] = []
     @State private var tagColors: [String: Int] = [:]
@@ -386,6 +442,21 @@
             // Tab 1: General
             Form {
                 Section {
+                    HStack(spacing: 20) {
+                        Toggle("Launch at login", isOn: $launchAtLogin)
+                            .onChange(of: launchAtLogin) { _, enabled in
+                                if enabled {
+                                    try? SMAppService.mainApp.register()
+                                } else {
+                                    try? SMAppService.mainApp.unregister()
+                                }
+                            }
+                        Toggle("Show in Dock", isOn: $showDockIcon)
+                        Toggle("Hide app names", isOn: $hideAppNames)
+                    }
+                }
+
+                Section {
                     LabeledContent("App list style:") {
                         Picker("", selection: $displayMode) {
                             Text("Flat").tag("flat")
@@ -427,8 +498,6 @@
                     Text("Adjust the size of group-name labels in the overlay.")
                         .font(.caption)
                         .foregroundStyle(.secondary)
-                } header: {
-                    Text("Appearance")
                 }
 
                 Section {
@@ -444,8 +513,6 @@
                     Text("Icon display size. Grid columns adjust automatically.")
                         .font(.caption)
                         .foregroundStyle(.secondary)
-                } header: {
-                    Text("Layout")
                 }
             }
             .tabItem { Label("General", systemImage: "gearshape") }
@@ -464,21 +531,6 @@
 
             // Tab 3: Data
             Form {
-                Section {
-                    HStack {
-                        Button("Re-index Now") {
-                            NotificationCenter.default.post(name: .apptagReindex, object: nil)
-                        }
-                        .buttonStyle(.borderedProminent)
-
-                        Text("Re-scan all app directories and refresh the tag database.")
-                            .font(.caption)
-                            .foregroundStyle(.secondary)
-                    }
-                } header: {
-                    Text("Index Management")
-                }
-
                 Section {
                     HStack(spacing: 12) {
                         Button("Export Tags…") { exportTags() }

--
Gitblit v1.9.3