Skip to content

Commit 35dd197

Browse files
committed
Refactor tray icon and menu APIs for improved event handling
- Simplify and clarify Menu and TrayIcon Swift wrappers - Replace legacy callback setup with unified event listener approach - Add strongly typed event structs for tray icon and menu events - Remove unused test code and streamline Example/main.swift demo - Fix memory management for subproject and tray icon creation - Improve enum bridging and option set usage for keyboard accelerators - Ensure proper cleanup and resource management for menu and tray objects
1 parent 9733cf7 commit 35dd197

File tree

6 files changed

+547
-797
lines changed

6 files changed

+547
-797
lines changed

Sources/Example/main.swift

Lines changed: 69 additions & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,9 @@
11
import Foundation
22
import NativeAPI
33

4-
/// Test DisplayManager functionality
5-
func testDisplayManager() {
6-
print("=== DisplayManager Test ===")
7-
8-
let displayManager = DisplayManager.shared
9-
10-
// Test display count
11-
let displays = displayManager.getAll()
12-
print("✅ Display count: \(displays.count)")
13-
14-
// Test primary display
15-
if let primary = displayManager.getPrimary() {
16-
print("✅ Primary display: \(primary.name)")
17-
print(" Size: \(primary.size.width) x \(primary.size.height)")
18-
print(" Scale: \(primary.scaleFactor)x")
19-
print(" Position: (\(primary.position.x), \(primary.position.y))")
20-
print(" Work area: \(primary.workArea.width) x \(primary.workArea.height)")
21-
print(" Is primary: \(primary.isPrimary)")
22-
print(" Orientation: \(primary.orientation)")
23-
} else {
24-
print("❌ No primary display found")
25-
}
26-
27-
displayManager.get(withId: "aaa")
28-
29-
// Test cursor position
30-
let cursor = displayManager.getCursorPosition()
31-
print("✅ Cursor position: (\(cursor.x), \(cursor.y))")
32-
33-
print("✅ DisplayManager test completed successfully")
34-
print()
35-
}
36-
37-
/// Create a basic context menu with various item types
38-
func createBasicContextMenu() -> Menu {
39-
let menu = Menu()
40-
41-
// Add a simple menu item with click handler
42-
menu.addItem(text: "New File") { event in
43-
print("Creating new file: \(event.itemText)")
44-
}
45-
46-
// Add menu item with icon and keyboard shortcut
47-
let openItem = MenuItem(
48-
text: "Open...",
49-
type: .normal,
50-
icon: "folder",
51-
accelerator: KeyboardAccelerator(key: "O", modifiers: .ctrl)
52-
)
53-
openItem.onClick { event in
54-
print("Opening file dialog")
55-
}
56-
menu.addItem(openItem)
57-
58-
// Add separator
59-
menu.addSeparator()
60-
61-
// Add checkbox item
62-
menu.addCheckboxItem(text: "Show Hidden Files", checked: true) { event in
63-
print("Toggle hidden files: \(event.isChecked)")
64-
}
65-
66-
// Add radio button group
67-
menu.addRadioItem(text: "Small Icons", groupId: 1, checked: true) { event in
68-
if event.isChecked {
69-
print("Switched to small icons")
70-
}
71-
}
72-
73-
menu.addRadioItem(text: "Large Icons", groupId: 1) { event in
74-
if event.isChecked {
75-
print("Switched to large icons")
76-
}
77-
}
78-
79-
return menu
80-
}
81-
82-
/// Create minimal tray icon test
83-
@MainActor func createBasicTrayIcon() {
84-
print("=== Basic Tray Icon Demo ===")
4+
/// Create minimal tray icon with context menu
5+
@MainActor func createTrayIconWithContextMenu() {
6+
print("=== Tray Icon with Context Menu Demo ===")
857

868
// Check if system tray is supported
879
guard TrayManager.isSystemTraySupported else {
@@ -99,57 +21,71 @@ func createBasicContextMenu() -> Menu {
9921

10022
print("✅ Tray icon created successfully with ID: \(trayIcon.id)")
10123

102-
trayIcon.setTitle("Example")
103-
// Configure the tray icon with minimal properties
104-
trayIcon.setTooltip("NativeAPI Demo")
105-
print("✅ Tooltip set")
24+
trayIcon.setTitle("NativeAPI Demo")
25+
trayIcon.setTooltip("NativeAPI Tray Icon Demo")
26+
print("✅ Tray icon configured")
10627

10728
// Create context menu for tray icon
108-
let trayMenu = Menu()
29+
let contextMenu = Menu()
10930

110-
// Add "Show" menu item
111-
trayMenu.addItem(text: "显示") { event in
112-
print("📱 显示窗口")
31+
// Add "Show Window" menu item
32+
let showItem = contextMenu.addItem(text: "显示窗口") { event in
33+
print("📱 显示窗口 - 菜单项点击成功!")
34+
print(" 事件详情: ID=\(event.itemId), Text='\(event.itemText)'")
11335
}
36+
print("✅ 创建'显示窗口'菜单项,ID: \(showItem.id)")
11437

11538
// Add separator
116-
trayMenu.addSeparator()
39+
contextMenu.addSeparator()
11740

11841
// Add "About" menu item
119-
trayMenu.addItem(text: "关于") { event in
120-
print("ℹ️ NativeAPI Demo v1.0")
121-
print(" 一个跨平台的原生API演示应用")
42+
let aboutItem = contextMenu.addItem(text: "关于") { event in
43+
print("ℹ️ 关于菜单项点击成功!")
44+
print(" NativeAPI Demo v1.0")
45+
print(" 事件详情: ID=\(event.itemId), Text='\(event.itemText)'")
12246
}
47+
print("✅ 创建'关于'菜单项,ID: \(aboutItem.id)")
12348

12449
// Add "Settings" menu item
125-
trayMenu.addItem(text: "设置") { event in
126-
print("⚙️ 打开设置面板")
50+
let settingsItem = contextMenu.addItem(text: "设置") { event in
51+
print("⚙️ 设置菜单项点击成功!")
52+
print(" 打开设置面板")
53+
print(" 事件详情: ID=\(event.itemId), Text='\(event.itemText)'")
12754
}
55+
print("✅ 创建'设置'菜单项,ID: \(settingsItem.id)")
12856

12957
// Add separator
130-
trayMenu.addSeparator()
58+
contextMenu.addSeparator()
13159

13260
// Add "Exit" menu item
133-
trayMenu.addItem(text: "退出") { event in
134-
print("👋 退出应用程序")
61+
let exitItem = contextMenu.addItem(text: "退出") { event in
62+
print("👋 退出菜单项点击成功!")
63+
print(" 退出应用程序")
64+
print(" 事件详情: ID=\(event.itemId), Text='\(event.itemText)'")
13565
exit(0)
13666
}
67+
print("✅ 创建'退出'菜单项,ID: \(exitItem.id)")
13768

13869
// Set the context menu for tray icon
139-
trayIcon.setContextMenu(trayMenu)
140-
print("右键菜单已设置")
70+
trayIcon.setContextMenu(contextMenu)
71+
print("上下文菜单已设置")
14172

14273
// Configure click handlers
143-
trayIcon.onLeftClick {
144-
print("👆 Tray icon left clicked")
74+
trayIcon.onLeftClick { event in
75+
print("👆 Tray icon left clicked, ID: \(event.trayIconId)")
14576
print("💡 左键点击 - 可以显示主窗口或切换可见性")
14677
}
14778

148-
trayIcon.onRightClick {
149-
print("👆 Tray icon right clicked")
79+
trayIcon.onRightClick { event in
80+
print("👆 Tray icon right clicked, ID: \(event.trayIconId)")
15081
print("💡 右键点击 - 显示上下文菜单")
15182
}
15283

84+
trayIcon.onDoubleClick { event in
85+
print("👆 Tray icon double clicked, ID: \(event.trayIconId)")
86+
print("💡 双击事件触发")
87+
}
88+
15389
print("✅ Click handlers configured")
15490

15591
// Show the tray icon
@@ -159,96 +95,45 @@ func createBasicContextMenu() -> Menu {
15995
print("💡 右键点击托盘图标可查看菜单")
16096

16197
if let bounds = trayIcon.bounds {
162-
print(
163-
"💡 Tray icon bounds: (\(bounds.x), \(bounds.y), \(bounds.width)x\(bounds.height))")
98+
print("💡 Tray icon bounds: (\(bounds.x), \(bounds.y), \(bounds.width)x\(bounds.height))")
16499
}
165100
} else {
166101
print("❌ Failed to show tray icon")
167102
}
168103
}
169-
// MARK: - NativeAPI Demo Examples
104+
// MARK: - Main Application
170105

171-
print("=== NativeAPI 托盘菜单修复验证 ===")
172-
print("🚀 验证 C++ shared_ptr 内存管理修复")
106+
print("=== NativeAPI Tray Icon Demo ===")
107+
print("🚀 Testing TrayIcon with ContextMenu")
173108
print()
174109

175-
// Test DisplayManager functionality first
176-
print("📱 Testing DisplayManager functionality:")
177-
testDisplayManager()
178-
179-
// Test tray icon functionality with comprehensive menu
180-
print("📱 Testing Fixed Tray Icon Menu:")
181-
print("💡 验证托盘菜单的 C++ 内存管理修复")
182-
183-
createBasicTrayIcon()
184-
185-
// 添加更多菜单项测试修复
186-
guard TrayManager.isSystemTraySupported else {
187-
print("❌ 系统不支持托盘")
188-
exit(1)
189-
}
190-
191-
if let testTray = TrayManager.createTrayIcon() {
192-
print("✅ 创建额外测试托盘图标成功")
193-
194-
let testMenu = Menu()
195-
196-
// 测试多个菜单项添加 - 这是之前崩溃的操作
197-
for i in 1...5 {
198-
let item = testMenu.addItem(text: "测试菜单项 \(i)") { event in
199-
print("✅ 菜单项 \(i) 点击成功: \(event.itemText)")
200-
}
201-
print("✅ 成功添加菜单项 \(i)")
202-
}
203-
204-
// 添加分隔符
205-
testMenu.addSeparator()
206-
207-
// 添加复选框
208-
testMenu.addCheckboxItem(text: "测试复选框", checked: true) { event in
209-
print("✅ 复选框切换: \(event.isChecked)")
210-
}
211-
212-
// 添加单选按钮
213-
testMenu.addRadioItem(text: "选项 A", groupId: 1, checked: true) { event in
214-
if event.isChecked { print("✅ 选择了选项 A") }
215-
}
216-
testMenu.addRadioItem(text: "选项 B", groupId: 1, checked: false) { event in
217-
if event.isChecked { print("✅ 选择了选项 B") }
110+
// Create and run the application with tray icon
111+
@MainActor func runApplication() {
112+
// Create tray icon with context menu
113+
createTrayIconWithContextMenu()
114+
115+
// Keep the application running
116+
print("\n✅ Tray icon demo started")
117+
print("💡 应用程序正在运行,托盘图标已显示")
118+
print("💡 右键点击托盘图标查看上下文菜单")
119+
print("💡 点击菜单项测试事件处理")
120+
121+
// Create a minimal window to keep the app running
122+
let windowOptions = WindowOptions()
123+
_ = windowOptions.setTitle("NativeAPI Demo")
124+
windowOptions.setSize(Size(width: 400, height: 300))
125+
126+
print("💡 创建后台窗口以保持应用程序运行")
127+
guard let window = WindowManager.shared.create(with: windowOptions) else {
128+
print("❌ 无法创建窗口")
129+
return
218130
}
219131

220-
testTray.setContextMenu(testMenu)
221-
testTray.setTooltip("测试修复的托盘")
132+
// Hide the window so only tray icon is visible
133+
window.hide()
222134

223-
if testTray.show() {
224-
print("✅ 测试托盘显示成功")
225-
print("💡 内存管理修复验证完成")
226-
}
135+
let exitCode = AppRunner.shared.run(with: window)
136+
print("💡 应用程序退出,退出码: \(exitCode.rawValue)")
227137
}
228138

229-
print()
230-
print("🎉 C++ shared_ptr 修复验证完成!")
231-
print("📝 修复内容:")
232-
print(" ✅ 修复了 C API 中 shared_ptr 的双重删除问题")
233-
print(" ✅ 添加了全局对象存储来管理生命周期")
234-
print(" ✅ 正确处理了 MenuItem 和 Menu 的内存管理")
235-
print(" ✅ 托盘菜单现在可以正常添加多个项目")
236-
print(" ✅ 所有菜单类型(普通、复选框、单选按钮)都正常工作")
237-
print()
238-
print("🔧 技术细节:")
239-
print(" • 使用 std::unordered_map 存储 shared_ptr")
240-
print(" • C API 返回原始指针但保持 shared_ptr 引用")
241-
print(" • AddItem 时从存储中获取现有 shared_ptr")
242-
print(" • Destroy 时正确清理存储以释放对象")
243-
print()
244-
245-
// 简化的窗口测试
246-
print("📱 Simple Window Test:")
247-
let options = WindowOptions()
248-
_ = options.setTitle("NativeAPI Fixed")
249-
options.setSize(Size(width: 800, height: 600))
250-
options.setCentered(true)
251-
252-
print("⚙️ 窗口配置: 800x600 居中")
253-
let exitCode = runApp(with: options)
254-
print("✅ 程序退出码: \(exitCode)")
139+
runApplication()

Sources/NativeAPI/DisplayManager.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ public class DisplayManager: @unchecked Sendable {
1111
/// Get all available displays
1212
/// - Returns: Array of Display objects containing all system displays
1313
public func getAll() -> [Display] {
14-
let cList = native_display_manager_get_all()
14+
var cList = native_display_manager_get_all()
15+
defer { native_display_list_free(&cList) }
16+
1517
var result: [Display] = []
1618
for i in 0..<cList.count {
1719
let cDisplay = cList.displays.advanced(by: i).pointee

0 commit comments

Comments
 (0)