diff --git a/README.md b/README.md
index 2b21711..a8d19ab 100644
--- a/README.md
+++ b/README.md
@@ -84,7 +84,9 @@ sudo apt-get install libayatana-appindicator3-dev
|
@@ -100,14 +102,14 @@ sudo apt-get install libayatana-appindicator3-dev
Modify the tray tooltip |
✔️ |
✔️ |
- ➖ |
+ ✔️ |
| setTitle / getTitle |
Set / Get the tray title |
➖ |
✔️ |
- ➖ |
+ ✔️ |
| setContextMenu |
@@ -147,7 +149,14 @@ sudo apt-get install libayatana-appindicator3-dev
right-click
- ➖ |
+
+
+ - click
+ - right-click
+ - double-click
+ - scroll
+
+ |
diff --git a/example/linux/flutter/generated_plugin_registrant.cc b/example/linux/flutter/generated_plugin_registrant.cc
index f178fea..bc8dc59 100644
--- a/example/linux/flutter/generated_plugin_registrant.cc
+++ b/example/linux/flutter/generated_plugin_registrant.cc
@@ -7,13 +7,9 @@
#include "generated_plugin_registrant.h"
#include
-#include
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) bitsdojo_window_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "BitsdojoWindowPlugin");
bitsdojo_window_plugin_register_with_registrar(bitsdojo_window_linux_registrar);
- g_autoptr(FlPluginRegistrar) system_tray_registrar =
- fl_plugin_registry_get_registrar_for_plugin(registry, "SystemTrayPlugin");
- system_tray_plugin_register_with_registrar(system_tray_registrar);
}
diff --git a/example/linux/flutter/generated_plugins.cmake b/example/linux/flutter/generated_plugins.cmake
index b62057c..cfdc298 100644
--- a/example/linux/flutter/generated_plugins.cmake
+++ b/example/linux/flutter/generated_plugins.cmake
@@ -4,7 +4,6 @@
list(APPEND FLUTTER_PLUGIN_LIST
bitsdojo_window_linux
- system_tray
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 69083bb..6142ca8 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -1,118 +1,150 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
+ args:
+ dependency: transitive
+ description:
+ name: args
+ sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.7.0"
async:
dependency: transitive
description:
name: async
- url: "https://pub.flutter-io.cn"
+ sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
+ url: "https://pub.dev"
source: hosted
- version: "2.9.0"
+ version: "2.13.0"
bitsdojo_window:
dependency: "direct main"
description:
name: bitsdojo_window
- url: "https://pub.flutter-io.cn"
+ sha256: "69afdbea4273d984ef8064be967f8cdc303a79909879867afecbbf56f5ebc35f"
+ url: "https://pub.dev"
source: hosted
version: "0.1.2"
bitsdojo_window_linux:
dependency: transitive
description:
name: bitsdojo_window_linux
- url: "https://pub.flutter-io.cn"
+ sha256: "9519c0614f98be733e0b1b7cb15b827007886f6fe36a4fb62cf3d35b9dd578ab"
+ url: "https://pub.dev"
source: hosted
- version: "0.1.2"
+ version: "0.1.4"
bitsdojo_window_macos:
dependency: transitive
description:
name: bitsdojo_window_macos
- url: "https://pub.flutter-io.cn"
+ sha256: f7c5be82e74568c68c5b8449e2c5d8fd12ec195ecd70745a7b9c0f802bb0268f
+ url: "https://pub.dev"
source: hosted
- version: "0.1.2"
+ version: "0.1.4"
bitsdojo_window_platform_interface:
dependency: transitive
description:
name: bitsdojo_window_platform_interface
- url: "https://pub.flutter-io.cn"
+ sha256: "65daa015a0c6dba749bdd35a0f092e7a8ba8b0766aa0480eb3ef808086f6e27c"
+ url: "https://pub.dev"
source: hosted
version: "0.1.2"
bitsdojo_window_windows:
dependency: transitive
description:
name: bitsdojo_window_windows
- url: "https://pub.flutter-io.cn"
+ sha256: fa982cf61ede53f483e50b257344a1c250af231a3cdc93a7064dd6dc0d720b68
+ url: "https://pub.dev"
source: hosted
- version: "0.1.2"
+ version: "0.1.6"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
- url: "https://pub.flutter-io.cn"
+ sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
+ url: "https://pub.dev"
source: hosted
- version: "2.1.0"
+ version: "2.1.2"
characters:
dependency: transitive
description:
name: characters
- url: "https://pub.flutter-io.cn"
+ sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
+ url: "https://pub.dev"
source: hosted
- version: "1.2.0"
- charcode:
- dependency: transitive
- description:
- name: charcode
- url: "https://pub.flutter-io.cn"
- source: hosted
- version: "1.3.1"
+ version: "1.4.0"
clock:
dependency: transitive
description:
name: clock
- url: "https://pub.flutter-io.cn"
+ sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
+ url: "https://pub.dev"
source: hosted
- version: "1.1.0"
+ version: "1.1.2"
collection:
dependency: transitive
description:
name: collection
- url: "https://pub.flutter-io.cn"
+ sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
+ url: "https://pub.dev"
source: hosted
- version: "1.16.0"
+ version: "1.19.1"
crypto:
dependency: transitive
description:
name: crypto
- url: "https://pub.flutter-io.cn"
+ sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
+ url: "https://pub.dev"
source: hosted
- version: "3.0.2"
+ version: "3.0.7"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
- url: "https://pub.flutter-io.cn"
+ sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
+ url: "https://pub.dev"
source: hosted
- version: "1.0.4"
+ version: "1.0.8"
+ dart_xdg_status_notifier_item:
+ dependency: transitive
+ description:
+ name: dart_xdg_status_notifier_item
+ sha256: "797fbf18f9abeba81e20d93fc3f0d28a406f39516a43620930844f47b530c1ad"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
+ dbus:
+ dependency: transitive
+ description:
+ name: dbus
+ sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.7.12"
english_words:
dependency: "direct main"
description:
name: english_words
- url: "https://pub.flutter-io.cn"
+ sha256: "6a7ef6473a97bd8571b6b641d006a6e58a7c67e65fb6f3d6d1151cb46b0e983c"
+ url: "https://pub.dev"
source: hosted
version: "4.0.0"
fake_async:
dependency: transitive
description:
name: fake_async
- url: "https://pub.flutter-io.cn"
+ sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
+ url: "https://pub.dev"
source: hosted
- version: "1.3.0"
+ version: "1.3.3"
ffi:
dependency: transitive
description:
name: ffi
- url: "https://pub.flutter-io.cn"
+ sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
+ url: "https://pub.dev"
source: hosted
- version: "1.1.2"
+ version: "2.2.0"
flutter:
dependency: "direct main"
description: flutter
@@ -122,7 +154,8 @@ packages:
dependency: "direct dev"
description:
name: flutter_lints
- url: "https://pub.flutter-io.cn"
+ sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493
+ url: "https://pub.dev"
source: hosted
version: "1.0.4"
flutter_test:
@@ -130,130 +163,202 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ leak_tracker:
+ dependency: transitive
+ description:
+ name: leak_tracker
+ sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
+ url: "https://pub.dev"
+ source: hosted
+ version: "11.0.2"
+ leak_tracker_flutter_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_flutter_testing
+ sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.10"
+ leak_tracker_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_testing
+ sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.2"
lints:
dependency: transitive
description:
name: lints
- url: "https://pub.flutter-io.cn"
+ sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c
+ url: "https://pub.dev"
source: hosted
version: "1.0.1"
+ logging:
+ dependency: transitive
+ description:
+ name: logging
+ sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.0"
matcher:
dependency: transitive
description:
name: matcher
- url: "https://pub.flutter-io.cn"
+ sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
+ url: "https://pub.dev"
source: hosted
- version: "0.12.11"
+ version: "0.12.17"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
- url: "https://pub.flutter-io.cn"
+ sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
+ url: "https://pub.dev"
source: hosted
- version: "0.1.4"
+ version: "0.11.1"
meta:
dependency: transitive
description:
name: meta
- url: "https://pub.flutter-io.cn"
+ sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
+ url: "https://pub.dev"
source: hosted
- version: "1.7.0"
+ version: "1.17.0"
path:
dependency: transitive
description:
name: path
- url: "https://pub.flutter-io.cn"
+ sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
+ url: "https://pub.dev"
source: hosted
- version: "1.8.1"
+ version: "1.9.1"
+ petitparser:
+ dependency: transitive
+ description:
+ name: petitparser
+ sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.2"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
- url: "https://pub.flutter-io.cn"
+ sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
+ url: "https://pub.dev"
source: hosted
- version: "2.1.2"
+ version: "2.1.8"
sky_engine:
dependency: transitive
description: flutter
source: sdk
- version: "0.0.99"
+ version: "0.0.0"
source_span:
dependency: transitive
description:
name: source_span
- url: "https://pub.flutter-io.cn"
+ sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
+ url: "https://pub.dev"
source: hosted
- version: "1.8.2"
+ version: "1.10.2"
stack_trace:
dependency: transitive
description:
name: stack_trace
- url: "https://pub.flutter-io.cn"
+ sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
+ url: "https://pub.dev"
source: hosted
- version: "1.10.0"
+ version: "1.12.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
- url: "https://pub.flutter-io.cn"
+ sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
+ url: "https://pub.dev"
source: hosted
- version: "2.1.0"
+ version: "2.1.4"
string_scanner:
dependency: transitive
description:
name: string_scanner
- url: "https://pub.flutter-io.cn"
+ sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
+ url: "https://pub.dev"
source: hosted
- version: "1.1.0"
+ version: "1.4.1"
system_tray:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
- version: "2.0.1"
+ version: "2.0.3"
term_glyph:
dependency: transitive
description:
name: term_glyph
- url: "https://pub.flutter-io.cn"
+ sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
+ url: "https://pub.dev"
source: hosted
- version: "1.2.0"
+ version: "1.2.2"
test_api:
dependency: transitive
description:
name: test_api
- url: "https://pub.flutter-io.cn"
+ sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
+ url: "https://pub.dev"
source: hosted
- version: "0.4.9"
+ version: "0.7.7"
typed_data:
dependency: transitive
description:
name: typed_data
- url: "https://pub.flutter-io.cn"
+ sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
+ url: "https://pub.dev"
source: hosted
- version: "1.3.1"
+ version: "1.4.0"
uuid:
dependency: transitive
description:
name: uuid
- url: "https://pub.flutter-io.cn"
+ sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
+ url: "https://pub.dev"
source: hosted
- version: "3.0.6"
+ version: "3.0.7"
vector_math:
dependency: transitive
description:
name: vector_math
- url: "https://pub.flutter-io.cn"
+ sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
+ url: "https://pub.dev"
source: hosted
- version: "2.1.2"
+ version: "2.2.0"
+ vm_service:
+ dependency: transitive
+ description:
+ name: vm_service
+ sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
+ url: "https://pub.dev"
+ source: hosted
+ version: "15.0.2"
win32:
dependency: transitive
description:
name: win32
- url: "https://pub.flutter-io.cn"
+ sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.15.0"
+ xml:
+ dependency: transitive
+ description:
+ name: xml
+ sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
+ url: "https://pub.dev"
source: hosted
- version: "2.6.1"
+ version: "6.6.1"
sdks:
- dart: ">=2.17.0 <3.0.0"
- flutter: ">=1.22.0"
+ dart: ">=3.8.0 <4.0.0"
+ flutter: ">=3.18.0-18.0.pre.54"
diff --git a/lib/src/app_window.dart b/lib/src/app_window.dart
index 0a6a488..eb0ea9e 100644
--- a/lib/src/app_window.dart
+++ b/lib/src/app_window.dart
@@ -1,41 +1,46 @@
-import 'dart:async';
-
-import 'package:flutter/services.dart';
-
-const String _kChannelName = "flutter/system_tray/app_window";
-
-const String _kInitAppWindow = "InitAppWindow";
-const String _kShowAppWindow = "ShowAppWindow";
-const String _kHideAppWindow = "HideAppWindow";
-const String _kCloseAppWindow = "CloseAppWindow";
-
-/// Representation of native window
-class AppWindow {
- AppWindow() {
- _platformChannel.setMethodCallHandler(_callbackHandler);
- _init();
- }
-
- static const MethodChannel _platformChannel = MethodChannel(_kChannelName);
-
- /// Show native window
- Future show() async {
- await _platformChannel.invokeMethod(_kShowAppWindow);
- }
-
- /// Hide native window
- Future hide() async {
- await _platformChannel.invokeMethod(_kHideAppWindow);
- }
-
- /// Close native window
- Future close() async {
- await _platformChannel.invokeMethod(_kCloseAppWindow);
- }
-
- void _init() async {
- await _platformChannel.invokeMethod(_kInitAppWindow);
- }
-
- Future _callbackHandler(MethodCall methodCall) async {}
-}
+import 'dart:async';
+import 'dart:io';
+
+import 'package:flutter/services.dart';
+
+const String _kChannelName = "flutter/system_tray/app_window";
+
+const String _kInitAppWindow = "InitAppWindow";
+const String _kShowAppWindow = "ShowAppWindow";
+const String _kHideAppWindow = "HideAppWindow";
+const String _kCloseAppWindow = "CloseAppWindow";
+
+/// Representation of native window
+class AppWindow {
+ AppWindow() {
+ _platformChannel.setMethodCallHandler(_callbackHandler);
+ _init();
+ }
+
+ static const MethodChannel _platformChannel = MethodChannel(_kChannelName);
+
+ /// Show native window
+ Future show() async {
+ if (Platform.isLinux) return;
+ await _platformChannel.invokeMethod(_kShowAppWindow);
+ }
+
+ /// Hide native window
+ Future hide() async {
+ if (Platform.isLinux) return;
+ await _platformChannel.invokeMethod(_kHideAppWindow);
+ }
+
+ /// Close native window
+ Future close() async {
+ if (Platform.isLinux) return;
+ await _platformChannel.invokeMethod(_kCloseAppWindow);
+ }
+
+ void _init() async {
+ if (Platform.isLinux) return;
+ await _platformChannel.invokeMethod(_kInitAppWindow);
+ }
+
+ Future _callbackHandler(MethodCall methodCall) async {}
+}
diff --git a/lib/src/constants.dart b/lib/src/constants.dart
index d2d6066..225d8bd 100644
--- a/lib/src/constants.dart
+++ b/lib/src/constants.dart
@@ -2,3 +2,4 @@
const String kSystemTrayEventClick = "click";
const String kSystemTrayEventRightClick = "right-click";
const String kSystemTrayEventDoubleClick = "double-click";
+const String kSystemTrayEventScroll = "scroll";
diff --git a/lib/src/menu.dart b/lib/src/menu.dart
index 93ce2bc..adcdf03 100644
--- a/lib/src/menu.dart
+++ b/lib/src/menu.dart
@@ -1,148 +1,181 @@
-import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-
-import 'menu_item.dart';
-import 'utils.dart';
-
-const String _kChannelName = "flutter/system_tray/menu_manager";
-
-const String _kCreateContextMenu = "CreateContextMenu";
-
-const String _kMenuIdKey = 'menu_id';
-const String _kMenuItemIdKey = 'menu_item_id';
-const String _kMenuListKey = 'menu_list';
-
-const String _kMenuItemSelectedCallbackMethod = 'MenuItemSelectedCallback';
-
-class Menu {
- static const MethodChannel _platformChannel = MethodChannel(_kChannelName);
-
- static final Map _menuMap = {};
-
- /// The ID to use the next time a menu needs an ID assigned.
- static int _nextMenuId = 1;
-
- List? _menus;
-
- int _menuId = 1;
-
- int _menuItemId = 1;
-
- bool _updateInProgress = false;
-
- Menu() {
- _platformChannel.setMethodCallHandler(_callbackHandler);
- }
-
- int get menuId => _menuId;
-
- int get nextMenuItemId {
- return _menuItemId++;
- }
-
- Future buildFrom(List menus) async {
- _menuId = _nextMenuId++;
- _menus = menus;
- _menuMap.putIfAbsent(_menuId, () => this);
- return await _createContextMenu(_menus!);
- }
-
- T? findItemByName(final String name) {
- return _findItemByName(name, _menus!) as T;
- }
-
- MenuItemBase? _findItemByName(
- final String name, final List menus) {
- MenuItemBase? item;
- for (final menuItem in menus) {
- if (menuItem is SubMenu) {
- item = _findItemByName(name, menuItem.children);
- } else if (menuItem.name == name) {
- item = menuItem;
- }
-
- if (item != null) {
- break;
- }
- }
- return item;
- }
-
- MenuItemBase? _findItemById(
- final int? menuItemId, final List? menus) {
- MenuItemBase? item;
- if (menuItemId != null && menus != null) {
- for (final menuItem in menus) {
- if (menuItem is SubMenu) {
- item = _findItemById(menuItemId, menuItem.children);
- } else if (menuItem.menuItemId == menuItemId) {
- item = menuItem;
- }
-
- if (item != null) {
- break;
- }
- }
- }
- return item;
- }
-
- Future _createContextMenu(List menus) async {
- bool result = false;
- try {
- _updateInProgress = true;
-
- await _channelRepresentationForMenus(menus);
-
- result = await _platformChannel
- .invokeMethod(_kCreateContextMenu, {
- _kMenuIdKey: _menuId,
- _kMenuListKey: menus.map((e) => e.toJson()).toList(),
- });
- _updateInProgress = false;
- } on PlatformException catch (e) {
- debugPrint('Platform exception create context menu: ${e.message}');
- }
- return result;
- }
-
- Future _channelRepresentationForMenus(List menus) async {
- _menuItemId = 1;
- await _channelRepresentationForMenu(menus);
- }
-
- Future _channelRepresentationForMenu(List menus) async {
- for (final menuItem in menus) {
- menuItem.channel = _platformChannel;
- menuItem.menuId = menuId;
- menuItem.menuItemId = nextMenuItemId;
- menuItem.imageAbsolutePath = await Utils.getIcon(menuItem.image);
-
- if (menuItem is SubMenu) {
- await _channelRepresentationForMenu(menuItem.children);
- }
- }
- }
-
- Future _callbackHandler(MethodCall methodCall) async {
- if (methodCall.method == _kMenuItemSelectedCallbackMethod) {
- if (_updateInProgress) {
- debugPrint(
- 'Warning: Menu selection callback received during menu update.');
- return;
- }
-
- final int? menuId = methodCall.arguments[_kMenuIdKey];
- final int? menuItemId = methodCall.arguments[_kMenuItemIdKey];
- final MenuItemBase? menuItem =
- _findItemById(menuItemId, _menuMap[menuId]?._menus);
-
- debugPrint('MenuItemBase select menuId:$menuId menuItemId:$menuItemId');
-
- final callback = menuItem?.onClicked;
- if (callback != null) {
- callback(menuItem!);
- }
- }
- }
-}
+import 'dart:io';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+
+import 'menu_item.dart';
+import 'system_tray_linux.dart';
+import 'utils.dart';
+
+const String _kChannelName = "flutter/system_tray/menu_manager";
+
+const String _kCreateContextMenu = "CreateContextMenu";
+
+const String _kMenuIdKey = 'menu_id';
+const String _kMenuItemIdKey = 'menu_item_id';
+const String _kMenuListKey = 'menu_list';
+
+const String _kMenuItemSelectedCallbackMethod = 'MenuItemSelectedCallback';
+
+class Menu {
+ static const MethodChannel _platformChannel = MethodChannel(_kChannelName);
+
+ static final Map _menuMap = {};
+
+ /// The ID to use the next time a menu needs an ID assigned.
+ static int _nextMenuId = 1;
+
+ List? _menus;
+
+ int _menuId = 1;
+
+ int _menuItemId = 1;
+
+ bool _updateInProgress = false;
+
+ Menu() {
+ if (Platform.isLinux) {
+ SystemTrayLinux.menuItemSelectedCallback = (menuId, menuItemId) {
+ _callbackHandler(MethodCall(_kMenuItemSelectedCallbackMethod, {
+ _kMenuIdKey: menuId,
+ _kMenuItemIdKey: menuItemId,
+ }));
+ };
+ } else {
+ _platformChannel.setMethodCallHandler(_callbackHandler);
+ }
+ }
+
+ int get menuId => _menuId;
+
+ int get nextMenuItemId {
+ return _menuItemId++;
+ }
+
+ Future buildFrom(List menus) async {
+ _menuId = _nextMenuId++;
+ _menus = menus;
+ _menuMap.putIfAbsent(_menuId, () => this);
+ if (Platform.isLinux) {
+ _channelRepresentationForMenusSync(menus);
+ return await SystemTrayLinux.buildMenu(_menuId, menus);
+ }
+ return await _createContextMenu(_menus!);
+ }
+
+ T? findItemByName(final String name) {
+ return _findItemByName(name, _menus!) as T;
+ }
+
+ MenuItemBase? _findItemByName(
+ final String name, final List menus) {
+ MenuItemBase? item;
+ for (final menuItem in menus) {
+ if (menuItem is SubMenu) {
+ item = _findItemByName(name, menuItem.children);
+ } else if (menuItem.name == name) {
+ item = menuItem;
+ }
+
+ if (item != null) {
+ break;
+ }
+ }
+ return item;
+ }
+
+ MenuItemBase? _findItemById(
+ final int? menuItemId, final List? menus) {
+ MenuItemBase? item;
+ if (menuItemId != null && menus != null) {
+ for (final menuItem in menus) {
+ if (menuItem is SubMenu) {
+ item = _findItemById(menuItemId, menuItem.children);
+ } else if (menuItem.menuItemId == menuItemId) {
+ item = menuItem;
+ }
+
+ if (item != null) {
+ break;
+ }
+ }
+ }
+ return item;
+ }
+
+ Future _createContextMenu(List menus) async {
+ bool result = false;
+ try {
+ _updateInProgress = true;
+
+ await _channelRepresentationForMenus(menus);
+
+ result = await _platformChannel
+ .invokeMethod(_kCreateContextMenu, {
+ _kMenuIdKey: _menuId,
+ _kMenuListKey: menus.map((e) => e.toJson()).toList(),
+ });
+ _updateInProgress = false;
+ } on PlatformException catch (e) {
+ debugPrint('Platform exception create context menu: ${e.message}');
+ }
+ return result;
+ }
+
+ Future _channelRepresentationForMenus(List menus) async {
+ _menuItemId = 1;
+ await _channelRepresentationForMenu(menus);
+ }
+
+ void _channelRepresentationForMenusSync(List menus) {
+ _menuItemId = 1;
+ _channelRepresentationForMenuSync(menus);
+ }
+
+ Future _channelRepresentationForMenu(List menus) async {
+ for (final menuItem in menus) {
+ menuItem.channel = _platformChannel;
+ menuItem.menuId = menuId;
+ menuItem.menuItemId = nextMenuItemId;
+ menuItem.imageAbsolutePath = await Utils.getIcon(menuItem.image);
+
+ if (menuItem is SubMenu) {
+ await _channelRepresentationForMenu(menuItem.children);
+ }
+ }
+ }
+
+ void _channelRepresentationForMenuSync(List menus) {
+ for (final menuItem in menus) {
+ menuItem.channel = _platformChannel;
+ menuItem.menuId = menuId;
+ menuItem.menuItemId = nextMenuItemId;
+
+ if (menuItem is SubMenu) {
+ _channelRepresentationForMenuSync(menuItem.children);
+ }
+ }
+ }
+
+ Future _callbackHandler(MethodCall methodCall) async {
+ if (methodCall.method == _kMenuItemSelectedCallbackMethod) {
+ if (_updateInProgress) {
+ debugPrint(
+ 'Warning: Menu selection callback received during menu update.');
+ return;
+ }
+
+ final int? menuId = methodCall.arguments[_kMenuIdKey];
+ final int? menuItemId = methodCall.arguments[_kMenuItemIdKey];
+ final MenuItemBase? menuItem =
+ _findItemById(menuItemId, _menuMap[menuId]?._menus);
+
+ debugPrint('MenuItemBase select menuId:$menuId menuItemId:$menuItemId');
+
+ final callback = menuItem?.onClicked;
+ if (callback != null) {
+ callback(menuItem!);
+ }
+ }
+ }
+}
diff --git a/lib/src/menu_item.dart b/lib/src/menu_item.dart
index 5a3a88e..7045323 100644
--- a/lib/src/menu_item.dart
+++ b/lib/src/menu_item.dart
@@ -1,6 +1,10 @@
+import 'dart:io';
+
import 'package:flutter/services.dart';
import 'package:system_tray/src/utils.dart';
+import 'system_tray_linux.dart';
+
const String _kSetLabel = "SetLabel";
const String _kSetImage = "SetImage";
const String _kSetEnable = "SetEnable";
@@ -40,6 +44,12 @@ abstract class MenuItemBase {
}
Future setLabel(String label) async {
+ if (Platform.isLinux) {
+ await SystemTrayLinux.setMenuItemLabel(
+ menuId ?? -1, menuItemId ?? -1, label);
+ this.label = label;
+ return;
+ }
bool result = await channel?.invokeMethod(_kSetLabel, {
_kMenuIdKey: menuId ?? -1,
_kMenuItemIdKey: menuItemId ?? -1,
@@ -53,6 +63,14 @@ abstract class MenuItemBase {
Future setImage(String image) async {
String? imageAbsolutePath = await Utils.getIcon(image);
+ if (Platform.isLinux) {
+ await SystemTrayLinux.setMenuItemImage(
+ menuId ?? -1, menuItemId ?? -1, imageAbsolutePath ?? '');
+ this.image = image;
+ this.imageAbsolutePath = imageAbsolutePath;
+ return;
+ }
+
bool result = await channel?.invokeMethod(_kSetImage, {
_kMenuIdKey: menuId ?? -1,
_kMenuItemIdKey: menuItemId ?? -1,
@@ -65,6 +83,12 @@ abstract class MenuItemBase {
}
Future setEnable(bool enabled) async {
+ if (Platform.isLinux) {
+ await SystemTrayLinux.setMenuItemEnable(
+ menuId ?? -1, menuItemId ?? -1, enabled);
+ this.enabled = enabled;
+ return;
+ }
bool result = await channel?.invokeMethod(_kSetEnable, {
_kMenuIdKey: menuId ?? -1,
_kMenuItemIdKey: menuItemId ?? -1,
@@ -80,6 +104,13 @@ abstract class MenuItemBase {
return;
}
+ if (Platform.isLinux) {
+ await SystemTrayLinux.setMenuItemCheck(
+ menuId ?? -1, menuItemId ?? -1, checked);
+ this.checked = checked;
+ return;
+ }
+
bool result = await channel?.invokeMethod(_kSetCheck, {
_kMenuIdKey: menuId ?? -1,
_kMenuItemIdKey: menuItemId ?? -1,
diff --git a/lib/src/system_tray_linux.dart b/lib/src/system_tray_linux.dart
new file mode 100644
index 0000000..acdf686
--- /dev/null
+++ b/lib/src/system_tray_linux.dart
@@ -0,0 +1,149 @@
+import 'dart:async';
+
+import 'package:dart_xdg_status_notifier_item/dart_xdg_status_notifier_item.dart';
+
+import 'constants.dart';
+import 'menu.dart';
+import 'menu_item.dart';
+
+class SystemTrayLinux {
+ static final Map _menuMap = {};
+ static StatusNotifierItemClient? _client;
+
+ static void Function(String eventName)? systemTrayEventCallback;
+ static void Function(int menuId, int menuItemId)? menuItemSelectedCallback;
+
+ static Future initSystemTray({
+ required String trayId,
+ required String iconPath,
+ String? title,
+ String? toolTip,
+ bool isTemplate = false,
+ }) async {
+ _client = StatusNotifierItemClient(
+ id: trayId,
+ iconName: iconPath,
+ title: title ?? '',
+ menu: DBusMenuItem(children: []),
+ onContextMenu: (x, y) async {
+ if (systemTrayEventCallback != null) {
+ systemTrayEventCallback!(kSystemTrayEventRightClick);
+ }
+ },
+ onActivate: (x, y) async {
+ if (systemTrayEventCallback != null) {
+ systemTrayEventCallback!(kSystemTrayEventClick);
+ }
+ },
+ onSecondaryActivate: (x, y) async {
+ if (systemTrayEventCallback != null) {
+ systemTrayEventCallback!(kSystemTrayEventDoubleClick);
+ }
+ },
+ onScroll: (delta, orientation) async {
+ if (systemTrayEventCallback != null) {
+ systemTrayEventCallback!(kSystemTrayEventScroll);
+ }
+ });
+
+ if (toolTip != null && toolTip.isNotEmpty) {
+ _client!.toolTip = StatusNotifierToolTip(
+ iconName: 'icon-name', title: toolTip, body: '', iconPixmap: []);
+ }
+
+ await _client!.connect();
+ return true;
+ }
+
+ static Future setSystemTrayInfo({
+ String? title,
+ String? iconPath,
+ String? toolTip,
+ bool isTemplate = false,
+ }) async {
+ if (_client != null) {
+ if (iconPath != null) _client!.iconName = iconPath;
+ if (title != null) _client!.title = title;
+ if (toolTip != null) {
+ _client!.toolTip = StatusNotifierToolTip(
+ iconName: 'icon-name', title: toolTip, body: '', iconPixmap: []);
+ }
+ }
+ return true;
+ }
+
+ static Future setContextMenu(int menuId) async {
+ if (_menuMap.containsKey(menuId) && _client != null) {
+ await _client!.updateMenu(_menuMap[menuId]!);
+ }
+ }
+
+ static Future popUpContextMenu() async {
+ // Not typically supported directly via xdg_status_notifier_item without shell interaction
+ }
+
+ static Future getTitle() async {
+ return _client?.title ?? "";
+ }
+
+ static Future destroySystemTray() async {
+ if (_client != null) {
+ await _client!.close();
+ _client = null;
+ }
+ }
+
+ static DBusMenuItem _buildMenuItem(MenuItemBase item, int menuId) {
+ if (item is MenuSeparator) {
+ return DBusMenuItem.separator();
+ }
+
+ if (item is SubMenu) {
+ final children =
+ item.children.map((e) => _buildMenuItem(e, menuId)).toList();
+ return DBusMenuItem(
+ label: item.label,
+ enabled: item.enabled,
+ children: children,
+ );
+ }
+
+ if (item is MenuItemCheckbox) {
+ return DBusMenuItem.checkmark(item.label,
+ state: item.checked, enabled: item.enabled, onClicked: () async {
+ if (menuItemSelectedCallback != null) {
+ menuItemSelectedCallback!(menuId, item.menuItemId ?? -1);
+ }
+ });
+ }
+
+ return DBusMenuItem(
+ label: item.label,
+ enabled: item.enabled,
+ onClicked: () async {
+ if (menuItemSelectedCallback != null) {
+ menuItemSelectedCallback!(menuId, item.menuItemId ?? -1);
+ }
+ });
+ }
+
+ static Future buildMenu(int menuId, List menus) async {
+ final children = menus.map((e) => _buildMenuItem(e, menuId)).toList();
+ final menu = DBusMenuItem(children: children);
+ _menuMap[menuId] = menu;
+
+ return true;
+ }
+
+ static Future setMenuItemLabel(
+ int menuId, int menuItemId, String label) async {}
+
+ static Future setMenuItemImage(
+ int menuId, int menuItemId, String imageAbsolutePath) async {}
+
+ static Future setMenuItemEnable(
+ int menuId, int menuItemId, bool enabled) async {}
+
+ static Future setMenuItemCheck(
+ int menuId, int menuItemId, bool checked) async {}
+}
diff --git a/lib/src/tray.dart b/lib/src/tray.dart
index b1ed149..6fbc469 100644
--- a/lib/src/tray.dart
+++ b/lib/src/tray.dart
@@ -1,10 +1,12 @@
import 'dart:async';
+import 'dart:io';
import 'package:flutter/services.dart';
import 'package:uuid/uuid.dart';
import 'menu.dart';
import 'utils.dart';
+import 'system_tray_linux.dart';
const String _kChannelName = "flutter/system_tray/tray";
@@ -29,7 +31,11 @@ typedef SystemTrayEventCallback = void Function(String eventName);
/// Representation of system tray
class SystemTray {
SystemTray() {
- _platformChannel.setMethodCallHandler(_callbackHandler);
+ if (Platform.isLinux) {
+ SystemTrayLinux.systemTrayEventCallback = _callbackHandlerSync;
+ } else {
+ _platformChannel.setMethodCallHandler(_callbackHandler);
+ }
}
static const MethodChannel _platformChannel = MethodChannel(_kChannelName);
@@ -44,6 +50,15 @@ class SystemTray {
String? toolTip,
bool isTemplate = false,
}) async {
+ if (Platform.isLinux) {
+ return await SystemTrayLinux.initSystemTray(
+ trayId: const Uuid().v1(),
+ iconPath: await Utils.getIcon(iconPath) ?? '',
+ title: title,
+ toolTip: toolTip,
+ isTemplate: isTemplate,
+ );
+ }
bool value = await _platformChannel.invokeMethod(
_kInitSystemTray,
{
@@ -64,6 +79,14 @@ class SystemTray {
String? toolTip,
bool isTemplate = false,
}) async {
+ if (Platform.isLinux) {
+ return await SystemTrayLinux.setSystemTrayInfo(
+ title: title,
+ iconPath: await Utils.getIcon(iconPath),
+ toolTip: toolTip,
+ isTemplate: isTemplate,
+ );
+ }
bool value = await _platformChannel.invokeMethod(
_kSetSystemTrayInfo,
{
@@ -93,6 +116,9 @@ class SystemTray {
/// (macOS) Returns string - the title displayed next to the tray icon in the status bar
Future getTitle() async {
+ if (Platform.isLinux) {
+ return await SystemTrayLinux.getTitle();
+ }
return await _platformChannel.invokeMethod(_kGetTitle);
}
@@ -102,12 +128,20 @@ class SystemTray {
/// For instance, special menus that are handled entirely on the native
/// side might be added to the provided menus.
Future setContextMenu(Menu menu) async {
+ if (Platform.isLinux) {
+ await SystemTrayLinux.setContextMenu(menu.menuId);
+ return;
+ }
await _platformChannel.invokeMethod(_kSetContextMenu, menu.menuId);
}
/// Pop up the context menu.
///
Future popUpContextMenu() async {
+ if (Platform.isLinux) {
+ await SystemTrayLinux.popUpContextMenu();
+ return;
+ }
await _platformChannel.invokeMethod(_kPopupContextMenu);
}
@@ -125,7 +159,17 @@ class SystemTray {
}
}
+ void _callbackHandlerSync(String eventName) {
+ if (_systemTrayEventCallback != null) {
+ _systemTrayEventCallback!(eventName);
+ }
+ }
+
Future destroy() async {
+ if (Platform.isLinux) {
+ await SystemTrayLinux.destroySystemTray();
+ return;
+ }
await _platformChannel.invokeMethod(_kDestroySystemTray);
}
}
diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt
deleted file mode 100644
index 73124c8..0000000
--- a/linux/CMakeLists.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-cmake_minimum_required(VERSION 3.10)
-set(PROJECT_NAME "system_tray")
-project(${PROJECT_NAME} LANGUAGES CXX)
-
-# This value is used when generating builds using this plugin, so it must
-# not be changed
-set(PLUGIN_NAME "${PROJECT_NAME}_plugin")
-
-find_package(PkgConfig REQUIRED)
-
-add_library(${PLUGIN_NAME} SHARED
- "system_tray_plugin.cc"
- "app_window.cc"
- "menu_manager.cc"
- "menu.cc"
- "tray.cc"
- "errors.cc"
-)
-
-pkg_check_modules(APPINDICATOR IMPORTED_TARGET ayatana-appindicator3-0.1)
-if(APPINDICATOR_FOUND)
- target_compile_definitions(${PLUGIN_NAME} PRIVATE HAVE_AYATANA)
-else()
- pkg_check_modules(APPINDICATOR IMPORTED_TARGET appindicator3-0.1)
-endif()
-if(APPINDICATOR_FOUND)
- target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::APPINDICATOR)
-else()
- message(
- FATAL_ERROR
- "\n"
- "The `system_tray` package requires ayatana-appindicator3-0.1 or appindicator3-0.1."
- )
-endif()
-
-
-apply_standard_settings(${PLUGIN_NAME})
-set_target_properties(${PLUGIN_NAME} PROPERTIES
- CXX_VISIBILITY_PRESET hidden)
-target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
-target_include_directories(${PLUGIN_NAME} INTERFACE
- "${CMAKE_CURRENT_SOURCE_DIR}/include")
-target_link_libraries(${PLUGIN_NAME} PRIVATE flutter)
-target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK)
-target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::APPINDICATOR)
-
-# List of absolute paths to libraries that should be bundled with the plugin
-set(system_tray_bundled_libraries
- ""
- PARENT_SCOPE
-)
diff --git a/linux/app_window.cc b/linux/app_window.cc
deleted file mode 100644
index 2a45573..0000000
--- a/linux/app_window.cc
+++ /dev/null
@@ -1,205 +0,0 @@
-#include "app_window.h"
-
-#include
-#include "errors.h"
-
-constexpr char kInitAppWindow[] = "InitAppWindow";
-constexpr char kShowAppWindow[] = "ShowAppWindow";
-constexpr char kHideAppWindow[] = "HideAppWindow";
-constexpr char kCloseAppWindow[] = "CloseAppWindow";
-
-// static
-gboolean AppWindow::static_window_state_event_callback_fun(
- GtkWidget* widget,
- GdkEventWindowState* event,
- AppWindow* self) {
- return self->window_state_event_callback_fun(widget, event);
-}
-
-gboolean AppWindow::window_state_event_callback_fun(
- GtkWidget* widget,
- GdkEventWindowState* event) {
- if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) {
- window_iconify_ = true;
- } else {
- window_iconify_ = false;
- }
- return TRUE;
-}
-
-AppWindow::AppWindow(FlPluginRegistrar* registrar,
- FlMethodChannel* channel) noexcept
- : registrar_(registrar), channel_(channel) {}
-
-AppWindow::~AppWindow() noexcept {
- channel_ = nullptr;
-}
-
-void AppWindow::handle_method_call(FlMethodCall* method_call) {
- g_autoptr(FlMethodResponse) response = nullptr;
-
- const gchar* method = fl_method_call_get_name(method_call);
- FlValue* args = fl_method_call_get_args(method_call);
-
- // g_print("method call %s\n", method);
-
- if (strcmp(method, kInitAppWindow) == 0) {
- response = init_app_window(args);
- } else if (strcmp(method, kShowAppWindow) == 0) {
- response = show_app_window(args);
- } else if (strcmp(method, kHideAppWindow) == 0) {
- response = hide_app_window(args);
- } else if (strcmp(method, kCloseAppWindow) == 0) {
- response = close_app_window(args);
- } else {
- response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
- }
-
- g_autoptr(GError) error = nullptr;
- if (!fl_method_call_respond(method_call, response, &error)) {
- g_warning("Failed to send method call response: %s", error->message);
- }
-}
-
-FlMethodResponse* AppWindow::init_app_window(FlValue* args) {
- g_autoptr(FlValue) result = fl_value_new_bool(FALSE);
- FlMethodResponse* response = nullptr;
-
- do {
- FlView* view = fl_plugin_registrar_get_view(registrar_);
- if (view == nullptr) {
- response = FL_METHOD_RESPONSE(fl_method_error_response_new(
- errors::kBadArgumentsError, "", fl_value_new_bool(FALSE)));
- break;
- }
-
- GtkWindow* window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view)));
- if (window == nullptr) {
- response = FL_METHOD_RESPONSE(fl_method_error_response_new(
- errors::kBadArgumentsError, "", fl_value_new_bool(FALSE)));
- break;
- }
-
- if (!init_app_window(window)) {
- response = FL_METHOD_RESPONSE(fl_method_error_response_new(
- errors::kBadArgumentsError, "", fl_value_new_bool(FALSE)));
- break;
- }
-
- result = fl_value_new_bool(TRUE);
-
- } while (false);
-
- if (nullptr == response) {
- response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
- }
- return response;
-}
-
-FlMethodResponse* AppWindow::show_app_window(FlValue* args) {
- g_autoptr(FlValue) result = fl_value_new_bool(FALSE);
- FlMethodResponse* response = nullptr;
-
- do {
- if (!show_app_window()) {
- break;
- }
-
- result = fl_value_new_bool(TRUE);
-
- } while (false);
-
- if (nullptr == response) {
- response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
- }
- return response;
-}
-
-FlMethodResponse* AppWindow::hide_app_window(FlValue* args) {
- g_autoptr(FlValue) result = fl_value_new_bool(FALSE);
- FlMethodResponse* response = nullptr;
-
- do {
- if (!hide_app_window()) {
- break;
- }
-
- result = fl_value_new_bool(TRUE);
-
- } while (false);
-
- if (nullptr == response) {
- response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
- }
- return response;
-}
-
-FlMethodResponse* AppWindow::close_app_window(FlValue* args) {
- g_autoptr(FlValue) result = fl_value_new_bool(FALSE);
- FlMethodResponse* response = nullptr;
-
- do {
- if (!close_app_window()) {
- break;
- }
-
- result = fl_value_new_bool(TRUE);
-
- } while (false);
-
- if (nullptr == response) {
- response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
- }
- return response;
-}
-
-bool AppWindow::init_app_window(GtkWindow* window) {
- window_ = window;
- g_signal_connect(
- G_OBJECT(window_), "window-state-event",
- G_CALLBACK(AppWindow::static_window_state_event_callback_fun), this);
- return true;
-}
-
-bool AppWindow::show_app_window() {
- if (!window_) {
- return false;
- }
-
- if (x_ != -1 && y_ != -1) {
- gtk_window_move(window_, x_, y_);
- x_ = -1;
- y_ = -1;
- }
-
- gtk_widget_show(GTK_WIDGET(window_));
- gtk_window_present(window_);
-
- if (window_iconify_) {
- gtk_window_deiconify(window_);
- }
-
- // GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
- // GdkDisplay* display = gdk_window_get_display(gdk_window);
- // gdk_display_flush(display);
- return true;
-}
-
-bool AppWindow::hide_app_window() {
- if (!window_) {
- return false;
- }
-
- gtk_window_get_position(window_, &x_, &y_);
- gtk_widget_hide(GTK_WIDGET(window_));
- return true;
-}
-
-bool AppWindow::close_app_window() {
- if (!window_) {
- return false;
- }
-
- gtk_window_close(window_);
- return true;
-}
\ No newline at end of file
diff --git a/linux/app_window.h b/linux/app_window.h
deleted file mode 100644
index 739cb9b..0000000
--- a/linux/app_window.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#ifndef __APPWINDOW_H__
-#define __APPWINDOW_H__
-
-#include
-#include
-
-extern const char kInitAppWindow[];
-extern const char kShowAppWindow[];
-extern const char kHideAppWindow[];
-extern const char kCloseAppWindow[];
-
-class AppWindow {
- public:
- AppWindow(FlPluginRegistrar* registrar, FlMethodChannel* channel) noexcept;
- ~AppWindow() noexcept;
-
- void handle_method_call(FlMethodCall* method_call);
-
- protected:
- FlMethodResponse* init_app_window(FlValue* args);
- FlMethodResponse* show_app_window(FlValue* args);
- FlMethodResponse* hide_app_window(FlValue* args);
- FlMethodResponse* close_app_window(FlValue* args);
-
- bool init_app_window(GtkWindow* window);
- bool show_app_window();
- bool hide_app_window();
- bool close_app_window();
-
- static gboolean static_window_state_event_callback_fun(
- GtkWidget* widget,
- GdkEventWindowState* event,
- AppWindow* self);
-
- gboolean window_state_event_callback_fun(GtkWidget* widget,
- GdkEventWindowState* event);
-
- protected:
- FlPluginRegistrar* registrar_ = nullptr;
- FlMethodChannel* channel_ = nullptr;
-
- GtkWindow* window_ = nullptr;
- bool window_iconify_ = false;
- gint x_ = -1;
- gint y_ = -1;
-};
-
-#endif // __APPWINDOW_H__
diff --git a/linux/errors.cc b/linux/errors.cc
deleted file mode 100644
index 9a7de3e..0000000
--- a/linux/errors.cc
+++ /dev/null
@@ -1,10 +0,0 @@
-#include "errors.h"
-
-namespace errors {
-
-const char kBadArgumentsError[] = "Bad Arguments";
-const char kOutOfMemoryError[] = "Out of memory";
-const char kFailedError[] = "Failed";
-const char kNotFoundError[] = "Not Found";
-
-} // namespace errors
\ No newline at end of file
diff --git a/linux/errors.h b/linux/errors.h
deleted file mode 100644
index b4661a4..0000000
--- a/linux/errors.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef __ERRORS_H__
-#define __ERRORS_H__
-
-namespace errors {
-
-extern const char kBadArgumentsError[];
-extern const char kOutOfMemoryError[];
-extern const char kFailedError[];
-extern const char kNotFoundError[];
-
-} // namespace errors
-
-#endif // __ERRORS_H__
diff --git a/linux/include/system_tray/system_tray_plugin.h b/linux/include/system_tray/system_tray_plugin.h
deleted file mode 100644
index 0c1c11f..0000000
--- a/linux/include/system_tray/system_tray_plugin.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef FLUTTER_PLUGIN_SYSTEM_TRAY_PLUGIN_H_
-#define FLUTTER_PLUGIN_SYSTEM_TRAY_PLUGIN_H_
-
-#include
-
-G_BEGIN_DECLS
-
-#ifdef FLUTTER_PLUGIN_IMPL
-#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default")))
-#else
-#define FLUTTER_PLUGIN_EXPORT
-#endif
-
-typedef struct _SystemTrayPlugin SystemTrayPlugin;
-typedef struct {
- GObjectClass parent_class;
-} SystemTrayPluginClass;
-
-FLUTTER_PLUGIN_EXPORT GType system_tray_plugin_get_type();
-
-FLUTTER_PLUGIN_EXPORT void system_tray_plugin_register_with_registrar(
- FlPluginRegistrar* registrar);
-
-G_END_DECLS
-
-#endif // FLUTTER_PLUGIN_SYSTEM_TRAY_PLUGIN_H_
diff --git a/linux/menu.cc b/linux/menu.cc
deleted file mode 100644
index 5fd53c4..0000000
--- a/linux/menu.cc
+++ /dev/null
@@ -1,379 +0,0 @@
-#include "menu.h"
-
-#include
-
-#include "errors.h"
-
-namespace {
-
-constexpr char kMenuIdKey[] = "menu_id";
-constexpr char kMenuItemIdKey[] = "menu_item_id";
-constexpr char kMenuListKey[] = "menu_list";
-constexpr char kIdKey[] = "id";
-constexpr char kTypeKey[] = "type";
-constexpr char kSeparatorKey[] = "separator";
-constexpr char kSubMenuKey[] = "submenu";
-constexpr char kCheckboxKey[] = "checkbox";
-constexpr char kLabelKey[] = "label";
-constexpr char kImageKey[] = "image";
-constexpr char kEnabledKey[] = "enabled";
-constexpr char kCheckedKey[] = "checked";
-
-constexpr char kMenuItemSelectedCallbackMethod[] = "MenuItemSelectedCallback";
-
-struct TrayCallbackData {
- Menu* menu;
- int64_t menu_id;
- int64_t menu_item_id;
-};
-
-} // namespace
-
-Menu::Menu(FlMethodChannel* channel, int menu_id) noexcept
- : channel_(channel), menu_id_(menu_id) {}
-
-Menu::~Menu() noexcept {
- // printf("~Menu this: %p\n", this);
-}
-
-bool Menu::create_context_menu(FlValue* args) {
- bool result = false;
-
- do {
- if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
- break;
- }
-
- FlValue* list_value = fl_value_lookup_string(args, kMenuListKey);
- if (!list_value || fl_value_get_type(list_value) != FL_VALUE_TYPE_LIST) {
- break;
- }
-
- GtkWidget* gtk_menu = value_to_menu(menu_id(), list_value);
- if (!gtk_menu) {
- break;
- }
-
- gtk_menu_ = GTK_WIDGET(g_object_ref(gtk_menu));
-
- result = true;
-
- } while (false);
-
- return result;
-}
-
-FlMethodResponse* Menu::set_label(FlValue* args) {
- g_autoptr(FlValue) result = fl_value_new_bool(FALSE);
- FlMethodResponse* response = nullptr;
-
- do {
- if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
- response = FL_METHOD_RESPONSE(fl_method_error_response_new(
- errors::kBadArgumentsError, "", nullptr));
- break;
- }
-
- FlValue* menu_item_id_value = fl_value_lookup_string(args, kMenuItemIdKey);
- if (!menu_item_id_value ||
- fl_value_get_type(menu_item_id_value) != FL_VALUE_TYPE_INT) {
- response = FL_METHOD_RESPONSE(fl_method_error_response_new(
- errors::kBadArgumentsError, "", nullptr));
- break;
- }
-
- int64_t menu_item_id = fl_value_get_int(menu_item_id_value);
-
- const gchar* label = nullptr;
- FlValue* label_value = fl_value_lookup_string(args, kLabelKey);
- if (label_value && fl_value_get_type(label_value) == FL_VALUE_TYPE_STRING) {
- label = fl_value_get_string(label_value);
- }
-
- set_label(menu_item_id, label);
-
- result = fl_value_new_bool(TRUE);
-
- } while (false);
-
- if (nullptr == response) {
- response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
- }
-
- return response;
-}
-
-FlMethodResponse* Menu::set_image(FlValue* args) {
- g_autoptr(FlValue) result = fl_value_new_bool(FALSE);
- FlMethodResponse* response = nullptr;
-
- do {
- if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
- response = FL_METHOD_RESPONSE(fl_method_error_response_new(
- errors::kBadArgumentsError, "", nullptr));
- break;
- }
-
- FlValue* menu_item_id_value = fl_value_lookup_string(args, kMenuItemIdKey);
- if (!menu_item_id_value ||
- fl_value_get_type(menu_item_id_value) != FL_VALUE_TYPE_INT) {
- response = FL_METHOD_RESPONSE(fl_method_error_response_new(
- errors::kBadArgumentsError, "", nullptr));
- break;
- }
-
- int64_t menu_item_id = fl_value_get_int(menu_item_id_value);
-
- const gchar* image = nullptr;
- FlValue* image_value = fl_value_lookup_string(args, kImageKey);
- if (image_value && fl_value_get_type(image_value) == FL_VALUE_TYPE_STRING) {
- image = fl_value_get_string(image_value);
- }
-
- set_image(menu_item_id, image);
-
- result = fl_value_new_bool(TRUE);
-
- } while (false);
-
- if (nullptr == response) {
- response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
- }
-
- return response;
-}
-
-FlMethodResponse* Menu::set_enable(FlValue* args) {
- g_autoptr(FlValue) result = fl_value_new_bool(FALSE);
- FlMethodResponse* response = nullptr;
-
- do {
- if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
- response = FL_METHOD_RESPONSE(fl_method_error_response_new(
- errors::kBadArgumentsError, "", nullptr));
- break;
- }
-
- FlValue* menu_item_id_value = fl_value_lookup_string(args, kMenuItemIdKey);
- if (!menu_item_id_value ||
- fl_value_get_type(menu_item_id_value) != FL_VALUE_TYPE_INT) {
- response = FL_METHOD_RESPONSE(fl_method_error_response_new(
- errors::kBadArgumentsError, "", nullptr));
- break;
- }
-
- int64_t menu_item_id = fl_value_get_int(menu_item_id_value);
-
- bool enable = true;
- FlValue* enable_value = fl_value_lookup_string(args, kEnabledKey);
- if (enable_value &&
- fl_value_get_type(enable_value) == FL_VALUE_TYPE_STRING) {
- enable = fl_value_get_bool(enable_value);
- }
-
- set_enable(menu_item_id, enable);
-
- result = fl_value_new_bool(TRUE);
-
- } while (false);
-
- if (nullptr == response) {
- response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
- }
-
- return response;
-}
-
-FlMethodResponse* Menu::set_check(FlValue* args) {
- g_autoptr(FlValue) result = fl_value_new_bool(FALSE);
- FlMethodResponse* response = nullptr;
-
- do {
- if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
- response = FL_METHOD_RESPONSE(fl_method_error_response_new(
- errors::kBadArgumentsError, "not map", nullptr));
- break;
- }
-
- FlValue* menu_item_id_value = fl_value_lookup_string(args, kMenuItemIdKey);
- if (!menu_item_id_value ||
- fl_value_get_type(menu_item_id_value) != FL_VALUE_TYPE_INT) {
- break;
- }
-
- int64_t menu_item_id = fl_value_get_int(menu_item_id_value);
-
- bool checked = true;
- FlValue* checked_value = fl_value_lookup_string(args, kCheckedKey);
- if (checked_value &&
- fl_value_get_type(checked_value) == FL_VALUE_TYPE_STRING) {
- checked = fl_value_get_bool(checked_value);
- }
-
- set_check(menu_item_id, checked);
-
- result = fl_value_new_bool(TRUE);
-
- } while (false);
-
- if (nullptr == response) {
- response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
- }
-
- return response;
-}
-
-void Menu::set_label(int64_t menu_item_id, const char* label) {}
-void Menu::set_image(int64_t menu_item_id, const char* image) {}
-void Menu::set_enable(int64_t menu_item_id, bool enabled) {}
-void Menu::set_check(int64_t menu_item_id, bool checked) {}
-
-int64_t Menu::menu_id() const {
- return menu_id_;
-}
-
-GtkWidget* Menu::get_menu() const {
- return gtk_menu_;
-}
-
-// static
-void Menu::menu_item_callback(GtkMenuItem* item, gpointer user_data) {
- TrayCallbackData* callback_data =
- reinterpret_cast(user_data);
- if (callback_data && callback_data->menu) {
- callback_data->menu->handle_menu_item_callback(item, callback_data);
- }
-}
-
-void Menu::handle_menu_item_callback(GtkMenuItem* item, gpointer user_data) {
- TrayCallbackData* callback_data =
- reinterpret_cast(user_data);
-
- // g_print("handle_menu_item_callback menu_id:%ld, menu_item_id:%ld\n",
- // callback_data->menu_id, callback_data->menu_item_id);
-
- g_autoptr(FlValue) result = fl_value_new_map();
- fl_value_set_string_take(result, kMenuIdKey,
- fl_value_new_int(callback_data->menu_id));
- fl_value_set_string_take(result, kMenuItemIdKey,
- fl_value_new_int(callback_data->menu_item_id));
- fl_method_channel_invoke_method(channel_, kMenuItemSelectedCallbackMethod,
- result, nullptr, nullptr, nullptr);
-}
-
-GtkWidget* Menu::value_to_menu(int64_t menu_id, FlValue* value) {
- if (fl_value_get_type(value) != FL_VALUE_TYPE_LIST) {
- return nullptr;
- }
-
- GtkWidget* menu = gtk_menu_new();
-
- for (size_t i = 0; i < fl_value_get_length(value); ++i) {
- GtkWidget* menu_item =
- value_to_menu_item(menu_id, fl_value_get_list_value(value, i));
- if (menu_item == nullptr) {
- return nullptr;
- }
-
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(menu_item));
- }
- return GTK_WIDGET(menu);
-}
-
-GtkWidget* Menu::value_to_menu_item(int64_t menu_id, FlValue* value) {
- if (fl_value_get_type(value) != FL_VALUE_TYPE_MAP) {
- return nullptr;
- }
-
- FlValue* type_value = fl_value_lookup_string(value, kTypeKey);
- if (type_value == nullptr ||
- fl_value_get_type(type_value) != FL_VALUE_TYPE_STRING) {
- return nullptr;
- }
-
- GtkWidget* menu_item = nullptr;
-
- const gchar* type = fl_value_get_string(type_value);
-
- if (strcmp(type, kSeparatorKey) == 0) {
- menu_item = gtk_separator_menu_item_new();
- } else {
- const gchar* label = nullptr;
- FlValue* label_value = fl_value_lookup_string(value, kLabelKey);
- if (label_value != nullptr &&
- fl_value_get_type(label_value) == FL_VALUE_TYPE_STRING) {
- label = fl_value_get_string(label_value);
- }
-
- FlValue* image_value = fl_value_lookup_string(value, kImageKey);
- if (image_value != nullptr &&
- fl_value_get_type(image_value) == FL_VALUE_TYPE_STRING) {
- const gchar* image = fl_value_get_string(image_value);
-
- // g_print("value_to_menu_item type:%s, label:%s, image:%s\n", type,
- // label, image);
-
- menu_item = gtk_menu_item_new();
-
- GtkWidget* box_widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
- GtkWidget* icon_widget = gtk_image_new_from_file(image);
- GtkWidget* label_widget = gtk_label_new(label);
-
- gtk_container_add(GTK_CONTAINER(box_widget), icon_widget);
- gtk_container_add(GTK_CONTAINER(box_widget), label_widget);
- gtk_container_add(GTK_CONTAINER(menu_item), box_widget);
-
- gtk_widget_show_all(menu_item);
- } else {
- g_print("value_to_menu_item type:%s, label:%s\n", type, label);
- }
-
- if (!menu_item) {
- if (strcmp(type, kCheckboxKey) == 0) {
- menu_item = gtk_check_menu_item_new_with_label(label);
- } else {
- menu_item = gtk_menu_item_new_with_label(label);
- }
- }
-
- FlValue* enabled_value = fl_value_lookup_string(value, kEnabledKey);
- if (enabled_value != nullptr &&
- fl_value_get_type(enabled_value) == FL_VALUE_TYPE_BOOL) {
- gtk_widget_set_sensitive(menu_item,
- fl_value_get_bool(enabled_value) ? TRUE : FALSE);
- }
-
- if (strcmp(type, kSubMenuKey) == 0) {
- GtkWidget* subMenu =
- value_to_menu(menu_id, fl_value_lookup_string(value, kSubMenuKey));
- if (subMenu == nullptr) {
- return nullptr;
- }
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), subMenu);
- } else {
- if (strcmp(type, kCheckboxKey) == 0) {
- FlValue* checked_value = fl_value_lookup_string(value, kCheckedKey);
- if (checked_value &&
- fl_value_get_type(checked_value) == FL_VALUE_TYPE_BOOL) {
- bool checked = fl_value_get_bool(checked_value);
- gtk_check_menu_item_set_active(
- reinterpret_cast(menu_item), checked);
- }
- }
-
- FlValue* id_value = fl_value_lookup_string(value, kIdKey);
- if (id_value != nullptr &&
- fl_value_get_type(id_value) == FL_VALUE_TYPE_INT) {
- TrayCallbackData* callback_data = new TrayCallbackData();
- callback_data->menu = this;
- callback_data->menu_id = menu_id;
- callback_data->menu_item_id = fl_value_get_int(id_value);
-
- g_signal_connect(G_OBJECT(menu_item), "activate",
- G_CALLBACK(Menu::menu_item_callback), callback_data);
- }
- }
- }
-
- return menu_item;
-}
\ No newline at end of file
diff --git a/linux/menu.h b/linux/menu.h
deleted file mode 100644
index 3d12601..0000000
--- a/linux/menu.h
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef __MENU_H__
-#define __MENU_H__
-
-#include
-#include
-#include
-
-class Menu {
- public:
- Menu(FlMethodChannel* channel, int menu_id) noexcept;
- ~Menu() noexcept;
-
- bool create_context_menu(FlValue* args);
- FlMethodResponse* set_label(FlValue* args);
- FlMethodResponse* set_image(FlValue* args);
- FlMethodResponse* set_enable(FlValue* args);
- FlMethodResponse* set_check(FlValue* args);
-
- GtkWidget* get_menu() const;
-
- protected:
- GtkWidget* value_to_menu(int64_t menu_id, FlValue* value);
- GtkWidget* value_to_menu_item(int64_t menu_id, FlValue* value);
-
- static void menu_item_callback(GtkMenuItem* item, gpointer user_data);
- void handle_menu_item_callback(GtkMenuItem* item, gpointer user_data);
-
- int64_t menu_id() const;
-
- void set_label(int64_t menu_item_id, const char* label);
- void set_image(int64_t menu_item_id, const char* image);
- void set_enable(int64_t menu_item_id, bool enabled);
- void set_check(int64_t menu_item_id, bool checked);
-
- protected:
- FlMethodChannel* channel_ = nullptr;
-
- int64_t menu_id_ = -1;
-
- GtkWidget* gtk_menu_ = nullptr;
-};
-
-#endif // __MENU_H__
\ No newline at end of file
diff --git a/linux/menu_manager.cc b/linux/menu_manager.cc
deleted file mode 100644
index 58933e2..0000000
--- a/linux/menu_manager.cc
+++ /dev/null
@@ -1,229 +0,0 @@
-#include "menu_manager.h"
-
-#include "errors.h"
-#include "menu.h"
-
-constexpr char kCreateContextMenu[] = "CreateContextMenu";
-constexpr char kSetLabel[] = "SetLabel";
-constexpr char kSetImage[] = "SetImage";
-constexpr char kSetEnable[] = "SetEnable";
-constexpr char kSetCheck[] = "SetCheck";
-
-namespace {
-
-constexpr char kMenuIdKey[] = "menu_id";
-
-} // namespace
-
-MenuManager::MenuManager(FlMethodChannel* channel) noexcept
- : channel_(channel) {}
-
-MenuManager::~MenuManager() noexcept {
- channel_ = nullptr;
-}
-
-void MenuManager::handle_method_call(FlMethodCall* method_call) {
- g_autoptr(FlMethodResponse) response = nullptr;
-
- const gchar* method = fl_method_call_get_name(method_call);
- FlValue* args = fl_method_call_get_args(method_call);
-
- // g_print("method call %s\n", method);
-
- if (strcmp(method, kCreateContextMenu) == 0) {
- response = create_context_menu(args);
- } else if (strcmp(method, kSetLabel) == 0) {
- response = set_label(args);
- } else if (strcmp(method, kSetImage) == 0) {
- response = set_image(args);
- } else if (strcmp(method, kSetEnable) == 0) {
- response = set_enable(args);
- } else if (strcmp(method, kSetCheck) == 0) {
- response = set_check(args);
- } else {
- response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
- }
-
- g_autoptr(GError) error = nullptr;
- if (!fl_method_call_respond(method_call, response, &error)) {
- g_warning("Failed to send method call response: %s", error->message);
- }
-}
-
-FlMethodResponse* MenuManager::create_context_menu(FlValue* args) {
- g_autoptr(FlValue) result = fl_value_new_bool(FALSE);
- FlMethodResponse* response = nullptr;
-
- do {
- if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
- response = FL_METHOD_RESPONSE(fl_method_error_response_new(
- errors::kBadArgumentsError, "", nullptr));
- break;
- }
-
- FlValue* menu_id_value = fl_value_lookup_string(args, kMenuIdKey);
- if (!menu_id_value ||
- fl_value_get_type(menu_id_value) != FL_VALUE_TYPE_INT) {
- response = FL_METHOD_RESPONSE(fl_method_error_response_new(
- errors::kBadArgumentsError, "", nullptr));
- break;
- }
-
- int64_t menu_id = fl_value_get_int(menu_id_value);
-
- std::unique_ptr