diff --git a/CHANGELOG.md b/CHANGELOG.md
index ed27c7a..2c46acb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,13 @@
+## 0.0.9
+
+* [Feature] add popUpContextMenu
+* [Feature] add new system tray event (leftMouseDown / rightMouseDown)
+
## 0.0.8
-* tray icon support high DPI on windows
-* Fixed crash when minimize window, then click on menubar on macOS
-* support simple appwindow on ubuntu
+* [Feature] tray icon support high DPI on windows
+* [Feature] support simple class appwindow for control window on all platforms
+* [Fixed] fixed crash when minimize window, then click on menubar on macOS
## 0.0.7
diff --git a/README.md b/README.md
index 4db33f0..462e0ba 100644
--- a/README.md
+++ b/README.md
@@ -3,17 +3,13 @@
A [Flutter package](https://github.com/antler119/system_tray.git) that that enables support for system tray menu for desktop flutter apps. **on Windows, macOS and Linux**.
-## Features
-* Modify system tray title/icon/tooltip
-* Handle system tray event leftMouseUp/rightMouseUp (only for macos、windows)
-
## Install
In the pubspec.yaml of your flutter project, add the following dependency:
```yaml
dependencies:
...
- system_tray: ^0.0.8
+ system_tray: ^0.0.9
```
In your library add the following import:
@@ -32,18 +28,94 @@ sudo apt-get install appindicator3-0.1 libappindicator3-dev
## Example App
### Windows
-
+
### macOS
-
+
### Linux
-
-
-
-## Usage:
+
+
+## API
+
+
+
+ Method |
+ Description |
+ Windows |
+ macOS |
+ Linux |
+
+
+ initSystemTray |
+ Initialize system tray |
+ ✔️ |
+ ✔️ |
+ ✔️ |
+
+
+ setSystemTrayInfo |
+ Modify the tray info |
+
+
+ |
+
+
+ - title
+ - icon
+ - toolTip
+
+ |
+
+
+ |
+
+
+ setContextMenu |
+ Set the tray context menu |
+ ✔️ |
+ ✔️ |
+ ✔️ |
+
+
+ popUpContextMenu |
+ Popup the tray context menu |
+ ✔️ |
+ ✔️ |
+ ➖ |
+
+
+ registerSystemTrayEventHandler |
+ Register system tray event |
+
+
+ - leftMouseUp
+ - leftMouseDown
+ - leftMouseDblClk
+ - rightMouseUp
+ - rightMouseDown
+
+ |
+
+
+ - leftMouseUp
+ - leftMouseDown
+ - rightMouseUp
+ - rightMouseDown
+
+ |
+ ➖ |
+
+
+
+## Usage
Smallest example:
```dart
@@ -60,16 +132,29 @@ Future initSystemTray() async {
MenuItem(label: 'Exit', onClicked: _appWindow.close),
];
+ // We first init the systray menu and then add the menu entries
await _systemTray.initSystemTray(
title: "system tray",
iconPath: path,
);
await _systemTray.setContextMenu(menu);
+
+ // handle system tray event
+ _systemTray.registerSystemTrayEventHandler((eventName) {
+ debugPrint("eventName: $eventName");
+ if (eventName == "leftMouseDown") {
+ } else if (eventName == "leftMouseUp") {
+ _systemTray.popUpContextMenu();
+ } else if (eventName == "rightMouseDown") {
+ } else if (eventName == "rightMouseUp") {
+ _appWindow.show();
+ }
+ });
}
```
-Icon flashing effect example:
+Flashing icon example:
```dart
Future initSystemTray() async {
@@ -113,27 +198,43 @@ Future initSystemTray() async {
),
MenuSeparator(),
SubMenu(
- label: "SubMenu",
+ label: "Test API",
children: [
- MenuItem(
- label: 'SubItem1',
- enabled: false,
- onClicked: () {
- debugPrint("click SubItem1");
- },
- ),
- MenuItem(
- label: 'SubItem2',
- onClicked: () {
- debugPrint("click SubItem2");
- },
- ),
- MenuItem(
- label: 'SubItem3',
- onClicked: () {
- debugPrint("click SubItem3");
- },
+ SubMenu(
+ label: "setSystemTrayInfo",
+ children: [
+ MenuItem(
+ label: 'set title',
+ onClicked: () {
+ final String text = WordPair.random().asPascalCase;
+ debugPrint("click 'set title' : $text");
+ _systemTray.setSystemTrayInfo(
+ title: text,
+ );
+ },
+ ),
+ MenuItem(
+ label: 'set icon path',
+ onClicked: () {
+ debugPrint("click 'set icon path' : $path");
+ _systemTray.setSystemTrayInfo(
+ iconPath: path,
+ );
+ },
+ ),
+ MenuItem(
+ label: 'set tooltip',
+ onClicked: () {
+ final String text = WordPair.random().asPascalCase;
+ debugPrint("click 'set tooltip' : $text");
+ _systemTray.setSystemTrayInfo(
+ toolTip: text,
+ );
+ },
+ ),
+ ],
),
+ MenuItem(label: 'disabled Item', enabled: false),
],
),
MenuSeparator(),
@@ -155,7 +256,11 @@ Future initSystemTray() async {
// handle system tray event
_systemTray.registerSystemTrayEventHandler((eventName) {
debugPrint("eventName: $eventName");
- if (eventName == "leftMouseUp") {
+ if (eventName == "leftMouseDown") {
+ } else if (eventName == "leftMouseUp") {
+ _systemTray.popUpContextMenu();
+ } else if (eventName == "rightMouseDown") {
+ } else if (eventName == "rightMouseUp") {
_appWindow.show();
}
});
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 21c7114..26a9356 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -4,7 +4,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:bitsdojo_window/bitsdojo_window.dart';
-
+import 'package:english_words/english_words.dart';
import 'package:system_tray/system_tray.dart';
void main() async {
@@ -88,27 +88,43 @@ class _MyAppState extends State {
),
MenuSeparator(),
SubMenu(
- label: "SubMenu",
+ label: "Test API",
children: [
- MenuItem(
- label: 'SubItem1',
- enabled: false,
- onClicked: () {
- debugPrint("click SubItem1");
- },
- ),
- MenuItem(
- label: 'SubItem2',
- onClicked: () {
- debugPrint("click SubItem2");
- },
- ),
- MenuItem(
- label: 'SubItem3',
- onClicked: () {
- debugPrint("click SubItem3");
- },
+ SubMenu(
+ label: "setSystemTrayInfo",
+ children: [
+ MenuItem(
+ label: 'set title',
+ onClicked: () {
+ final String text = WordPair.random().asPascalCase;
+ debugPrint("click 'set title' : $text");
+ _systemTray.setSystemTrayInfo(
+ title: text,
+ );
+ },
+ ),
+ MenuItem(
+ label: 'set icon path',
+ onClicked: () {
+ debugPrint("click 'set icon path' : $path");
+ _systemTray.setSystemTrayInfo(
+ iconPath: path,
+ );
+ },
+ ),
+ MenuItem(
+ label: 'set toolTip',
+ onClicked: () {
+ final String text = WordPair.random().asPascalCase;
+ debugPrint("click 'set toolTip' : $text");
+ _systemTray.setSystemTrayInfo(
+ toolTip: text,
+ );
+ },
+ ),
+ ],
),
+ MenuItem(label: 'disabled Item', enabled: false),
],
),
MenuSeparator(),
@@ -130,8 +146,12 @@ class _MyAppState extends State {
// handle system tray event
_systemTray.registerSystemTrayEventHandler((eventName) {
debugPrint("eventName: $eventName");
- if (eventName == "leftMouseUp") {
+ if (eventName == "leftMouseDown") {
+ } else if (eventName == "leftMouseUp") {
_appWindow.show();
+ } else if (eventName == "rightMouseDown") {
+ } else if (eventName == "rightMouseUp") {
+ _systemTray.popUpContextMenu();
}
});
}
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 3311f63..a9a7e0a 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -85,6 +85,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.4"
+ english_words:
+ dependency: "direct main"
+ description:
+ name: english_words
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "4.0.0"
fake_async:
dependency: transitive
description:
@@ -197,7 +204,7 @@ packages:
path: ".."
relative: true
source: path
- version: "0.0.8"
+ version: "0.0.9"
term_glyph:
dependency: transitive
description:
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index ef44245..6ab64d0 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -30,6 +30,7 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
bitsdojo_window: 0.1.1+1
+ english_words: ^4.0.0
dev_dependencies:
flutter_test:
diff --git a/lib/src/tray.dart b/lib/src/tray.dart
index c4ed37a..523a630 100644
--- a/lib/src/tray.dart
+++ b/lib/src/tray.dart
@@ -12,6 +12,8 @@ const String _kChannelName = "flutter/system_tray";
const String _kInitSystemTray = "InitSystemTray";
const String _kSetSystemTrayInfo = "SetSystemTrayInfo";
const String _kSetContextMenu = "SetContextMenu";
+const String _kPopupContextMenu = "PopupContextMenu";
+
const String _kMenuItemSelectedCallbackMethod = 'MenuItemSelectedCallback';
const String _kSystemTrayEventCallbackMethod = 'SystemTrayEventCallback';
@@ -51,7 +53,7 @@ class SystemTray {
//
SystemTrayEventCallback? _systemTrayEventCallback;
- // Show a SystemTray icon
+ /// Show a SystemTray icon
Future initSystemTray({
required String title,
required String iconPath,
@@ -68,6 +70,7 @@ class SystemTray {
return value;
}
+ /// Set system info info
Future setSystemTrayInfo({
String? title,
String? iconPath,
@@ -84,6 +87,7 @@ class SystemTray {
return value;
}
+ /// register listener for system tray event.
void registerSystemTrayEventHandler(SystemTrayEventCallback callback) {
_systemTrayEventCallback = callback;
}
@@ -104,6 +108,12 @@ class SystemTray {
}
}
+ /// Pop up the context menu.
+ ///
+ Future popUpContextMenu() async {
+ await _platformChannel.invokeMethod(_kPopupContextMenu);
+ }
+
/// Converts [menus] to a representation that can be sent in the arguments to
/// [_kMenuSetMethod].
///
diff --git a/macos/Classes/SystemTrayPlugin.swift b/macos/Classes/SystemTrayPlugin.swift
index c0e2966..56e89a2 100644
--- a/macos/Classes/SystemTrayPlugin.swift
+++ b/macos/Classes/SystemTrayPlugin.swift
@@ -3,27 +3,29 @@ import FlutterMacOS
let kChannelName = "flutter/system_tray"
-let kInitSystemTray = "InitSystemTray";
-let kSetSystemTrayInfo = "SetSystemTrayInfo";
-let kSetContextMenu = "SetContextMenu";
-let kMenuItemSelectedCallbackMethod = "MenuItemSelectedCallback";
+let kInitSystemTray = "InitSystemTray"
+let kSetSystemTrayInfo = "SetSystemTrayInfo"
+let kSetContextMenu = "SetContextMenu"
+let kPopupContextMenu = "PopupContextMenu"
+
+let kMenuItemSelectedCallbackMethod = "MenuItemSelectedCallback"
let kTitleKey = "title"
let kIconPathKey = "iconpath"
let kToolTipKey = "tooltip"
-let kIdKey = "id";
-let kTypeKey = "type";
-let kLabelKey = "label";
-let kSeparatorKey = "separator";
-let kSubMenuKey = "submenu";
-let kEnabledKey = "enabled";
+let kIdKey = "id"
+let kTypeKey = "type"
+let kLabelKey = "label"
+let kSeparatorKey = "separator"
+let kSubMenuKey = "submenu"
+let kEnabledKey = "enabled"
let kChannelAppWindowName = "flutter/system_tray/app_window"
-let kInitAppWindow = "InitAppWindow";
-let kShowAppWindow = "ShowAppWindow";
-let kHideAppWindow = "HideAppWindow";
-let kCloseAppWindow = "CloseAppWindow";
+let kInitAppWindow = "InitAppWindow"
+let kShowAppWindow = "ShowAppWindow"
+let kHideAppWindow = "HideAppWindow"
+let kCloseAppWindow = "CloseAppWindow"
public class SystemTrayPlugin: NSObject, FlutterPlugin {
var system_tray: SystemTray?
@@ -34,15 +36,19 @@ public class SystemTrayPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: kChannelName, binaryMessenger: registrar.messenger)
- let channel_app_window = FlutterMethodChannel(name: kChannelAppWindowName, binaryMessenger: registrar.messenger)
+ let channel_app_window = FlutterMethodChannel(
+ name: kChannelAppWindowName, binaryMessenger: registrar.messenger)
let instance = SystemTrayPlugin(registrar, channel, channel_app_window)
-
+
registrar.addMethodCallDelegate(instance, channel: channel)
registrar.addMethodCallDelegate(instance, channel: channel_app_window)
}
- init(_ registrar: FlutterPluginRegistrar, _ channel: FlutterMethodChannel, _ channel_app_window: FlutterMethodChannel) {
+ init(
+ _ registrar: FlutterPluginRegistrar, _ channel: FlutterMethodChannel,
+ _ channel_app_window: FlutterMethodChannel)
+ {
system_tray = SystemTray(channel)
self.channel = channel
self.channel_app_window = channel_app_window
@@ -59,6 +65,8 @@ public class SystemTrayPlugin: NSObject, FlutterPlugin {
setSystemTrayInfo(call, result)
case kSetContextMenu:
setContextMenu(call, result)
+ case kPopupContextMenu:
+ popUpContextMenu(call, result)
case kInitAppWindow:
initAppWindow(call, result)
case kShowAppWindow:
@@ -73,19 +81,20 @@ public class SystemTrayPlugin: NSObject, FlutterPlugin {
}
func initSystemTray(_ call: FlutterMethodCall, _ result: FlutterResult) {
- let arguments = call.arguments as! [String: Any]
- let title = arguments[kTitleKey] as? String
- let iconPath = arguments[kIconPathKey] as? String
- let toolTip = arguments[kToolTipKey] as? String
- result(system_tray?.initSystemTray(title: title, iconPath: iconPath, toolTip: toolTip) ?? false)
+ let arguments = call.arguments as! [String: Any]
+ let title = arguments[kTitleKey] as? String
+ let iconPath = arguments[kIconPathKey] as? String
+ let toolTip = arguments[kToolTipKey] as? String
+ result(system_tray?.initSystemTray(title: title, iconPath: iconPath, toolTip: toolTip) ?? false)
}
func setSystemTrayInfo(_ call: FlutterMethodCall, _ result: FlutterResult) {
- let arguments = call.arguments as! [String: Any]
- let title = arguments[kTitleKey] as? String
- let iconPath = arguments[kIconPathKey] as? String
- let toolTip = arguments[kToolTipKey] as? String
- result(system_tray?.setSystemTrayInfo(title: title, iconPath: iconPath, toolTip: toolTip) ?? false)
+ let arguments = call.arguments as! [String: Any]
+ let title = arguments[kTitleKey] as? String
+ let iconPath = arguments[kIconPathKey] as? String
+ let toolTip = arguments[kToolTipKey] as? String
+ result(
+ system_tray?.setSystemTrayInfo(title: title, iconPath: iconPath, toolTip: toolTip) ?? false)
}
func valueToMenuItem(menu: NSMenu, item: [String: Any]) -> Bool {
@@ -99,9 +108,9 @@ public class SystemTrayPlugin: NSObject, FlutterPlugin {
let isEnabled = item[kEnabledKey] as? Bool ?? false
switch type! {
- case kSeparatorKey:
+ case kSeparatorKey:
menu.addItem(.separator())
- case kSubMenuKey:
+ case kSubMenuKey:
let subMenu = NSMenu()
let children = item[kSubMenuKey] as? [[String: Any]] ?? [[String: Any]]()
if valueToMenu(menu: subMenu, items: children) {
@@ -110,7 +119,7 @@ public class SystemTrayPlugin: NSObject, FlutterPlugin {
menuItem.submenu = subMenu
menu.addItem(menuItem)
}
- default:
+ default:
let menuItem = NSMenuItem()
menuItem.title = label
menuItem.target = self
@@ -123,103 +132,107 @@ public class SystemTrayPlugin: NSObject, FlutterPlugin {
}
func valueToMenu(menu: NSMenu, items: [[String: Any]]) -> Bool {
- for item in items {
- if !valueToMenuItem(menu: menu, item: item) {
- return false
- }
+ for item in items {
+ if !valueToMenuItem(menu: menu, item: item) {
+ return false
}
- return true
+ }
+ return true
}
func setContextMenu(_ call: FlutterMethodCall, _ result: FlutterResult) {
- var popup_menu: NSMenu
- repeat {
- let items = call.arguments as? [[String: Any]]
- if items == nil {
- break
- }
-
- popup_menu = NSMenu()
- if !valueToMenu(menu: popup_menu, items: items!) {
- break
- }
-
- result(system_tray?.setContextMenu(menu: popup_menu) ?? false)
- return
- } while false
-
- result(false)
+ var popup_menu: NSMenu
+ repeat {
+ let items = call.arguments as? [[String: Any]]
+ if items == nil {
+ break
+ }
+
+ popup_menu = NSMenu()
+ if !valueToMenu(menu: popup_menu, items: items!) {
+ break
+ }
+
+ result(system_tray?.setContextMenu(menu: popup_menu) ?? false)
+ return
+ } while false
+
+ result(false)
+ }
+
+ func popUpContextMenu(_ call: FlutterMethodCall, _ result: FlutterResult) {
+ result(system_tray?.popUpContextMenu() ?? false)
}
- @objc func onMenuItemSelectedCallback(_ sender:Any) {
+ @objc func onMenuItemSelectedCallback(_ sender: Any) {
let menuItem = sender as! NSMenuItem
channel.invokeMethod(kMenuItemSelectedCallbackMethod, arguments: menuItem.tag, result: nil)
}
func initAppWindow(_ call: FlutterMethodCall, _ result: FlutterResult) {
- repeat {
- if self.app_window != nil {
- result(true)
- break;
- }
-
- let view = registrar.view
- if view == nil {
- break
- }
-
- let window = view!.window;
- if window == nil {
- break
- }
-
- self.app_window = AppWindow(channel, window!)
- result(true)
- return
- } while false
-
- result(false)
+ repeat {
+ if app_window != nil {
+ result(true)
+ break
+ }
+
+ let view = registrar.view
+ if view == nil {
+ break
+ }
+
+ let window = view!.window
+ if window == nil {
+ break
+ }
+
+ app_window = AppWindow(channel, window!)
+ result(true)
+ return
+ } while false
+
+ result(false)
}
func showAppWindow(_ call: FlutterMethodCall, _ result: FlutterResult) {
- repeat {
- if self.app_window == nil {
- break;
- }
+ repeat {
+ if app_window == nil {
+ break
+ }
- self.app_window!.showAppWindow()
- result(true)
- return
- } while false
+ app_window!.showAppWindow()
+ result(true)
+ return
+ } while false
- result(false)
+ result(false)
}
func hideAppWindow(_ call: FlutterMethodCall, _ result: FlutterResult) {
- repeat {
- if self.app_window == nil {
- break;
- }
+ repeat {
+ if app_window == nil {
+ break
+ }
- self.app_window!.hideAppWindow()
- result(true)
- return
- } while false
+ app_window!.hideAppWindow()
+ result(true)
+ return
+ } while false
- result(false)
+ result(false)
}
func closeAppWindow(_ call: FlutterMethodCall, _ result: FlutterResult) {
- repeat {
- if self.app_window == nil {
- break;
- }
+ repeat {
+ if app_window == nil {
+ break
+ }
- self.app_window!.closeAppWindow()
- result(true)
- return
- } while false
+ app_window!.closeAppWindow()
+ result(true)
+ return
+ } while false
- result(false)
+ result(false)
}
}
diff --git a/macos/Classes/Tray.swift b/macos/Classes/Tray.swift
index ef25f80..84594a5 100644
--- a/macos/Classes/Tray.swift
+++ b/macos/Classes/Tray.swift
@@ -1,92 +1,104 @@
-
import FlutterMacOS
let kDefaultSizeWidth = 18
let kDefaultSizeHeight = 18
-let kSystemTrayEventCallbackMethod = "SystemTrayEventCallback";
+let kSystemTrayEventCallbackMethod = "SystemTrayEventCallback"
+
+let kSystemTrayEventLButtnUp = "leftMouseUp"
+let kSystemTrayEventLButtnDown = "leftMouseDown"
+let kSystemTrayEventRButtnUp = "rightMouseUp"
+let kSystemTrayEventRButtnDown = "rightMouseDown"
+
+class SystemTray: NSObject, NSMenuDelegate {
+ var statusItem: NSStatusItem?
+ var statusItemMenu: NSMenu?
+ var channel: FlutterMethodChannel
+
+ init(_ channel: FlutterMethodChannel) {
+ self.channel = channel
+ }
+
+ func menuDidClose(_ menu: NSMenu) {
+ statusItem?.menu = nil
+ }
+
+ @objc func onSystemTrayEventCallback(sender: NSStatusBarButton) {
+ if let event = NSApp.currentEvent {
+ switch event.type {
+ case .leftMouseUp:
+ channel.invokeMethod(kSystemTrayEventCallbackMethod, arguments: kSystemTrayEventLButtnUp)
+ case .leftMouseDown:
+ channel.invokeMethod(kSystemTrayEventCallbackMethod, arguments: kSystemTrayEventLButtnDown)
+ case .rightMouseUp:
+ channel.invokeMethod(kSystemTrayEventCallbackMethod, arguments: kSystemTrayEventRButtnUp)
+ case .rightMouseDown:
+ channel.invokeMethod(kSystemTrayEventCallbackMethod, arguments: kSystemTrayEventRButtnDown)
+ default:
+ break
+ }
+ }
+ }
-let kSystemTrayEventLButtnUp = "leftMouseUp";
-let kSystemTrayEventLButtonDblClk = "leftMouseDblClk";
-let kSystemTrayEventRButtnUp = "rightMouseUp";
+ func initSystemTray(title: String?, iconPath: String?, toolTip: String?) -> Bool {
+ statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
-class SystemTray : NSObject, NSMenuDelegate {
- var statusItem: NSStatusItem?
- var statusItemMenu: NSMenu?
- var channel: FlutterMethodChannel
+ statusItem?.button?.action = #selector(onSystemTrayEventCallback(sender:))
+ statusItem?.button?.target = self
+ statusItem?.button?.sendAction(on: [
+ .leftMouseUp, .leftMouseDown, .rightMouseUp, .rightMouseDown,
+ ])
- init(_ channel: FlutterMethodChannel) {
- self.channel = channel
+ if let toolTip = toolTip {
+ statusItem?.button?.toolTip = toolTip
}
-
- func menuDidClose(_ menu: NSMenu) {
- self.statusItem?.menu = nil
+ if let title = title {
+ statusItem?.button?.title = title
}
- @objc func onSystemTrayEventCallback(sender: NSStatusBarButton) {
- if let event = NSApp.currentEvent {
- switch event.type {
- case .leftMouseUp:
- channel.invokeMethod(kSystemTrayEventCallbackMethod, arguments: kSystemTrayEventLButtnUp)
- default:
- channel.invokeMethod(kSystemTrayEventCallbackMethod, arguments: kSystemTrayEventRButtnUp)
-
- statusItem?.menu = statusItemMenu
- statusItem?.button?.performClick(nil)
- }
- }
+ if let iconPath = iconPath {
+ if let itemImage = NSImage(named: iconPath) {
+ let destSize = NSSize(width: kDefaultSizeWidth, height: kDefaultSizeHeight)
+ itemImage.size = destSize
+ statusItem?.button?.image = itemImage
+ statusItem?.button?.imagePosition = NSControl.ImagePosition.imageLeft
+ }
}
+ return true
+ }
- func initSystemTray(title: String?, iconPath: String?, toolTip: String?) -> Bool {
- statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
-
- statusItem?.button?.action = #selector(onSystemTrayEventCallback(sender:))
- statusItem?.button?.target = self
- statusItem?.button?.sendAction(on: [.leftMouseUp, .rightMouseUp])
-
- if let toolTip = toolTip {
- statusItem?.button?.toolTip = toolTip
- }
- if let title = title {
- statusItem?.button?.title = title
- }
-
- if let iconPath = iconPath {
- if let itemImage = NSImage(named: iconPath) {
- let destSize = NSSize(width: kDefaultSizeWidth, height: kDefaultSizeHeight)
- itemImage.size = destSize
- statusItem?.button?.image = itemImage
- statusItem?.button?.imagePosition = NSControl.ImagePosition.imageLeft
- }
- }
- return true
+ func setSystemTrayInfo(title: String?, iconPath: String?, toolTip: String?) -> Bool {
+ if let toolTip = toolTip {
+ statusItem?.button?.toolTip = toolTip
}
-
- func setSystemTrayInfo(title: String?, iconPath: String?, toolTip: String?) -> Bool {
- if let toolTip = toolTip {
- statusItem?.button?.toolTip = toolTip
- }
- if let title = title {
- statusItem?.button?.title = title
- }
- if let iconPath = iconPath {
- if let itemImage = NSImage(named: iconPath) {
- let destSize = NSSize(width: kDefaultSizeWidth, height: kDefaultSizeHeight)
- itemImage.size = destSize
- statusItem?.button?.image = itemImage
- statusItem?.button?.imagePosition = NSControl.ImagePosition.imageLeft
- } else {
- statusItem?.button?.image = nil
- }
- }
- return true
+ if let title = title {
+ statusItem?.button?.title = title
}
-
- func setContextMenu(menu: NSMenu) -> Bool {
- statusItemMenu = menu
- statusItemMenu?.delegate = self
- return true
+ if let iconPath = iconPath {
+ if let itemImage = NSImage(named: iconPath) {
+ let destSize = NSSize(width: kDefaultSizeWidth, height: kDefaultSizeHeight)
+ itemImage.size = destSize
+ statusItem?.button?.image = itemImage
+ statusItem?.button?.imagePosition = NSControl.ImagePosition.imageLeft
+ } else {
+ statusItem?.button?.image = nil
+ }
}
-
-
+ return true
+ }
+
+ func setContextMenu(menu: NSMenu) -> Bool {
+ statusItemMenu = menu
+ statusItemMenu?.delegate = self
+ return true
+ }
+
+ func popUpContextMenu() -> Bool {
+ if let statusItemMenu = statusItemMenu {
+ statusItem?.menu = statusItemMenu
+ statusItem?.button?.performClick(nil)
+ return true
+ }
+ return false
+ }
}
diff --git a/pubspec.yaml b/pubspec.yaml
index 60700b5..ed903e2 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: system_tray
-description: system_tray that makes it easy to customize tray and work with your Flutter desktop app window **on Windows, macOS and Linux**.
-version: 0.0.8
+description: system_tray that makes it easy to customize tray and work with your Flutter desktop app.
+version: 0.0.9
repository: https://github.com/antler119/system_tray.git
environment:
diff --git a/resources/screenshot_macos.jpg b/resources/screenshot_macos.jpg
deleted file mode 100644
index 2039683..0000000
Binary files a/resources/screenshot_macos.jpg and /dev/null differ
diff --git a/resources/screenshot_macos.png b/resources/screenshot_macos.png
new file mode 100644
index 0000000..d3e0e84
Binary files /dev/null and b/resources/screenshot_macos.png differ
diff --git a/resources/screenshot_ubuntu.jpg b/resources/screenshot_ubuntu.jpg
deleted file mode 100644
index ba82ada..0000000
Binary files a/resources/screenshot_ubuntu.jpg and /dev/null differ
diff --git a/resources/screenshot_ubuntu.png b/resources/screenshot_ubuntu.png
new file mode 100644
index 0000000..b8aa1de
Binary files /dev/null and b/resources/screenshot_ubuntu.png differ
diff --git a/resources/screenshot_windows.jpg b/resources/screenshot_windows.jpg
deleted file mode 100644
index b1c53f8..0000000
Binary files a/resources/screenshot_windows.jpg and /dev/null differ
diff --git a/resources/screenshot_windows.png b/resources/screenshot_windows.png
new file mode 100644
index 0000000..1af488e
Binary files /dev/null and b/resources/screenshot_windows.png differ
diff --git a/windows/app_window.cpp b/windows/app_window.cpp
index 446d3aa..613f3b9 100644
--- a/windows/app_window.cpp
+++ b/windows/app_window.cpp
@@ -7,8 +7,9 @@ bool AppWindow::initAppWindow(HWND window, HWND flutter_window) {
}
bool AppWindow::showAppWindow(bool visible) {
- if (!IsWindow(window_))
+ if (!IsWindow(window_)) {
return false;
+ }
if (visible) {
activeWindow();
@@ -20,19 +21,22 @@ bool AppWindow::showAppWindow(bool visible) {
}
bool AppWindow::closeAppWindow() {
- if (!IsWindow(window_))
+ if (!IsWindow(window_)) {
return false;
+ }
PostMessage(window_, WM_SYSCOMMAND, SC_CLOSE, 0);
return true;
}
void AppWindow::activeWindow() {
- if (!IsWindow(window_))
+ if (!IsWindow(window_)) {
return;
+ }
- if (!::IsWindowVisible(window_))
+ if (!::IsWindowVisible(window_)) {
ShowWindow(window_, SW_SHOW);
+ }
if (IsIconic(window_)) {
SendMessage(window_, WM_SYSCOMMAND, SC_RESTORE | HTCAPTION, 0);
@@ -43,8 +47,9 @@ void AppWindow::activeWindow() {
}
void AppWindow::refreshFlutterWindow() {
- if (!IsWindow(flutter_window_))
+ if (!IsWindow(flutter_window_)) {
return;
+ }
RECT rc = {};
GetClientRect(flutter_window_, &rc);
diff --git a/windows/system_tray_plugin.cpp b/windows/system_tray_plugin.cpp
index 2eadf9d..f4f4a96 100644
--- a/windows/system_tray_plugin.cpp
+++ b/windows/system_tray_plugin.cpp
@@ -27,6 +27,8 @@ const static char kChannelName[] = "flutter/system_tray";
const static char kInitSystemTray[] = "InitSystemTray";
const static char kSetSystemTrayInfo[] = "SetSystemTrayInfo";
const static char kSetContextMenu[] = "SetContextMenu";
+const static char kPopupContextMenu[] = "PopupContextMenu";
+
const static char kMenuItemSelectedCallbackMethod[] =
"MenuItemSelectedCallback";
const static char kSystemTrayEventCallbackMethod[] = "SystemTrayEventCallback";
@@ -122,6 +124,10 @@ class SystemTrayPlugin : public flutter::Plugin, public SystemTray::Delegate {
const flutter::MethodCall& method_call,
flutter::MethodResult& result);
+ void popupContextMenu(
+ const flutter::MethodCall& method_call,
+ flutter::MethodResult& result);
+
bool valueToMenu(HMENU menu, const flutter::EncodableList& representation);
bool valueToMenuItem(HMENU menu, const flutter::EncodableMap& representation);
@@ -222,6 +228,8 @@ void SystemTrayPlugin::HandleMethodCall(
setSystemTrayInfo(method_call, *result);
} else if (method_call.method_name().compare(kSetContextMenu) == 0) {
setContextMenu(method_call, *result);
+ } else if (method_call.method_name().compare(kPopupContextMenu) == 0) {
+ popupContextMenu(method_call, *result);
} else if (method_call.method_name().compare(kInitAppWindow) == 0) {
initAppWindow(method_call, *result);
} else if (method_call.method_name().compare(kShowAppWindow) == 0) {
@@ -265,7 +273,8 @@ void SystemTrayPlugin::initSystemTray(
const std::string* toolTip =
std::get_if(ValueOrNull(*map, kToolTipKey));
- if (!system_tray_->initSystemTray(window, title, iconPath, toolTip)) {
+ if (!system_tray_ ||
+ !system_tray_->initSystemTray(window, title, iconPath, toolTip)) {
result.Error(kBadArgumentsError, "Unable to init system tray",
flutter::EncodableValue(false));
break;
@@ -297,7 +306,8 @@ void SystemTrayPlugin::setSystemTrayInfo(
const std::string* toolTip =
std::get_if(ValueOrNull(*map, kToolTipKey));
- if (!system_tray_->setSystemTrayInfo(title, iconPath, toolTip)) {
+ if (!system_tray_ ||
+ !system_tray_->setSystemTrayInfo(title, iconPath, toolTip)) {
result.Error(kBadArgumentsError, "Unable to set system tray info",
flutter::EncodableValue(false));
break;
@@ -389,7 +399,7 @@ void SystemTrayPlugin::setContextMenu(
break;
}
- if (!system_tray_->setContextMenu(popup_menu)) {
+ if (!system_tray_ || !system_tray_->setContextMenu(popup_menu)) {
result.Error(kBadArgumentsError, "Unable to set context menu",
flutter::EncodableValue(false));
break;
@@ -405,6 +415,22 @@ void SystemTrayPlugin::setContextMenu(
}
}
+void SystemTrayPlugin::popupContextMenu(
+ const flutter::MethodCall& method_call,
+ flutter::MethodResult& result) {
+ do {
+ if (!system_tray_) {
+ result.Error(kBadArgumentsError, "Expected system tray",
+ flutter::EncodableValue(false));
+ break;
+ }
+
+ system_tray_->popUpContextMenu();
+
+ result.Success(flutter::EncodableValue(true));
+ } while (false);
+}
+
std::optional SystemTrayPlugin::HandleWindowProc(HWND hwnd,
UINT message,
WPARAM wparam,
diff --git a/windows/tray.cpp b/windows/tray.cpp
index 606fc9e..2fba534 100644
--- a/windows/tray.cpp
+++ b/windows/tray.cpp
@@ -12,8 +12,10 @@ constexpr const wchar_t kTrayWindowClassName[] =
}
const static char kSystemTrayEventLButtnUp[] = "leftMouseUp";
+const static char kSystemTrayEventLButtnDown[] = "leftMouseDown";
const static char kSystemTrayEventLButtonDblClk[] = "leftMouseDblClk";
const static char kSystemTrayEventRButtnUp[] = "rightMouseUp";
+const static char kSystemTrayEventRButtnDown[] = "rightMouseDown";
// Converts the given UTF-8 string to UTF-16.
static std::wstring Utf16FromUtf8(const std::string& utf8_string) {
@@ -121,6 +123,10 @@ bool SystemTray::setContextMenu(HMENU context_menu) {
return true;
}
+void SystemTray::popUpContextMenu() {
+ ShowPopupMenu();
+}
+
bool SystemTray::installTrayIcon(HWND window,
const std::string* title,
const std::string* iconPath,
@@ -210,6 +216,11 @@ std::optional SystemTray::OnTrayIconCallback(UINT id,
const POINT& pt) {
do {
switch (notifyMsg) {
+ case WM_LBUTTONDOWN: {
+ if (delegate_) {
+ delegate_->OnSystemTrayEventCallback(kSystemTrayEventLButtnDown);
+ }
+ } break;
case WM_LBUTTONUP: {
if (delegate_) {
delegate_->OnSystemTrayEventCallback(kSystemTrayEventLButtnUp);
@@ -220,12 +231,21 @@ std::optional SystemTray::OnTrayIconCallback(UINT id,
delegate_->OnSystemTrayEventCallback(kSystemTrayEventLButtonDblClk);
}
} break;
+ case WM_RBUTTONDOWN: {
+ if (delegate_) {
+ delegate_->OnSystemTrayEventCallback(kSystemTrayEventRButtnDown);
+ }
+ } break;
case WM_RBUTTONUP: {
if (delegate_) {
delegate_->OnSystemTrayEventCallback(kSystemTrayEventRButtnUp);
}
- ShowPopupMenu();
} break;
+ // default: {
+ // printf("OnTrayIconCallback id:%d, 0x%x, pt[%d,%d]\n", id,
+ // notifyMsg,
+ // pt.x, pt.y);
+ // } break;
}
} while (false);
diff --git a/windows/tray.h b/windows/tray.h
index d07b1ea..0e2f47f 100644
--- a/windows/tray.h
+++ b/windows/tray.h
@@ -30,6 +30,8 @@ class SystemTray {
bool setContextMenu(HMENU context_menu);
+ void popUpContextMenu();
+
std::optional HandleWindowProc(HWND hwnd,
UINT message,
WPARAM wparam,