Skip to content

Commit 4645503

Browse files
committed
Refactor TrayManager API and add menu item demos
- Replace static methods with singleton pattern in TrayManager - Rename methods for clarity (e.g., createTrayIcon → create) - Add checkbox and radio button menu item demos in main.swift - Update context menu event handling and demo instructions
1 parent 35dd197 commit 4645503

File tree

2 files changed

+142
-173
lines changed

2 files changed

+142
-173
lines changed

Sources/Example/main.swift

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@ import NativeAPI
44
/// Create minimal tray icon with context menu
55
@MainActor func createTrayIconWithContextMenu() {
66
print("=== Tray Icon with Context Menu Demo ===")
7+
let trayManager = TrayManager.shared
78

89
// Check if system tray is supported
9-
guard TrayManager.isSystemTraySupported else {
10+
guard trayManager.isSupported() else {
1011
print("❌ System tray is not supported on this platform")
1112
return
1213
}
1314

1415
print("✅ System tray is supported")
1516

1617
// Create a basic tray icon
17-
guard let trayIcon = TrayManager.createTrayIcon() else {
18+
guard let trayIcon = trayManager.create() else {
1819
print("❌ Failed to create tray icon")
1920
return
2021
}
@@ -57,6 +58,69 @@ import NativeAPI
5758
// Add separator
5859
contextMenu.addSeparator()
5960

61+
// Add checkbox items for demonstration
62+
print("📝 添加 Checkbox 菜单项演示...")
63+
64+
// First create the checkbox items without event handlers
65+
let showToolbarItem = contextMenu.addCheckboxItem(text: "显示工具栏", checked: true)
66+
print("✅ 创建复选框'显示工具栏',ID: \(showToolbarItem.id), 初始状态: \(showToolbarItem.isChecked)")
67+
68+
let autoSaveItem = contextMenu.addCheckboxItem(text: "自动保存", checked: false)
69+
print("✅ 创建复选框'自动保存',ID: \(autoSaveItem.id), 初始状态: \(autoSaveItem.isChecked)")
70+
71+
// Now add event handlers after the variables are declared
72+
showToolbarItem.onClick { event in
73+
let isChecked = showToolbarItem.isChecked
74+
print("☑️ 工具栏显示状态切换: \(isChecked)")
75+
print(" 工具栏现在\(isChecked ? "显示" : "隐藏")")
76+
print(" 事件详情: ID=\(event.itemId), Text='\(event.itemText)'")
77+
}
78+
79+
autoSaveItem.onClick { event in
80+
let isChecked = autoSaveItem.isChecked
81+
print("☑️ 自动保存状态切换: \(isChecked)")
82+
print(" 自动保存功能\(isChecked ? "已启用" : "已禁用")")
83+
print(" 事件详情: ID=\(event.itemId), Text='\(event.itemText)'")
84+
}
85+
86+
// Add separator
87+
contextMenu.addSeparator()
88+
89+
// Add radio button group for view mode selection
90+
print("📝 添加 Radio 按钮组演示...")
91+
92+
// First create the radio items without event handlers
93+
let compactViewItem = contextMenu.addRadioItem(text: "紧凑视图", groupId: 1, checked: false)
94+
print("✅ 创建单选按钮'紧凑视图',ID: \(compactViewItem.id), 组: 1, 选中: \(compactViewItem.isChecked)")
95+
96+
let normalViewItem = contextMenu.addRadioItem(text: "普通视图", groupId: 1, checked: true)
97+
print("✅ 创建单选按钮'普通视图',ID: \(normalViewItem.id), 组: 1, 选中: \(normalViewItem.isChecked)")
98+
99+
let detailedViewItem = contextMenu.addRadioItem(text: "详细视图", groupId: 1, checked: false)
100+
print("✅ 创建单选按钮'详细视图',ID: \(detailedViewItem.id), 组: 1, 选中: \(detailedViewItem.isChecked)")
101+
102+
// Now add event handlers after the variables are declared
103+
compactViewItem.onClick { event in
104+
print("🔘 视图模式切换为: 紧凑视图")
105+
print(" Radio 组 ID: 1")
106+
print(" 事件详情: ID=\(event.itemId), Text='\(event.itemText)'")
107+
}
108+
109+
normalViewItem.onClick { event in
110+
print("🔘 视图模式切换为: 普通视图")
111+
print(" Radio 组 ID: 1")
112+
print(" 事件详情: ID=\(event.itemId), Text='\(event.itemText)'")
113+
}
114+
115+
detailedViewItem.onClick { event in
116+
print("🔘 视图模式切换为: 详细视图")
117+
print(" Radio 组 ID: 1")
118+
print(" 事件详情: ID=\(event.itemId), Text='\(event.itemText)'")
119+
}
120+
121+
// Add separator
122+
contextMenu.addSeparator()
123+
60124
// Add "Exit" menu item
61125
let exitItem = contextMenu.addItem(text: "退出") { event in
62126
print("👋 退出菜单项点击成功!")
@@ -116,7 +180,12 @@ print()
116180
print("\n✅ Tray icon demo started")
117181
print("💡 应用程序正在运行,托盘图标已显示")
118182
print("💡 右键点击托盘图标查看上下文菜单")
119-
print("💡 点击菜单项测试事件处理")
183+
print("💡 测试功能:")
184+
print(" • 普通菜单项: 显示窗口、关于、设置")
185+
print(" • 复选框菜单项: 显示工具栏、自动保存")
186+
print(" • 单选按钮组: 紧凑视图、普通视图、详细视图")
187+
print(" • 退出: 关闭应用程序")
188+
print("💡 点击菜单项测试各种事件处理")
120189

121190
// Create a minimal window to keep the app running
122191
let windowOptions = WindowOptions()
Lines changed: 70 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,85 @@
11
import CNativeAPI
22
import Foundation
33

4-
/// Manages system tray icons
4+
/**
5+
* TrayManager is a singleton class that manages all system tray icons.
6+
*
7+
* This class provides a centralized way to create, manage, and destroy system
8+
* tray icons. It ensures that there's only one instance of the tray manager
9+
* throughout the application lifetime and provides thread-safe operations for
10+
* managing tray icons.
11+
*
12+
* - Note: This class is implemented as a singleton to ensure consistent
13+
* management of system tray resources across the entire application.
14+
*/
515
public class TrayManager: @unchecked Sendable {
16+
/**
17+
* Get the singleton instance of TrayManager.
18+
*
19+
* This property provides access to the unique instance of TrayManager.
20+
* The instance is created on first call and remains alive for the
21+
* duration of the application.
22+
*
23+
* - Returns: The singleton TrayManager instance
24+
* - Note: This property is thread-safe
25+
*/
626
public static let shared = TrayManager()
727

828
private init() {}
929

10-
/// Check if system tray is supported on the current platform
11-
/// - Returns: true if system tray is supported, false otherwise
12-
public var isSupported: Bool {
30+
/**
31+
* Check if the system tray is supported on the current platform.
32+
*
33+
* Some platforms or desktop environments may not support system tray
34+
* functionality. This method allows checking for availability before
35+
* attempting to create tray icons.
36+
*
37+
* - Returns: true if system tray is supported, false otherwise
38+
*/
39+
public func isSupported() -> Bool {
1340
return native_tray_manager_is_supported()
1441
}
1542

16-
/// Create a new tray icon
17-
/// - Returns: A new TrayIcon instance, or nil if creation failed
18-
public func createTrayIcon() -> TrayIcon? {
43+
/**
44+
* Create a new system tray icon.
45+
*
46+
* Creates a new tray icon instance and registers it with the manager.
47+
* The tray icon will be assigned a unique ID for future reference.
48+
*
49+
* - Returns: A new TrayIcon instance, or nil if creation failed
50+
* - Note: The returned tray icon is automatically managed by this TrayManager
51+
*/
52+
public func create() -> TrayIcon? {
1953
guard let handle = native_tray_manager_create() else {
2054
return nil
2155
}
2256
return TrayIcon(nativeHandle: handle)
2357
}
2458

25-
/// Get a tray icon by its ID
26-
/// - Parameter id: The tray icon ID
27-
/// - Returns: TrayIcon instance, or nil if not found
28-
public func getTrayIcon(withId id: Int) -> TrayIcon? {
59+
/**
60+
* Get a tray icon by its unique ID.
61+
*
62+
* Retrieves a previously created tray icon using its assigned ID.
63+
*
64+
* - Parameter id: The unique identifier of the tray icon
65+
* - Returns: TrayIcon instance, or nil if not found
66+
*/
67+
public func get(id: Int) -> TrayIcon? {
2968
guard let handle = native_tray_manager_get(native_tray_icon_id_t(id)) else {
3069
return nil
3170
}
3271
return TrayIcon(nativeHandle: handle)
3372
}
3473

35-
/// Get all managed tray icons
36-
/// - Returns: Array of all tray icons
37-
public func getAllTrayIcons() -> [TrayIcon] {
74+
/**
75+
* Get all managed tray icons.
76+
*
77+
* Returns an array containing all currently active tray icons
78+
* managed by this TrayManager instance.
79+
*
80+
* - Returns: Array of all active TrayIcon instances
81+
*/
82+
public func getAll() -> [TrayIcon] {
3883
let trayIconList = native_tray_manager_get_all()
3984
defer { native_tray_icon_list_free(trayIconList) }
4085

@@ -49,163 +94,18 @@ public class TrayManager: @unchecked Sendable {
4994
return trayIcons
5095
}
5196

52-
/// Destroy a tray icon by its ID
53-
/// - Parameter id: The tray icon ID to destroy
54-
/// - Returns: true if tray icon was found and destroyed, false otherwise
97+
/**
98+
* Destroy a tray icon by its ID.
99+
*
100+
* Removes and destroys a tray icon identified by its unique ID.
101+
* This will remove the icon from the system tray and clean up
102+
* associated resources.
103+
*
104+
* - Parameter id: The unique identifier of the tray icon to destroy
105+
* - Returns: true if the tray icon was found and destroyed, false otherwise
106+
*/
55107
@discardableResult
56-
public func destroyTrayIcon(withId id: Int) -> Bool {
108+
public func destroy(id: Int) -> Bool {
57109
return native_tray_manager_destroy(native_tray_icon_id_t(id))
58110
}
59-
60-
/// Destroy a tray icon
61-
/// - Parameter trayIcon: The tray icon to destroy
62-
/// - Returns: true if tray icon was found and destroyed, false otherwise
63-
@discardableResult
64-
public func destroyTrayIcon(_ trayIcon: TrayIcon) -> Bool {
65-
return destroyTrayIcon(withId: trayIcon.id)
66-
}
67-
}
68-
69-
// MARK: - Convenience Methods
70-
71-
extension TrayManager {
72-
/// Create and configure a tray icon in one step
73-
/// - Parameters:
74-
/// - icon: Path to icon file or base64 encoded image data
75-
/// - tooltip: The tooltip text (optional)
76-
/// - title: The title text (optional)
77-
/// - configure: Additional configuration closure (optional)
78-
/// - Returns: A new TrayIcon instance, or nil if creation failed
79-
public func createTrayIcon(
80-
icon: String,
81-
tooltip: String? = nil,
82-
title: String? = nil,
83-
configure: ((TrayIcon) -> Void)? = nil
84-
) -> TrayIcon? {
85-
guard let trayIcon = createTrayIcon() else {
86-
return nil
87-
}
88-
89-
trayIcon.setIcon(icon)
90-
if let tooltip = tooltip {
91-
trayIcon.setTooltip(tooltip)
92-
}
93-
if let title = title {
94-
trayIcon.setTitle(title)
95-
}
96-
97-
configure?(trayIcon)
98-
return trayIcon
99-
}
100-
101-
/// Create a tray icon with a context menu
102-
/// - Parameters:
103-
/// - icon: Path to icon file or base64 encoded image data
104-
/// - tooltip: The tooltip text (optional)
105-
/// - menuBuilder: Closure to build the context menu
106-
/// - Returns: A new TrayIcon instance, or nil if creation failed
107-
public func createTrayIcon(
108-
icon: String,
109-
tooltip: String? = nil,
110-
menuBuilder: (Menu) -> Void
111-
) -> TrayIcon? {
112-
guard let trayIcon = createTrayIcon() else {
113-
return nil
114-
}
115-
116-
trayIcon.setIcon(icon)
117-
if let tooltip = tooltip {
118-
trayIcon.setTooltip(tooltip)
119-
}
120-
121-
let menu = Menu()
122-
menuBuilder(menu)
123-
trayIcon.setContextMenu(menu)
124-
125-
return trayIcon
126-
}
127-
128-
/// Create a tray icon with a pre-built context menu
129-
/// - Parameters:
130-
/// - icon: Path to icon file or base64 encoded image data
131-
/// - tooltip: The tooltip text (optional)
132-
/// - menu: An existing menu to attach
133-
/// - Returns: A new TrayIcon instance, or nil if creation failed
134-
public func createTrayIcon(
135-
icon: String,
136-
tooltip: String? = nil,
137-
menu: Menu
138-
) -> TrayIcon? {
139-
guard let trayIcon = createTrayIcon() else {
140-
return nil
141-
}
142-
143-
trayIcon.setIcon(icon)
144-
if let tooltip = tooltip {
145-
trayIcon.setTooltip(tooltip)
146-
}
147-
148-
trayIcon.setContextMenu(menu)
149-
return trayIcon
150-
}
151-
152-
/// Get the number of currently managed tray icons
153-
public var trayIconCount: Int {
154-
return getAllTrayIcons().count
155-
}
156-
157-
/// Check if there are any managed tray icons
158-
public var hasActiveTrayIcons: Bool {
159-
return trayIconCount > 0
160-
}
161-
162-
/// Destroy all managed tray icons
163-
/// - Returns: Number of tray icons that were destroyed
164-
@discardableResult
165-
public func destroyAllTrayIcons() -> Int {
166-
let trayIcons = getAllTrayIcons()
167-
var destroyedCount = 0
168-
169-
for trayIcon in trayIcons {
170-
if destroyTrayIcon(trayIcon) {
171-
destroyedCount += 1
172-
}
173-
}
174-
175-
return destroyedCount
176-
}
177-
}
178-
179-
// MARK: - Static Convenience Methods
180-
181-
extension TrayManager {
182-
/// Quick check if system tray is available
183-
/// - Returns: true if system tray is supported, false otherwise
184-
public static var isSystemTraySupported: Bool {
185-
return TrayManager.shared.isSupported
186-
}
187-
188-
/// Quick access to create a tray icon
189-
/// - Returns: A new TrayIcon instance, or nil if creation failed
190-
public static func createTrayIcon() -> TrayIcon? {
191-
return TrayManager.shared.createTrayIcon()
192-
}
193-
194-
/// Quick access to create and configure a tray icon
195-
/// - Parameters:
196-
/// - icon: Path to icon file or base64 encoded image data
197-
/// - tooltip: The tooltip text (optional)
198-
/// - configure: Configuration closure (optional)
199-
/// - Returns: A new TrayIcon instance, or nil if creation failed
200-
public static func createTrayIcon(
201-
icon: String,
202-
tooltip: String? = nil,
203-
configure: ((TrayIcon) -> Void)? = nil
204-
) -> TrayIcon? {
205-
return TrayManager.shared.createTrayIcon(
206-
icon: icon,
207-
tooltip: tooltip,
208-
configure: configure
209-
)
210-
}
211111
}

0 commit comments

Comments
 (0)