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
  • - ➖ + + + 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 menu = std::make_unique(channel_, menu_id); - if (!menu) { - response = FL_METHOD_RESPONSE( - fl_method_error_response_new(errors::kOutOfMemoryError, "", nullptr)); - break; - } - - if (!menu->create_context_menu(args)) { - break; - } - - add_menu(menu_id, std::move(menu)); - - result = fl_value_new_bool(TRUE); - - } while (false); - - if (nullptr == response) { - response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); - } - - return response; -} - -FlMethodResponse* MenuManager::set_label(FlValue* args) { - g_autoptr(FlValue) result = fl_value_new_bool(FALSE); - FlMethodResponse* response = nullptr; - - do { - std::shared_ptr menu = get_menu(args); - if (!menu) { - response = FL_METHOD_RESPONSE( - fl_method_error_response_new(errors::kNotFoundError, "", nullptr)); - break; - } - - response = menu->set_label(args); - - result = fl_value_new_bool(TRUE); - - } while (false); - - if (nullptr == response) { - response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); - } - - return response; -} - -FlMethodResponse* MenuManager::set_image(FlValue* args) { - g_autoptr(FlValue) result = fl_value_new_bool(FALSE); - FlMethodResponse* response = nullptr; - - do { - std::shared_ptr menu = get_menu(args); - if (!menu) { - response = FL_METHOD_RESPONSE( - fl_method_error_response_new(errors::kNotFoundError, "", nullptr)); - break; - } - - response = menu->set_image(args); - - result = fl_value_new_bool(TRUE); - - } while (false); - - if (nullptr == response) { - response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); - } - - return response; -} - -FlMethodResponse* MenuManager::set_enable(FlValue* args) { - g_autoptr(FlValue) result = fl_value_new_bool(FALSE); - FlMethodResponse* response = nullptr; - - do { - std::shared_ptr menu = get_menu(args); - if (!menu) { - response = FL_METHOD_RESPONSE( - fl_method_error_response_new(errors::kNotFoundError, "", nullptr)); - break; - } - - response = menu->set_enable(args); - - result = fl_value_new_bool(TRUE); - - } while (false); - - if (nullptr == response) { - response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); - } - - return response; -} - -FlMethodResponse* MenuManager::set_check(FlValue* args) { - g_autoptr(FlValue) result = fl_value_new_bool(FALSE); - FlMethodResponse* response = nullptr; - - do { - std::shared_ptr menu = get_menu(args); - if (!menu) { - response = FL_METHOD_RESPONSE( - fl_method_error_response_new(errors::kNotFoundError, "", nullptr)); - break; - } - - response = menu->set_check(args); - - result = fl_value_new_bool(TRUE); - - } while (false); - - if (nullptr == response) { - response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); - } - - return response; -} - -bool MenuManager::add_menu(int64_t menu_id, std::unique_ptr menu) { - menus_map_.emplace(menu_id, std::move(menu)); - return true; -} - -std::shared_ptr MenuManager::get_menu(int64_t menu_id) { - auto iter = menus_map_.find(menu_id); - return (iter != menus_map_.end()) ? iter->second : nullptr; -} - -std::shared_ptr MenuManager::get_menu(FlValue* args) { - std::shared_ptr menu; - - do { - if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) { - 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) { - break; - } - - int64_t menu_id = fl_value_get_int(menu_id_value); - - menu = get_menu(menu_id); - - } while (false); - - return menu; -} \ No newline at end of file diff --git a/linux/menu_manager.h b/linux/menu_manager.h deleted file mode 100644 index 578bb5f..0000000 --- a/linux/menu_manager.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef __MENU_MANAGER_H__ -#define __MENU_MANAGER_H__ - -#include -#include -#include -#include - -extern const char kCreateContextMenu[]; -extern const char kSetLabel[]; -extern const char kSetImage[]; -extern const char kSetEnable[]; -extern const char kSetCheck[]; - -class Menu; - -class MenuManager { - public: - MenuManager(FlMethodChannel* channel) noexcept; - ~MenuManager() noexcept; - - void handle_method_call(FlMethodCall* method_call); - - std::shared_ptr get_menu(int64_t menu_id); - - protected: - FlMethodResponse* 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); - - protected: - bool add_menu(int64_t menu_id, std::unique_ptr menu); - std::shared_ptr get_menu(FlValue* args); - - protected: - FlMethodChannel* channel_ = nullptr; - - std::unordered_map> menus_map_; -}; - -#endif // __MENU_MANAGER_H__ \ No newline at end of file diff --git a/linux/system_tray_plugin.cc b/linux/system_tray_plugin.cc deleted file mode 100644 index 14b763a..0000000 --- a/linux/system_tray_plugin.cc +++ /dev/null @@ -1,151 +0,0 @@ -#include "include/system_tray/system_tray_plugin.h" - -#include -#include -#include - -#include -#include - -#include "app_window.h" -#include "menu_manager.h" -#include "tray.h" - -namespace { - -constexpr char kChannelNameAppWindow[] = "flutter/system_tray/app_window"; -constexpr char kChannelNameMenuManager[] = "flutter/system_tray/menu_manager"; -constexpr char kChannelNameTray[] = "flutter/system_tray/tray"; - -} // namespace - -#define SYSTEM_TRAY_PLUGIN(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), system_tray_plugin_get_type(), \ - SystemTrayPlugin)) - -struct _SystemTrayPlugin { - GObject parent_instance; - - FlPluginRegistrar* registrar; - - FlMethodChannel* channel_app_window = nullptr; - FlMethodChannel* channel_menu_manager = nullptr; - FlMethodChannel* channel_tray = nullptr; - - std::unique_ptr app_window; - std::shared_ptr menu_manager; - std::unique_ptr tray; -}; - -G_DEFINE_TYPE(SystemTrayPlugin, system_tray_plugin, g_object_get_type()) - -SystemTrayPlugin* g_plugin = nullptr; - -// Called when a method call is received from Flutter. -static void system_tray_plugin_handle_method_call(SystemTrayPlugin* self, - FlMethodCall* method_call) { - g_autoptr(FlMethodResponse) response = nullptr; - - const gchar* method = fl_method_call_get_name(method_call); - - g_print("method call %s\n", method); - - if (strcmp(method, kInitAppWindow) == 0 || - strcmp(method, kShowAppWindow) == 0 || - strcmp(method, kHideAppWindow) == 0 || - strcmp(method, kCloseAppWindow) == 0) { - self->app_window->handle_method_call(method_call); - } else if (strcmp(method, kCreateContextMenu) == 0 || - strcmp(method, kSetLabel) == 0 || strcmp(method, kSetImage) == 0 || - strcmp(method, kSetEnable) == 0 || - strcmp(method, kSetCheck) == 0) { - self->menu_manager->handle_method_call(method_call); - } else if (strcmp(method, kInitSystemTray) == 0 || - strcmp(method, kSetSystemTrayInfo) == 0 || - strcmp(method, kSetContextMenu) == 0 || - strcmp(method, kPopupContextMenu) == 0 || - strcmp(method, kGetTitle) == 0 || - strcmp(method, kDestroySystemTray) == 0) { - self->tray->handle_method_call(method_call); - } 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); - } - } -} - -static void system_tray_plugin_dispose(GObject* object) { - SystemTrayPlugin* self = SYSTEM_TRAY_PLUGIN(object); - - g_clear_object(&self->registrar); - - g_clear_object(&self->channel_app_window); - g_clear_object(&self->channel_menu_manager); - g_clear_object(&self->channel_tray); - - G_OBJECT_CLASS(system_tray_plugin_parent_class)->dispose(object); -} - -static void system_tray_plugin_class_init(SystemTrayPluginClass* klass) { - G_OBJECT_CLASS(klass)->dispose = system_tray_plugin_dispose; -} - -static void system_tray_plugin_init(SystemTrayPlugin* self) { - g_plugin = self; -} - -static void method_call_cb(FlMethodChannel* channel, - FlMethodCall* method_call, - gpointer user_data) { - SystemTrayPlugin* plugin = SYSTEM_TRAY_PLUGIN(user_data); - system_tray_plugin_handle_method_call(plugin, method_call); -} - -void system_tray_plugin_register_with_registrar(FlPluginRegistrar* registrar) { - SystemTrayPlugin* plugin = - SYSTEM_TRAY_PLUGIN(g_object_new(system_tray_plugin_get_type(), nullptr)); - - plugin->registrar = FL_PLUGIN_REGISTRAR(g_object_ref(registrar)); - - g_autoptr(FlStandardMethodCodec) codec_app_window = - fl_standard_method_codec_new(); - plugin->channel_app_window = fl_method_channel_new( - fl_plugin_registrar_get_messenger(registrar), kChannelNameAppWindow, - FL_METHOD_CODEC(codec_app_window)); - - g_autoptr(FlStandardMethodCodec) codec_menu_manager = - fl_standard_method_codec_new(); - plugin->channel_menu_manager = fl_method_channel_new( - fl_plugin_registrar_get_messenger(registrar), kChannelNameMenuManager, - FL_METHOD_CODEC(codec_menu_manager)); - - g_autoptr(FlStandardMethodCodec) codec_tray = fl_standard_method_codec_new(); - plugin->channel_tray = - fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar), - kChannelNameTray, FL_METHOD_CODEC(codec_tray)); - - plugin->app_window = std::make_unique(plugin->registrar, - plugin->channel_app_window); - - plugin->menu_manager = - std::make_shared(plugin->channel_menu_manager); - - plugin->tray = - std::make_unique(plugin->channel_tray, plugin->menu_manager); - - fl_method_channel_set_method_call_handler( - plugin->channel_app_window, method_call_cb, g_object_ref(plugin), - g_object_unref); - - fl_method_channel_set_method_call_handler( - plugin->channel_menu_manager, method_call_cb, g_object_ref(plugin), - g_object_unref); - - fl_method_channel_set_method_call_handler( - plugin->channel_tray, method_call_cb, g_object_ref(plugin), - g_object_unref); - - g_object_unref(plugin); -} \ No newline at end of file diff --git a/linux/tray.cc b/linux/tray.cc deleted file mode 100644 index 8f995a4..0000000 --- a/linux/tray.cc +++ /dev/null @@ -1,399 +0,0 @@ -#ifndef NATIVE_C -#define NATIVE_C - -#include "tray.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "errors.h" -#include "menu.h" -#include "menu_manager.h" - -constexpr char kInitSystemTray[] = "InitSystemTray"; -constexpr char kSetSystemTrayInfo[] = "SetSystemTrayInfo"; -constexpr char kSetContextMenu[] = "SetContextMenu"; -constexpr char kPopupContextMenu[] = "PopupContextMenu"; -constexpr char kGetTitle[] = "GetTitle"; -constexpr char kDestroySystemTray[] = "DestroySystemTray"; - -namespace { - -constexpr char kTrayIdKey[] = "tray_id"; -constexpr char kTitleKey[] = "title"; -constexpr char kIconPathKey[] = "iconpath"; -constexpr char kToolTipKey[] = "tooltip"; - -} // namespace - -Tray::Tray(FlMethodChannel* channel, - std::weak_ptr menu_manager) noexcept - : channel_(channel), menu_manager_(menu_manager) {} - -Tray::~Tray() noexcept { - destroy_indicator(); - - channel_ = nullptr; -} - -bool Tray::init_indicator_api() { - bool ret = false; - - do { - if (indicator_api_inited_) { - ret = true; - break; - } - - void* handle = dlopen("libappindicator3.so.1", RTLD_LAZY); - if (!handle) { - break; - } - - app_indicator_new_ = reinterpret_cast( - dlsym(handle, "app_indicator_new")); - app_indicator_set_status_ = reinterpret_cast( - dlsym(handle, "app_indicator_set_status")); - app_indicator_set_icon_full_ = - reinterpret_cast( - dlsym(handle, "app_indicator_set_icon_full")); - app_indicator_set_attention_icon_full_ = - reinterpret_cast( - dlsym(handle, "app_indicator_set_attention_icon_full")); - app_indicator_set_label_ = reinterpret_cast( - dlsym(handle, "app_indicator_set_label")); - app_indicator_set_title_ = reinterpret_cast( - dlsym(handle, "app_indicator_set_title")); - app_indicator_get_label_ = reinterpret_cast( - dlsym(handle, "app_indicator_get_label")); - app_indicator_set_menu_ = reinterpret_cast( - dlsym(handle, "app_indicator_set_menu")); - - if (!app_indicator_new_ || !app_indicator_set_status_ || - !app_indicator_set_icon_full_ || - !app_indicator_set_attention_icon_full_ || !app_indicator_set_label_ || - !app_indicator_set_label_ || !app_indicator_get_label_ || - !app_indicator_set_menu_) { - break; - } - - indicator_api_inited_ = true; - - ret = true; - } while (false); - - return ret; -} - -bool Tray::create_indicator(const char* tray_id) { - // printf("SystemTray::create_indicator tray_id: %s\n", tray_id); - - bool ret = false; - - do { - if (!tray_id) { - break; - } - - if (!indicator_api_inited_) { - break; - } - - if (!app_indicator_) { - app_indicator_ = app_indicator_new_( - tray_id, "", APP_INDICATOR_CATEGORY_APPLICATION_STATUS); - if (!app_indicator_) { - break; - } - } - - app_indicator_set_status_(app_indicator_, APP_INDICATOR_STATUS_ACTIVE); - ret = true; - } while (false); - - return ret; -} - -void Tray::destroy_indicator() { - context_menu_id_ = -1; - - if (app_indicator_) { - g_object_unref(G_OBJECT(app_indicator_)); - app_indicator_ = nullptr; - } -} - -void Tray::hide_indicator() { - context_menu_id_ = -1; - - if (app_indicator_) { - app_indicator_set_status_(app_indicator_, APP_INDICATOR_STATUS_PASSIVE); - } -} - -void Tray::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, kInitSystemTray) == 0) { - response = init_tray(args); - } else if (strcmp(method, kSetSystemTrayInfo) == 0) { - response = set_tray_info(args); - } else if (strcmp(method, kSetContextMenu) == 0) { - response = set_context_menu(args); - } else if (strcmp(method, kPopupContextMenu) == 0) { - response = popup_context_menu(args); - } else if (strcmp(method, kGetTitle) == 0) { - response = get_title(args); - } else if (strcmp(method, kDestroySystemTray) == 0) { - response = destroy_system_tray(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* Tray::init_tray(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; - } - - const gchar* tray_id = nullptr; - - FlValue* tray_id_value = fl_value_lookup_string(args, kTrayIdKey); - if (tray_id_value && - fl_value_get_type(tray_id_value) == FL_VALUE_TYPE_STRING) { - tray_id = fl_value_get_string(tray_id_value); - } - - if (!init_tray(tray_id)) { - break; - } - - response = set_tray_info(args); - - } while (false); - - if (nullptr == response) { - response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); - } - - return response; -} - -FlMethodResponse* Tray::set_tray_info(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; - } - - const gchar* title = nullptr; - const gchar* icon_path = nullptr; - const gchar* tool_tip = nullptr; - - FlValue* title_value = fl_value_lookup_string(args, kTitleKey); - if (title_value && fl_value_get_type(title_value) == FL_VALUE_TYPE_STRING) { - title = fl_value_get_string(title_value); - } - - FlValue* icon_path_value = fl_value_lookup_string(args, kIconPathKey); - if (icon_path_value && - fl_value_get_type(icon_path_value) == FL_VALUE_TYPE_STRING) { - icon_path = fl_value_get_string(icon_path_value); - } - - FlValue* tooltip_value = fl_value_lookup_string(args, kToolTipKey); - if (tooltip_value && - fl_value_get_type(tooltip_value) == FL_VALUE_TYPE_STRING) { - tool_tip = fl_value_get_string(tooltip_value); - } - - result = fl_value_new_bool(set_tray_info(title, icon_path, tool_tip)); - - } while (false); - - if (nullptr == response) { - response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); - } - - return response; -} - -FlMethodResponse* Tray::set_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_INT) { - response = FL_METHOD_RESPONSE(fl_method_error_response_new( - errors::kBadArgumentsError, "", nullptr)); - break; - } - - set_context_menu(fl_value_get_int(args)); - - result = fl_value_new_bool(TRUE); - - } while (false); - - if (nullptr == response) { - response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); - } - return response; -} - -FlMethodResponse* Tray::popup_context_menu(FlValue* args) { - g_autoptr(FlValue) result = fl_value_new_bool(TRUE); - return FL_METHOD_RESPONSE(fl_method_success_response_new(result)); -} - -FlMethodResponse* Tray::get_title(FlValue* args) { - g_autoptr(FlValue) result = fl_value_new_string(""); - FlMethodResponse* response = nullptr; - - do { - if (!app_indicator_) { - break; - } - - const gchar* title = app_indicator_get_label_(app_indicator_); - result = fl_value_new_string(title ? title : ""); - - } while (false); - - if (nullptr == response) { - response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); - } - return response; -} - -FlMethodResponse* Tray::destroy_system_tray(FlValue* args) { - g_autoptr(FlValue) result = fl_value_new_string(FALSE); - FlMethodResponse* response = nullptr; - - do { - if (!app_indicator_) { - break; - } - - hide_indicator(); - - result = fl_value_new_bool(TRUE); - - } while (false); - - if (nullptr == response) { - response = FL_METHOD_RESPONSE(fl_method_success_response_new(result)); - } - return response; -} - -bool Tray::init_tray(const char* tray_id) { - bool ret = false; - - do { - if (!init_indicator_api()) { - break; - } - - if (!create_indicator(tray_id)) { - break; - } - - ret = true; - } while (false); - - return ret; -} - -bool Tray::set_tray_info(const char* title, - const char* icon_path, - const char* toolTip) { - printf( - "SystemTray::set_system_tray_info title: %s, icon_path: %s, toolTip: " - "%s\n", - title, icon_path, toolTip); - - bool ret = false; - - do { - if (!app_indicator_) { - break; - } - - if (icon_path) { - if (strlen(icon_path)) { - app_indicator_set_status_(app_indicator_, APP_INDICATOR_STATUS_ACTIVE); - app_indicator_set_icon_full_(app_indicator_, icon_path, "icon"); - } else { - app_indicator_set_status_(app_indicator_, APP_INDICATOR_STATUS_PASSIVE); - } - } - - if (title) { - app_indicator_set_label_(app_indicator_, title, nullptr); - } - - ret = true; - } while (false); - - return ret; -} - -void Tray::set_context_menu(int64_t context_menu_id) { - context_menu_id_ = context_menu_id; - - do { - if (menu_manager_.expired()) { - break; - } - - std::shared_ptr menu_manager = menu_manager_.lock(); - std::shared_ptr menu = menu_manager->get_menu(get_context_menu_id()); - if (!menu) { - break; - } - - if (!app_indicator_) { - break; - } - - GtkWidget* system_menu = menu->get_menu(); - - gtk_widget_show_all(system_menu); - app_indicator_set_menu_(app_indicator_, GTK_MENU(system_menu)); - - } while (false); -} - -int64_t Tray::get_context_menu_id() const { - return context_menu_id_; -} - -#endif // NATIVE_C \ No newline at end of file diff --git a/linux/tray.h b/linux/tray.h deleted file mode 100644 index 2ce872a..0000000 --- a/linux/tray.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef __TRAY_H__ -#define __TRAY_H__ - -#include -#include -#ifdef HAVE_AYATANA -#include -#else -#include -#endif -#include - -typedef AppIndicator* (*app_indicator_new_fun)(const gchar*, - const gchar*, - AppIndicatorCategory); - -typedef void (*app_indicator_set_status_fun)(AppIndicator*, AppIndicatorStatus); -typedef void (*app_indicator_set_icon_full_func)(AppIndicator* self, - const gchar* icon_name, - const gchar* icon_desc); -typedef void (*app_indicator_set_attention_icon_full_fun)(AppIndicator*, - const gchar*, - const gchar*); -typedef void (*app_indicator_set_label_func)(AppIndicator* self, - const gchar* label, - const gchar* guide); - -typedef void (*app_indicator_set_title_func)(AppIndicator* self, - const gchar* title); - -typedef const gchar* (*app_indicator_get_label_func)(AppIndicator* self); - -typedef void (*app_indicator_set_menu_fun)(AppIndicator*, GtkMenu*); - -extern const char kInitSystemTray[]; -extern const char kSetSystemTrayInfo[]; -extern const char kSetContextMenu[]; -extern const char kPopupContextMenu[]; -extern const char kGetTitle[]; -extern const char kDestroySystemTray[]; - -class MenuManager; - -class Tray { - public: - Tray(FlMethodChannel* _channel, - std::weak_ptr menu_manager) noexcept; - ~Tray() noexcept; - - void handle_method_call(FlMethodCall* method_call); - - protected: - FlMethodResponse* init_tray(FlValue* args); - FlMethodResponse* set_tray_info(FlValue* args); - FlMethodResponse* set_context_menu(FlValue* args); - FlMethodResponse* popup_context_menu(FlValue* args); - FlMethodResponse* get_title(FlValue* args); - FlMethodResponse* destroy_system_tray(FlValue* args); - - bool init_tray(const char* tray_id); - bool set_tray_info(const char* title, - const char* icon_path, - const char* toolTip); - void set_context_menu(int64_t context_menu_id); - int64_t get_context_menu_id() const; - - bool init_indicator_api(); - bool create_indicator(const char* tray_id); - void destroy_indicator(); - void hide_indicator(); - - protected: - app_indicator_new_fun app_indicator_new_ = nullptr; - app_indicator_set_status_fun app_indicator_set_status_ = nullptr; - app_indicator_set_icon_full_func app_indicator_set_icon_full_ = nullptr; - app_indicator_set_attention_icon_full_fun - app_indicator_set_attention_icon_full_ = nullptr; - app_indicator_set_label_func app_indicator_set_label_ = nullptr; - app_indicator_set_title_func app_indicator_set_title_ = nullptr; - app_indicator_get_label_func app_indicator_get_label_ = nullptr; - app_indicator_set_menu_fun app_indicator_set_menu_ = nullptr; - - FlMethodChannel* channel_ = nullptr; - std::weak_ptr menu_manager_; - - bool indicator_api_inited_ = false; - - AppIndicator* app_indicator_ = nullptr; - - int context_menu_id_ = -1; -}; - -#endif // __TRAY_H__ \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 084d0f7..2898346 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,62 +1,94 @@ # 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" 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" - source: hosted - version: "1.2.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.flutter-io.cn" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" 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" + dart_xdg_status_notifier_item: + dependency: "direct main" + 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" 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 + sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" + url: "https://pub.dev" + source: hosted + version: "2.2.0" flutter: dependency: "direct main" description: flutter @@ -66,7 +98,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: @@ -74,109 +107,179 @@ 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: "direct main" 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" 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" 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: "direct main" 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" + xml: + dependency: transitive + description: + name: xml + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" + url: "https://pub.dev" + source: hosted + version: "6.6.1" sdks: - dart: ">=2.17.0-0 <3.0.0" - flutter: ">=1.20.0" + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index 94bbd20..9d7a553 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,6 +8,7 @@ environment: flutter: ">=1.20.0" dependencies: + dart_xdg_status_notifier_item: ^1.1.0 flutter: sdk: flutter path: ^1.8.0 @@ -30,7 +31,7 @@ flutter: plugin: platforms: linux: - pluginClass: SystemTrayPlugin + dartPluginClass: SystemTrayLinuxPlugin macos: pluginClass: SystemTrayPlugin windows: