From 81ac583f9a9e1211fee557b03edec589fad1e4fc Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Sat, 7 Mar 2026 11:48:44 +0000
Subject: [PATCH 1/4] feat: replace Linux C++ backend with pure Dart using
dart_xdg_status_notifier_item
- Replaces the native C++ Linux backend with `dart_xdg_status_notifier_item`
- Removes all FFI and C code from the Linux side
- Refactors architecture to use `SystemTrayPlatform` interface
- Eliminates MethodChannel usage on Linux
- Expands supported feature matrix (tooltips, titles, various click events)
- Cleans up example application dependencies to allow pure Dart testing
---
README.md | 14 +-
example/.metadata | 24 +-
example/lib/main.dart | 111 +----
example/linux/CMakeLists.txt | 48 ++-
example/linux/flutter/CMakeLists.txt | 1 +
.../flutter/generated_plugin_registrant.cc | 8 -
example/linux/flutter/generated_plugins.cmake | 2 -
example/linux/runner/CMakeLists.txt | 26 ++
example/linux/{ => runner}/main.cc | 0
example/linux/{ => runner}/my_application.cc | 81 ++--
example/linux/{ => runner}/my_application.h | 5 +-
.../Flutter/GeneratedPluginRegistrant.swift | 2 -
example/pubspec.lock | 249 ++++++-----
example/pubspec.yaml | 1 -
.../flutter/generated_plugin_registrant.cc | 3 -
.../windows/flutter/generated_plugins.cmake | 1 -
lib/src/app_window.dart | 70 ++-
lib/src/menu.dart | 276 ++++++------
lib/src/menu_item.dart | 55 +--
lib/src/system_tray_linux.dart | 193 +++++++++
lib/src/system_tray_linux_plugin.dart | 5 +
lib/src/system_tray_method_channel.dart | 183 ++++++++
lib/src/system_tray_platform.dart | 62 +++
lib/src/tray.dart | 71 +---
lib/system_tray.dart | 1 +
linux/CMakeLists.txt | 51 ---
linux/app_window.cc | 205 ---------
linux/app_window.h | 48 ---
linux/errors.cc | 10 -
linux/errors.h | 13 -
.../include/system_tray/system_tray_plugin.h | 26 --
linux/menu.cc | 379 -----------------
linux/menu.h | 43 --
linux/menu_manager.cc | 229 ----------
linux/menu_manager.h | 43 --
linux/system_tray_plugin.cc | 151 -------
linux/tray.cc | 399 ------------------
linux/tray.h | 93 ----
pubspec.lock | 207 ++++++---
pubspec.yaml | 3 +-
40 files changed, 1087 insertions(+), 2305 deletions(-)
create mode 100644 example/linux/runner/CMakeLists.txt
rename example/linux/{ => runner}/main.cc (100%)
rename example/linux/{ => runner}/my_application.cc (58%)
rename example/linux/{ => runner}/my_application.h (70%)
create mode 100644 lib/src/system_tray_linux.dart
create mode 100644 lib/src/system_tray_linux_plugin.dart
create mode 100644 lib/src/system_tray_method_channel.dart
create mode 100644 lib/src/system_tray_platform.dart
delete mode 100644 linux/CMakeLists.txt
delete mode 100644 linux/app_window.cc
delete mode 100644 linux/app_window.h
delete mode 100644 linux/errors.cc
delete mode 100644 linux/errors.h
delete mode 100644 linux/include/system_tray/system_tray_plugin.h
delete mode 100644 linux/menu.cc
delete mode 100644 linux/menu.h
delete mode 100644 linux/menu_manager.cc
delete mode 100644 linux/menu_manager.h
delete mode 100644 linux/system_tray_plugin.cc
delete mode 100644 linux/tray.cc
delete mode 100644 linux/tray.h
diff --git a/README.md b/README.md
index 2b21711..bf8b5d9 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,13 @@ sudo apt-get install libayatana-appindicator3-dev
right-click
- ➖ |
+
+
+ - click
+ - right-click
+ - double-click
+
+ |
diff --git a/example/.metadata b/example/.metadata
index a8ebbd6..c5f4036 100644
--- a/example/.metadata
+++ b/example/.metadata
@@ -4,7 +4,27 @@
# This file should be version controlled and should not be manually edited.
version:
- revision: fa5883b78e566877613ad1ccb48dd92075cb5c23
- channel: dev
+ revision: "3b62efc2a3da49882f43c372e0bc53daef7295a6"
+ channel: "stable"
project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6
+ base_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6
+ - platform: linux
+ create_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6
+ base_revision: 3b62efc2a3da49882f43c372e0bc53daef7295a6
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 2c20a4e..d225ee1 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:io';
import 'dart:math';
-import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
import 'package:system_tray/system_tray.dart';
@@ -12,16 +11,6 @@ void main() async {
runApp(
const MyApp(),
);
-
- doWhenWindowReady(() {
- final win = appWindow;
- const initialSize = Size(600, 450);
- win.minSize = initialSize;
- win.size = initialSize;
- win.alignment = Alignment.center;
- win.title = "How to use system tray with Flutter";
- win.show();
- });
}
String getTrayImagePath(String imageName) {
@@ -243,7 +232,7 @@ class _MyAppState extends State {
),
MenuSeparator(),
MenuItemLabel(
- label: 'Exit', onClicked: (menuItem) => _appWindow.close()),
+ label: 'Exit', onClicked: (menuItem) => exit(0)),
],
);
@@ -270,7 +259,7 @@ class _MyAppState extends State {
MenuItemLabel(
label: 'Exit',
image: getImagePath('app_icon'),
- onClicked: (menuItem) => _appWindow.close(),
+ onClicked: (menuItem) => exit(0),
),
]);
@@ -282,47 +271,13 @@ class _MyAppState extends State {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
- body: WindowBorder(
- color: const Color(0xFF805306),
- width: 1,
- child: Column(
- children: [
- const TitleBar(),
- ContentBody(
- systemTray: _systemTray,
- menu: _menuMain,
- ),
- ],
- ),
- ),
- ),
- );
- }
-}
-
-const backgroundStartColor = Color(0xFFFFD500);
-const backgroundEndColor = Color(0xFFF6A00C);
-
-class TitleBar extends StatelessWidget {
- const TitleBar({Key? key}) : super(key: key);
-
- @override
- Widget build(BuildContext context) {
- return WindowTitleBarBox(
- child: Container(
- decoration: const BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- colors: [backgroundStartColor, backgroundEndColor],
- stops: [0.0, 1.0]),
- ),
- child: Row(
+ appBar: AppBar(title: const Text("System Tray Example")),
+ body: Column(
children: [
- Expanded(
- child: MoveWindow(),
+ ContentBody(
+ systemTray: _systemTray,
+ menu: _menuMain,
),
- const WindowButtons()
],
),
),
@@ -426,55 +381,3 @@ class ContentBody extends StatelessWidget {
);
}
}
-
-final buttonColors = WindowButtonColors(
- iconNormal: const Color(0xFF805306),
- mouseOver: const Color(0xFFF6A00C),
- mouseDown: const Color(0xFF805306),
- iconMouseOver: const Color(0xFF805306),
- iconMouseDown: const Color(0xFFFFD500));
-
-final closeButtonColors = WindowButtonColors(
- mouseOver: const Color(0xFFD32F2F),
- mouseDown: const Color(0xFFB71C1C),
- iconNormal: const Color(0xFF805306),
- iconMouseOver: Colors.white);
-
-class WindowButtons extends StatelessWidget {
- const WindowButtons({Key? key}) : super(key: key);
-
- @override
- Widget build(BuildContext context) {
- return Row(
- children: [
- MinimizeWindowButton(colors: buttonColors),
- MaximizeWindowButton(colors: buttonColors),
- CloseWindowButton(
- colors: closeButtonColors,
- onPressed: () {
- showDialog(
- context: context,
- barrierDismissible: false,
- builder: (BuildContext context) {
- return AlertDialog(
- title: const Text('Exit Program?'),
- content: const Text(
- ('The window will be hidden, to exit the program you can use the system menu.')),
- actions: [
- TextButton(
- child: const Text('OK'),
- onPressed: () {
- Navigator.of(context).pop();
- appWindow.hide();
- },
- ),
- ],
- );
- },
- );
- },
- ),
- ],
- );
- }
-}
diff --git a/example/linux/CMakeLists.txt b/example/linux/CMakeLists.txt
index 9c7f9a7..502cc35 100644
--- a/example/linux/CMakeLists.txt
+++ b/example/linux/CMakeLists.txt
@@ -1,11 +1,19 @@
-cmake_minimum_required(VERSION 3.10)
+# Project-level configuration.
+cmake_minimum_required(VERSION 3.13)
project(runner LANGUAGES CXX)
+# The name of the executable created for the application. Change this to change
+# the on-disk name of your application.
set(BINARY_NAME "system_tray_example")
-set(APPLICATION_ID "com.example.system_tray")
+# The unique GTK application identifier for this application. See:
+# https://wiki.gnome.org/HowDoI/ChooseApplicationID
+set(APPLICATION_ID "com.example.system_tray_example")
+# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
+# versions of CMake.
cmake_policy(SET CMP0063 NEW)
+# Load bundled libraries from the lib/ directory relative to the binary.
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Root filesystem for cross-building.
@@ -18,7 +26,7 @@ if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif()
-# Configure build options.
+# Define build configuration options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
@@ -27,6 +35,10 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
endif()
# Compilation settings that should be applied to most targets.
+#
+# Be cautious about adding new options here, as plugins use this function by
+# default. In most cases, you should add new options to specific targets instead
+# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
@@ -34,27 +46,20 @@ function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
endfunction()
-set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
-
# Flutter library and tool build rules.
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
-add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
+# Application build; see runner/CMakeLists.txt.
+add_subdirectory("runner")
-# Application build
-add_executable(${BINARY_NAME}
- "main.cc"
- "my_application.cc"
- "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
-)
-apply_standard_settings(${BINARY_NAME})
-target_link_libraries(${BINARY_NAME} PRIVATE flutter)
-target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
+# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)
+
# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
@@ -64,6 +69,7 @@ set_target_properties(${BINARY_NAME}
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)
+
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
@@ -94,11 +100,17 @@ install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
-if(PLUGIN_BUNDLED_LIBRARIES)
- install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
+ install(FILES "${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
-endif()
+endforeach(bundled_library)
+
+# Copy the native assets provided by the build.dart from all packages.
+set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/")
+install(DIRECTORY "${NATIVE_ASSETS_DIR}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
diff --git a/example/linux/flutter/CMakeLists.txt b/example/linux/flutter/CMakeLists.txt
index 33fd580..d5bd016 100644
--- a/example/linux/flutter/CMakeLists.txt
+++ b/example/linux/flutter/CMakeLists.txt
@@ -1,3 +1,4 @@
+# This file controls Flutter-level build steps. It should not be edited.
cmake_minimum_required(VERSION 3.10)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
diff --git a/example/linux/flutter/generated_plugin_registrant.cc b/example/linux/flutter/generated_plugin_registrant.cc
index f178fea..e71a16d 100644
--- a/example/linux/flutter/generated_plugin_registrant.cc
+++ b/example/linux/flutter/generated_plugin_registrant.cc
@@ -6,14 +6,6 @@
#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..2e1de87 100644
--- a/example/linux/flutter/generated_plugins.cmake
+++ b/example/linux/flutter/generated_plugins.cmake
@@ -3,8 +3,6 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
- bitsdojo_window_linux
- system_tray
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
diff --git a/example/linux/runner/CMakeLists.txt b/example/linux/runner/CMakeLists.txt
new file mode 100644
index 0000000..e97dabc
--- /dev/null
+++ b/example/linux/runner/CMakeLists.txt
@@ -0,0 +1,26 @@
+cmake_minimum_required(VERSION 3.13)
+project(runner LANGUAGES CXX)
+
+# Define the application target. To change its name, change BINARY_NAME in the
+# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
+# work.
+#
+# Any new source files that you add to the application should be added here.
+add_executable(${BINARY_NAME}
+ "main.cc"
+ "my_application.cc"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+)
+
+# Apply the standard set of build settings. This can be removed for applications
+# that need different build settings.
+apply_standard_settings(${BINARY_NAME})
+
+# Add preprocessor definitions for the application ID.
+add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
+
+# Add dependency libraries. Add any application-specific dependencies here.
+target_link_libraries(${BINARY_NAME} PRIVATE flutter)
+target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
+
+target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
diff --git a/example/linux/main.cc b/example/linux/runner/main.cc
similarity index 100%
rename from example/linux/main.cc
rename to example/linux/runner/main.cc
diff --git a/example/linux/my_application.cc b/example/linux/runner/my_application.cc
similarity index 58%
rename from example/linux/my_application.cc
rename to example/linux/runner/my_application.cc
index eba3c63..dabc05f 100644
--- a/example/linux/my_application.cc
+++ b/example/linux/runner/my_application.cc
@@ -1,14 +1,7 @@
#include "my_application.h"
-
#include
-#ifdef GDK_WINDOWING_X11
-#include
-#endif
-
#include "flutter/generated_plugin_registrant.h"
-#include
-
struct _MyApplication {
GtkApplication parent_instance;
char** dart_entrypoint_arguments;
@@ -16,52 +9,40 @@ struct _MyApplication {
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
+// Called when first Flutter frame received.
+static void first_frame_cb(MyApplication* self, FlView* view) {
+ gtk_widget_show(gtk_widget_get_toplevel(GTK_WIDGET(view)));
+}
+
// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
MyApplication* self = MY_APPLICATION(application);
GtkWindow* window =
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
- // Use a header bar when running in GNOME as this is the common style used
- // by applications and is the setup most users will be using (e.g. Ubuntu
- // desktop).
- // If running on X and not using GNOME then just use a traditional title bar
- // in case the window manager does more exotic layout, e.g. tiling.
- // If running on Wayland assume the header bar will work (may need changing
- // if future cases occur).
- gboolean use_header_bar = TRUE;
-#ifdef GDK_WINDOWING_X11
- GdkScreen* screen = gtk_window_get_screen(window);
- if (GDK_IS_X11_SCREEN(screen)) {
- const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
- if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
- use_header_bar = FALSE;
- }
- }
-#endif
- if (use_header_bar) {
- GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
- gtk_widget_show(GTK_WIDGET(header_bar));
- gtk_header_bar_set_title(header_bar, "system_tray_example");
- gtk_header_bar_set_show_close_button(header_bar, TRUE);
- gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
- } else {
- gtk_window_set_title(window, "system_tray_example");
- }
+ gtk_window_set_title(window, "system_tray_example");
- auto bdw = bitsdojo_window_from(window);
- bdw->setCustomFrame(true);
- // gtk_window_set_default_size(window, 1280, 720);
- gtk_widget_show(GTK_WIDGET(window));
+ gtk_window_set_default_size(window, 1280, 720);
g_autoptr(FlDartProject) project = fl_dart_project_new();
fl_dart_project_set_dart_entrypoint_arguments(
project, self->dart_entrypoint_arguments);
FlView* view = fl_view_new(project);
+ GdkRGBA background_color;
+ // Background defaults to black, override it here if necessary, e.g. #00000000
+ // for transparent.
+ gdk_rgba_parse(&background_color, "#000000");
+ fl_view_set_background_color(view, &background_color);
gtk_widget_show(GTK_WIDGET(view));
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
+ // Show the window when Flutter renders.
+ // Requires the view to be realized so we can start rendering.
+ g_signal_connect_swapped(view, "first-frame", G_CALLBACK(first_frame_cb),
+ self);
+ gtk_widget_realize(GTK_WIDGET(view));
+
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
gtk_widget_grab_focus(GTK_WIDGET(view));
@@ -88,6 +69,24 @@ static gboolean my_application_local_command_line(GApplication* application,
return TRUE;
}
+// Implements GApplication::startup.
+static void my_application_startup(GApplication* application) {
+ // MyApplication* self = MY_APPLICATION(object);
+
+ // Perform any actions required at application startup.
+
+ G_APPLICATION_CLASS(my_application_parent_class)->startup(application);
+}
+
+// Implements GApplication::shutdown.
+static void my_application_shutdown(GApplication* application) {
+ // MyApplication* self = MY_APPLICATION(object);
+
+ // Perform any actions required at application shutdown.
+
+ G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);
+}
+
// Implements GObject::dispose.
static void my_application_dispose(GObject* object) {
MyApplication* self = MY_APPLICATION(object);
@@ -99,12 +98,20 @@ static void my_application_class_init(MyApplicationClass* klass) {
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
G_APPLICATION_CLASS(klass)->local_command_line =
my_application_local_command_line;
+ G_APPLICATION_CLASS(klass)->startup = my_application_startup;
+ G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;
G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
}
static void my_application_init(MyApplication* self) {}
MyApplication* my_application_new() {
+ // Set the program name to the application ID, which helps various systems
+ // like GTK and desktop environments map this running application to its
+ // corresponding .desktop file. This ensures better integration by allowing
+ // the application to be recognized beyond its binary name.
+ g_set_prgname(APPLICATION_ID);
+
return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID, "flags",
G_APPLICATION_NON_UNIQUE, nullptr));
diff --git a/example/linux/my_application.h b/example/linux/runner/my_application.h
similarity index 70%
rename from example/linux/my_application.h
rename to example/linux/runner/my_application.h
index 72271d5..db16367 100644
--- a/example/linux/my_application.h
+++ b/example/linux/runner/my_application.h
@@ -3,7 +3,10 @@
#include
-G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
+G_DECLARE_FINAL_TYPE(MyApplication,
+ my_application,
+ MY,
+ APPLICATION,
GtkApplication)
/**
diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift
index 57765f1..8b18f69 100644
--- a/example/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -5,10 +5,8 @@
import FlutterMacOS
import Foundation
-import bitsdojo_window_macos
import system_tray
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
- BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin"))
SystemTrayPlugin.register(with: registry.registrar(forPlugin: "SystemTrayPlugin"))
}
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 69083bb..1ad2751 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -1,118 +1,110 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
- async:
- dependency: transitive
- description:
- name: async
- url: "https://pub.flutter-io.cn"
- source: hosted
- version: "2.9.0"
- bitsdojo_window:
- dependency: "direct main"
- description:
- name: bitsdojo_window
- url: "https://pub.flutter-io.cn"
- source: hosted
- version: "0.1.2"
- bitsdojo_window_linux:
- dependency: transitive
- description:
- name: bitsdojo_window_linux
- url: "https://pub.flutter-io.cn"
- source: hosted
- version: "0.1.2"
- bitsdojo_window_macos:
- dependency: transitive
- description:
- name: bitsdojo_window_macos
- url: "https://pub.flutter-io.cn"
- source: hosted
- version: "0.1.2"
- bitsdojo_window_platform_interface:
+ args:
dependency: transitive
description:
- name: bitsdojo_window_platform_interface
- url: "https://pub.flutter-io.cn"
+ name: args
+ sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
+ url: "https://pub.dev"
source: hosted
- version: "0.1.2"
- bitsdojo_window_windows:
+ version: "2.7.0"
+ async:
dependency: transitive
description:
- name: bitsdojo_window_windows
- url: "https://pub.flutter-io.cn"
+ name: async
+ sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
+ url: "https://pub.dev"
source: hosted
- version: "0.1.2"
+ 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"
+ 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 +114,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 +123,186 @@ 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"
- plugin_platform_interface:
+ version: "1.9.1"
+ petitparser:
dependency: transitive
description:
- name: plugin_platform_interface
- url: "https://pub.flutter-io.cn"
+ name: petitparser
+ sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
+ url: "https://pub.dev"
source: hosted
- version: "2.1.2"
+ 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"
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"
- win32:
+ 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: win32
- url: "https://pub.flutter-io.cn"
+ 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/example/pubspec.yaml b/example/pubspec.yaml
index c3fc2c2..b2a65a0 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -29,7 +29,6 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
- bitsdojo_window: 0.1.2
english_words: ^4.0.0
dev_dependencies:
diff --git a/example/windows/flutter/generated_plugin_registrant.cc b/example/windows/flutter/generated_plugin_registrant.cc
index 6423d8b..cd16273 100644
--- a/example/windows/flutter/generated_plugin_registrant.cc
+++ b/example/windows/flutter/generated_plugin_registrant.cc
@@ -6,12 +6,9 @@
#include "generated_plugin_registrant.h"
-#include
#include
void RegisterPlugins(flutter::PluginRegistry* registry) {
- BitsdojoWindowPluginRegisterWithRegistrar(
- registry->GetRegistrarForPlugin("BitsdojoWindowPlugin"));
SystemTrayPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SystemTrayPlugin"));
}
diff --git a/example/windows/flutter/generated_plugins.cmake b/example/windows/flutter/generated_plugins.cmake
index 369b953..1caa447 100644
--- a/example/windows/flutter/generated_plugins.cmake
+++ b/example/windows/flutter/generated_plugins.cmake
@@ -3,7 +3,6 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
- bitsdojo_window_windows
system_tray
)
diff --git a/lib/src/app_window.dart b/lib/src/app_window.dart
index 0a6a488..92c3db6 100644
--- a/lib/src/app_window.dart
+++ b/lib/src/app_window.dart
@@ -1,41 +1,29 @@
-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 'system_tray_platform.dart';
+
+/// Representation of native window
+class AppWindow {
+ AppWindow() {
+ _init();
+ }
+
+ /// Show native window
+ Future show() async {
+ await SystemTrayPlatform.instance.showAppWindow();
+ }
+
+ /// Hide native window
+ Future hide() async {
+ await SystemTrayPlatform.instance.hideAppWindow();
+ }
+
+ /// Close native window
+ Future close() async {
+ await SystemTrayPlatform.instance.closeAppWindow();
+ }
+
+ void _init() async {
+ await SystemTrayPlatform.instance.initAppWindow();
+ }
+}
diff --git a/lib/src/menu.dart b/lib/src/menu.dart
index 93ce2bc..0d77018 100644
--- a/lib/src/menu.dart
+++ b/lib/src/menu.dart
@@ -1,148 +1,128 @@
-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 'package:flutter/material.dart';
+
+import 'menu_item.dart';
+import 'system_tray_platform.dart';
+import 'utils.dart';
+
+class Menu {
+ 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() {
+ SystemTrayPlatform.instance
+ .registerMenuItemSelectedCallback(_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 SystemTrayPlatform.instance.buildMenu(_menuId, menus);
+ _updateInProgress = false;
+ } catch (e) {
+ debugPrint('Exception create context menu: $e');
+ }
+ return result;
+ }
+
+ Future _channelRepresentationForMenus(List menus) async {
+ _menuItemId = 1;
+ await _channelRepresentationForMenu(menus);
+ }
+
+ Future _channelRepresentationForMenu(List menus) async {
+ for (final menuItem in menus) {
+ menuItem.menuId = menuId;
+ menuItem.menuItemId = nextMenuItemId;
+ menuItem.imageAbsolutePath = await Utils.getIcon(menuItem.image);
+
+ if (menuItem is SubMenu) {
+ await _channelRepresentationForMenu(menuItem.children);
+ }
+ }
+ }
+
+ void _callbackHandler(int menuId, int menuItemId) {
+ if (_updateInProgress) {
+ debugPrint(
+ 'Warning: Menu selection callback received during menu update.');
+ return;
+ }
+
+ 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..9b353e7 100644
--- a/lib/src/menu_item.dart
+++ b/lib/src/menu_item.dart
@@ -1,10 +1,5 @@
-import 'package:flutter/services.dart';
import 'package:system_tray/src/utils.dart';
-
-const String _kSetLabel = "SetLabel";
-const String _kSetImage = "SetImage";
-const String _kSetEnable = "SetEnable";
-const String _kSetCheck = "SetCheck";
+import 'system_tray_platform.dart';
const String _kMenuIdKey = 'menu_id';
const String _kMenuItemIdKey = 'menu_item_id';
@@ -40,39 +35,23 @@ abstract class MenuItemBase {
}
Future setLabel(String label) async {
- bool result = await channel?.invokeMethod(_kSetLabel, {
- _kMenuIdKey: menuId ?? -1,
- _kMenuItemIdKey: menuItemId ?? -1,
- _kLabelKey: label,
- });
- if (result) {
- this.label = label;
- }
+ await SystemTrayPlatform.instance
+ .setMenuItemLabel(menuId ?? -1, menuItemId ?? -1, label);
+ this.label = label;
}
Future setImage(String image) async {
String? imageAbsolutePath = await Utils.getIcon(image);
-
- bool result = await channel?.invokeMethod(_kSetImage, {
- _kMenuIdKey: menuId ?? -1,
- _kMenuItemIdKey: menuItemId ?? -1,
- _kImageKey: imageAbsolutePath,
- });
- if (result) {
- this.image = image;
- this.imageAbsolutePath = imageAbsolutePath;
- }
+ await SystemTrayPlatform.instance.setMenuItemImage(
+ menuId ?? -1, menuItemId ?? -1, imageAbsolutePath ?? '');
+ this.image = image;
+ this.imageAbsolutePath = imageAbsolutePath;
}
Future setEnable(bool enabled) async {
- bool result = await channel?.invokeMethod(_kSetEnable, {
- _kMenuIdKey: menuId ?? -1,
- _kMenuItemIdKey: menuItemId ?? -1,
- _kEnabledKey: enabled,
- });
- if (result) {
- this.enabled = enabled;
- }
+ await SystemTrayPlatform.instance
+ .setMenuItemEnable(menuId ?? -1, menuItemId ?? -1, enabled);
+ this.enabled = enabled;
}
Future setCheck(bool checked) async {
@@ -80,17 +59,11 @@ abstract class MenuItemBase {
return;
}
- bool result = await channel?.invokeMethod(_kSetCheck, {
- _kMenuIdKey: menuId ?? -1,
- _kMenuItemIdKey: menuItemId ?? -1,
- _kCheckedKey: checked,
- });
- if (result) {
- this.checked = checked;
- }
+ await SystemTrayPlatform.instance
+ .setMenuItemCheck(menuId ?? -1, menuItemId ?? -1, checked);
+ this.checked = checked;
}
- MethodChannel? channel;
int? menuId;
int? menuItemId;
String? imageAbsolutePath;
diff --git a/lib/src/system_tray_linux.dart b/lib/src/system_tray_linux.dart
new file mode 100644
index 0000000..abc3670
--- /dev/null
+++ b/lib/src/system_tray_linux.dart
@@ -0,0 +1,193 @@
+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';
+import 'system_tray_platform.dart';
+
+class SystemTrayLinux extends SystemTrayPlatform {
+ final Map _menuMap = {};
+ StatusNotifierItemClient? _client;
+
+ void Function(String eventName)? _systemTrayEventCallback;
+ void Function(int menuId, int menuItemId)? _menuItemSelectedCallback;
+
+ @override
+ Future initAppWindow() async {
+ // No-op for pure dart linux backend
+ }
+
+ @override
+ Future showAppWindow() async {
+ // No-op for pure dart linux backend
+ }
+
+ @override
+ Future hideAppWindow() async {
+ // No-op for pure dart linux backend
+ }
+
+ @override
+ Future closeAppWindow() async {
+ // No-op for pure dart linux backend
+ }
+
+ @override
+ 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);
+ }
+ });
+
+ if (toolTip != null && toolTip.isNotEmpty) {
+ _client!.toolTip = StatusNotifierToolTip(
+ iconName: 'icon-name', title: toolTip, body: '', iconPixmap: []);
+ }
+
+ await _client!.connect();
+ return true;
+ }
+
+ @override
+ 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;
+ }
+
+ @override
+ Future setContextMenu(int menuId) async {
+ if (_menuMap.containsKey(menuId) && _client != null) {
+ await _client!.updateMenu(_menuMap[menuId]!);
+ }
+ }
+
+ @override
+ Future popUpContextMenu() async {
+ // Not typically supported directly via xdg_status_notifier_item without shell interaction
+ }
+
+ @override
+ Future getTitle() async {
+ return _client?.title ?? "";
+ }
+
+ @override
+ Future destroySystemTray() async {
+ if (_client != null) {
+ await _client!.close();
+ _client = null;
+ }
+ }
+
+ @override
+ void registerSystemTrayEventHandler(
+ void Function(String eventName) callback) {
+ _systemTrayEventCallback = callback;
+ }
+
+ 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);
+ }
+ });
+ }
+
+ @override
+ Future buildMenu(int menuId, List menus) async {
+ final children = menus.map((e) => _buildMenuItem(e, menuId)).toList();
+ final menu = DBusMenuItem(children: children);
+ _menuMap[menuId] = menu;
+
+ if (_client != null) {
+ // Re-apply if it is the current context menu (We might need to track which menu is active, but updating all is fine for basic behavior)
+ }
+ return true;
+ }
+
+ @override
+ Future setMenuItemLabel(
+ int menuId, int menuItemId, String label) async {
+ // For pure dart implementation, since menus are built from object state, we just require the caller to re-set context menu
+ }
+
+ @override
+ Future setMenuItemImage(
+ int menuId, int menuItemId, String imageAbsolutePath) async {}
+
+ @override
+ Future setMenuItemEnable(
+ int menuId, int menuItemId, bool enabled) async {}
+
+ @override
+ Future setMenuItemCheck(
+ int menuId, int menuItemId, bool checked) async {}
+
+ @override
+ void registerMenuItemSelectedCallback(
+ void Function(int menuId, int menuItemId) callback) {
+ _menuItemSelectedCallback = callback;
+ }
+}
diff --git a/lib/src/system_tray_linux_plugin.dart b/lib/src/system_tray_linux_plugin.dart
new file mode 100644
index 0000000..59093ac
--- /dev/null
+++ b/lib/src/system_tray_linux_plugin.dart
@@ -0,0 +1,5 @@
+import 'package:flutter/services.dart';
+
+class SystemTrayLinuxPlugin {
+ static void registerWith() {}
+}
diff --git a/lib/src/system_tray_method_channel.dart b/lib/src/system_tray_method_channel.dart
new file mode 100644
index 0000000..42f2322
--- /dev/null
+++ b/lib/src/system_tray_method_channel.dart
@@ -0,0 +1,183 @@
+import 'dart:async';
+
+import 'package:flutter/services.dart';
+
+import 'menu.dart';
+import 'menu_item.dart';
+import 'system_tray_platform.dart';
+
+class SystemTrayMethodChannel extends SystemTrayPlatform {
+ static const MethodChannel _appWindowChannel =
+ MethodChannel("flutter/system_tray/app_window");
+ static const MethodChannel _trayChannel =
+ MethodChannel("flutter/system_tray/tray");
+ static const MethodChannel _menuManagerChannel =
+ MethodChannel("flutter/system_tray/menu_manager");
+
+ void Function(String eventName)? _systemTrayEventCallback;
+ void Function(int menuId, int menuItemId)? _menuItemSelectedCallback;
+
+ SystemTrayMethodChannel() {
+ _trayChannel.setMethodCallHandler((call) async {
+ if (call.method == 'SystemTrayEventCallback') {
+ if (_systemTrayEventCallback != null) {
+ final String eventName = call.arguments;
+ _systemTrayEventCallback!(eventName);
+ }
+ }
+ });
+
+ _menuManagerChannel.setMethodCallHandler((call) async {
+ if (call.method == 'MenuItemSelectedCallback') {
+ final int menuId = call.arguments['menu_id'];
+ final int menuItemId = call.arguments['menu_item_id'];
+ if (_menuItemSelectedCallback != null) {
+ _menuItemSelectedCallback!(menuId, menuItemId);
+ }
+ }
+ });
+ }
+
+ @override
+ Future initAppWindow() async {
+ await _appWindowChannel.invokeMethod("InitAppWindow");
+ }
+
+ @override
+ Future showAppWindow() async {
+ await _appWindowChannel.invokeMethod("ShowAppWindow");
+ }
+
+ @override
+ Future hideAppWindow() async {
+ await _appWindowChannel.invokeMethod("HideAppWindow");
+ }
+
+ @override
+ Future closeAppWindow() async {
+ await _appWindowChannel.invokeMethod("CloseAppWindow");
+ }
+
+ @override
+ Future initSystemTray({
+ required String trayId,
+ required String iconPath,
+ String? title,
+ String? toolTip,
+ bool isTemplate = false,
+ }) async {
+ return await _trayChannel.invokeMethod(
+ "InitSystemTray",
+ {
+ "tray_id": trayId,
+ "title": title,
+ "iconpath": iconPath,
+ "tooltip": toolTip,
+ "is_template": isTemplate,
+ },
+ );
+ }
+
+ @override
+ Future setSystemTrayInfo({
+ String? title,
+ String? iconPath,
+ String? toolTip,
+ bool isTemplate = false,
+ }) async {
+ return await _trayChannel.invokeMethod(
+ "SetSystemTrayInfo",
+ {
+ "title": title,
+ "iconpath": iconPath,
+ "tooltip": toolTip,
+ "is_template": isTemplate,
+ },
+ );
+ }
+
+ @override
+ Future setContextMenu(int menuId) async {
+ await _trayChannel.invokeMethod("SetContextMenu", menuId);
+ }
+
+ @override
+ Future popUpContextMenu() async {
+ await _trayChannel.invokeMethod("PopupContextMenu");
+ }
+
+ @override
+ Future getTitle() async {
+ return await _trayChannel.invokeMethod("GetTitle");
+ }
+
+ @override
+ Future destroySystemTray() async {
+ await _trayChannel.invokeMethod("DestroySystemTray");
+ }
+
+ @override
+ void registerSystemTrayEventHandler(
+ void Function(String eventName) callback) {
+ _systemTrayEventCallback = callback;
+ }
+
+ @override
+ Future buildMenu(int menuId, List menus) async {
+ try {
+ return await _menuManagerChannel
+ .invokeMethod("CreateContextMenu", {
+ "menu_id": menuId,
+ "menu_list": menus.map((e) => e.toJson()).toList(),
+ });
+ } catch (e) {
+ return false;
+ }
+ }
+
+ @override
+ Future setMenuItemLabel(
+ int menuId, int menuItemId, String label) async {
+ await _menuManagerChannel.invokeMethod("SetLabel", {
+ "menu_id": menuId,
+ "menu_item_id": menuItemId,
+ "label": label,
+ });
+ }
+
+ @override
+ Future setMenuItemImage(
+ int menuId, int menuItemId, String imageAbsolutePath) async {
+ await _menuManagerChannel.invokeMethod("SetImage", {
+ "menu_id": menuId,
+ "menu_item_id": menuItemId,
+ "image": imageAbsolutePath,
+ });
+ }
+
+ @override
+ Future setMenuItemEnable(
+ int menuId, int menuItemId, bool enabled) async {
+ await _menuManagerChannel.invokeMethod("SetEnable", {
+ "menu_id": menuId,
+ "menu_item_id": menuItemId,
+ "enabled": enabled,
+ });
+ }
+
+ @override
+ Future setMenuItemCheck(
+ int menuId, int menuItemId, bool checked) async {
+ await _menuManagerChannel.invokeMethod("SetCheck", {
+ "menu_id": menuId,
+ "menu_item_id": menuItemId,
+ "checked": checked,
+ });
+ }
+
+ @override
+ void registerMenuItemSelectedCallback(
+ void Function(int menuId, int menuItemId) callback) {
+ _menuItemSelectedCallback = callback;
+ }
+}
diff --git a/lib/src/system_tray_platform.dart b/lib/src/system_tray_platform.dart
new file mode 100644
index 0000000..bb42906
--- /dev/null
+++ b/lib/src/system_tray_platform.dart
@@ -0,0 +1,62 @@
+import 'dart:async';
+import 'dart:io';
+
+import 'package:flutter/services.dart';
+
+import 'menu.dart';
+import 'menu_item.dart';
+import 'system_tray_linux.dart';
+import 'system_tray_method_channel.dart';
+import 'constants.dart';
+
+abstract class SystemTrayPlatform {
+ static SystemTrayPlatform? _instance;
+
+ static SystemTrayPlatform get instance {
+ if (_instance != null) return _instance!;
+ if (Platform.isLinux) {
+ _instance = SystemTrayLinux();
+ } else {
+ _instance = SystemTrayMethodChannel();
+ }
+ return _instance!;
+ }
+
+ Future initAppWindow();
+ Future showAppWindow();
+ Future hideAppWindow();
+ Future closeAppWindow();
+
+ Future initSystemTray({
+ required String trayId,
+ required String iconPath,
+ String? title,
+ String? toolTip,
+ bool isTemplate = false,
+ });
+
+ Future setSystemTrayInfo({
+ String? title,
+ String? iconPath,
+ String? toolTip,
+ bool isTemplate = false,
+ });
+
+ Future setContextMenu(int menuId);
+ Future popUpContextMenu();
+ Future getTitle();
+ Future destroySystemTray();
+
+ void registerSystemTrayEventHandler(void Function(String eventName) callback);
+
+ Future buildMenu(int menuId, List menus);
+
+ Future setMenuItemLabel(int menuId, int menuItemId, String label);
+ Future setMenuItemImage(
+ int menuId, int menuItemId, String imageAbsolutePath);
+ Future setMenuItemEnable(int menuId, int menuItemId, bool enabled);
+ Future setMenuItemCheck(int menuId, int menuItemId, bool checked);
+
+ void registerMenuItemSelectedCallback(
+ void Function(int menuId, int menuItemId) callback);
+}
diff --git a/lib/src/tray.dart b/lib/src/tray.dart
index b1ed149..dcae6d0 100644
--- a/lib/src/tray.dart
+++ b/lib/src/tray.dart
@@ -4,37 +4,19 @@ import 'package:flutter/services.dart';
import 'package:uuid/uuid.dart';
import 'menu.dart';
+import 'system_tray_platform.dart';
import 'utils.dart';
-const String _kChannelName = "flutter/system_tray/tray";
-
-const String _kInitSystemTray = "InitSystemTray";
-const String _kSetSystemTrayInfo = "SetSystemTrayInfo";
-const String _kSetContextMenu = "SetContextMenu";
-const String _kPopupContextMenu = "PopupContextMenu";
-const String _kGetTitle = "GetTitle";
-const String _kDestroySystemTray = "DestroySystemTray";
-
-const String _kSystemTrayEventCallbackMethod = 'SystemTrayEventCallback';
-
-const String _kTrayIdKey = "tray_id";
-const String _kTitleKey = "title";
-const String _kIconPathKey = "iconpath";
-const String _kToolTipKey = "tooltip";
-const String _kIsTemplateKey = "is_template";
-
/// A callback provided to [SystemTray] to handle system tray click event.
typedef SystemTrayEventCallback = void Function(String eventName);
/// Representation of system tray
class SystemTray {
SystemTray() {
- _platformChannel.setMethodCallHandler(_callbackHandler);
+ SystemTrayPlatform.instance
+ .registerSystemTrayEventHandler(_callbackHandler);
}
- static const MethodChannel _platformChannel = MethodChannel(_kChannelName);
-
- ///
SystemTrayEventCallback? _systemTrayEventCallback;
/// Show a SystemTray icon
@@ -44,17 +26,13 @@ class SystemTray {
String? toolTip,
bool isTemplate = false,
}) async {
- bool value = await _platformChannel.invokeMethod(
- _kInitSystemTray,
- {
- _kTrayIdKey: const Uuid().v1(),
- _kTitleKey: title,
- _kIconPathKey: await Utils.getIcon(iconPath),
- _kToolTipKey: toolTip,
- _kIsTemplateKey: isTemplate,
- },
+ return await SystemTrayPlatform.instance.initSystemTray(
+ trayId: const Uuid().v1(),
+ iconPath: (await Utils.getIcon(iconPath)) ?? '',
+ title: title,
+ toolTip: toolTip,
+ isTemplate: isTemplate,
);
- return value;
}
/// Set system info info
@@ -64,16 +42,12 @@ class SystemTray {
String? toolTip,
bool isTemplate = false,
}) async {
- bool value = await _platformChannel.invokeMethod(
- _kSetSystemTrayInfo,
- {
- _kTitleKey: title,
- _kIconPathKey: await Utils.getIcon(iconPath),
- _kToolTipKey: toolTip,
- _kIsTemplateKey: isTemplate,
- },
+ return await SystemTrayPlatform.instance.setSystemTrayInfo(
+ title: title,
+ iconPath: await Utils.getIcon(iconPath),
+ toolTip: toolTip,
+ isTemplate: isTemplate,
);
- return value;
}
/// (Windows\macOS\Linux) Sets the image associated with this tray icon
@@ -93,7 +67,7 @@ class SystemTray {
/// (macOS) Returns string - the title displayed next to the tray icon in the status bar
Future getTitle() async {
- return await _platformChannel.invokeMethod(_kGetTitle);
+ return await SystemTrayPlatform.instance.getTitle();
}
/// Sets the native application menu to [menus].
@@ -102,13 +76,13 @@ 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 {
- await _platformChannel.invokeMethod(_kSetContextMenu, menu.menuId);
+ await SystemTrayPlatform.instance.setContextMenu(menu.menuId);
}
/// Pop up the context menu.
///
Future popUpContextMenu() async {
- await _platformChannel.invokeMethod(_kPopupContextMenu);
+ await SystemTrayPlatform.instance.popUpContextMenu();
}
/// register listener for system tray event.
@@ -116,16 +90,13 @@ class SystemTray {
_systemTrayEventCallback = callback;
}
- Future _callbackHandler(MethodCall methodCall) async {
- if (methodCall.method == _kSystemTrayEventCallbackMethod) {
- if (_systemTrayEventCallback != null) {
- final String eventName = methodCall.arguments;
- _systemTrayEventCallback!(eventName);
- }
+ void _callbackHandler(String eventName) {
+ if (_systemTrayEventCallback != null) {
+ _systemTrayEventCallback!(eventName);
}
}
Future destroy() async {
- await _platformChannel.invokeMethod(_kDestroySystemTray);
+ await SystemTrayPlatform.instance.destroySystemTray();
}
}
diff --git a/lib/system_tray.dart b/lib/system_tray.dart
index e4c7d62..a569a37 100644
--- a/lib/system_tray.dart
+++ b/lib/system_tray.dart
@@ -3,3 +3,4 @@ export 'src/app_window.dart';
export 'src/menu.dart';
export 'src/menu_item.dart';
export 'src/constants.dart';
+export 'src/system_tray_linux_plugin.dart';
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