From dd9f71236d14493e3dad01a7db2f9193b1fe93a2 Mon Sep 17 00:00:00 2001 From: LeoLu <927195249@qq.com> Date: Wed, 24 Jan 2024 23:34:27 +0800 Subject: [PATCH 01/50] add who using readme (#432) --- README-ZH.md | 1 + README.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README-ZH.md b/README-ZH.md index 5ef721c3..e816f4ce 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -508,6 +508,7 @@ class _HomePageState extends State with WindowListener { ## 谁在用使用它? +- [Airclap](https://airclap.app/) - 任何文件,任意设备,随意发送。简单好用的跨平台高速文件传输APP。 - [AuthPass](https://authpass.app/) - 基于Flutter的密码管理器,适用于所有平台。兼容Keepass 2.x(kdbx 3.x)。 - [Biyi (比译)](https://biyidev.com/) - 一个便捷的翻译和词典应用程序。 - [BlueBubbles](https://github.com/BlueBubblesApp/bluebubbles-app) - BlueBubbles is an ecosystem of apps bringing iMessage to Android, Windows, and Linux diff --git a/README.md b/README.md index e2a4a4f0..d892880b 100644 --- a/README.md +++ b/README.md @@ -509,6 +509,7 @@ class _HomePageState extends State with WindowListener { ## Who's using it? +- [Airclap](https://airclap.app/) - Send any file to any device. cross platform, ultra fast and easy to use. - [AuthPass](https://authpass.app/) - Password Manager based on Flutter for all platforms. Keepass 2.x (kdbx 3.x) compatible. - [Biyi (比译)](https://biyidev.com/) - A convenient translation and dictionary app written in dart / Flutter. - [BlueBubbles](https://github.com/BlueBubblesApp/bluebubbles-app) - BlueBubbles is an ecosystem of apps bringing iMessage to Android, Windows, and Linux From 9d2a78fa4dfaee00fa8f4d1e6d35d6b90a6ea461 Mon Sep 17 00:00:00 2001 From: lunxinfeng <892452936@qq.com> Date: Sun, 28 Jan 2024 16:33:08 +0800 Subject: [PATCH 02/50] setAlignment support custom Alignment, like Alignment(-0.5, -0.5). (#424) --- lib/src/utils/calc_window_position.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/src/utils/calc_window_position.dart b/lib/src/utils/calc_window_position.dart index 1a3ca7af..907c00df 100644 --- a/lib/src/utils/calc_window_position.dart +++ b/lib/src/utils/calc_window_position.dart @@ -79,6 +79,13 @@ Future calcWindowPosition( visibleStartX + visibleWidth - windowSize.width, visibleStartY + (visibleHeight - windowSize.height), ); + } else { + final left = (visibleWidth - windowSize.width) / 2 + alignment.x * ((visibleWidth - windowSize.width) / 2); + final top = (visibleHeight - windowSize.height) / 2 + alignment.y * ((visibleHeight - windowSize.height) / 2); + position = Offset( + visibleStartX + left, + visibleStartY + top, + ); } return position; } From b3b1417a39cd8eee2f124d5935f646363337b5c7 Mon Sep 17 00:00:00 2001 From: Damy Wise Date: Sun, 28 Jan 2024 15:35:23 +0700 Subject: [PATCH 03/50] fix #396: (windows) fullscreen/unfullscreen events, disable minimize on fullscreen (#409) - Fixed a bug where the newest implementation of fullscreen/unfullscreen broke the window listener events. - Disables minimize when window is fullscreen. This mimics the behavior of Chromium. --- windows/window_manager.cpp | 75 ++++++++++++++-------------- windows/window_manager_plugin.cpp | 82 +++++++++++++++---------------- 2 files changed, 78 insertions(+), 79 deletions(-) diff --git a/windows/window_manager.cpp b/windows/window_manager.cpp index 0c2b06bf..3484176d 100644 --- a/windows/window_manager.cpp +++ b/windows/window_manager.cpp @@ -323,6 +323,10 @@ bool WindowManager::IsMinimized() { } void WindowManager::Minimize() { + if (IsFullScreen()) { // Like chromium, we don't want to minimize fullscreen + // windows + return; + } HWND mainWindow = GetMainWindow(); WINDOWPLACEMENT windowPlacement; GetWindowPlacement(mainWindow, &windowPlacement); @@ -352,7 +356,7 @@ int WindowManager::IsDocked() { double WindowManager::GetDpiForHwnd(HWND hWnd) { auto monitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); - UINT newDpiX = 96; // Default values + UINT newDpiX = 96; // Default values UINT newDpiY = 96; // Dynamically load shcore.dll and get the GetDpiForMonitor function address @@ -362,12 +366,13 @@ double WindowManager::GetDpiForHwnd(HWND hWnd) { typedef HRESULT (*GetDpiForMonitor)(HMONITOR, int, UINT*, UINT*); GetDpiForMonitor GetDpiForMonitorFunc = - (GetDpiForMonitor)GetProcAddress(shcore, "GetDpiForMonitor"); + (GetDpiForMonitor)GetProcAddress(shcore, "GetDpiForMonitor"); if (GetDpiForMonitorFunc) { // Use the loaded function if available const int MDT_EFFECTIVE_DPI = 0; - if (FAILED(GetDpiForMonitorFunc(monitor, MDT_EFFECTIVE_DPI, &newDpiX, &newDpiY))) { + if (FAILED(GetDpiForMonitorFunc(monitor, MDT_EFFECTIVE_DPI, &newDpiX, + &newDpiY))) { // If it fails, set the default values again newDpiX = 96; newDpiY = 96; @@ -375,8 +380,8 @@ double WindowManager::GetDpiForHwnd(HWND hWnd) { } FreeLibrary(shcore); } - return ((double) newDpiX); -} + return ((double)newDpiX); +} void WindowManager::Dock(const flutter::EncodableMap& args) { HWND mainWindow = GetMainWindow(); @@ -468,7 +473,6 @@ void PASCAL WindowManager::AppBarQuerySetPos(HWND hwnd, } BOOL WindowManager::RegisterAccessBar(HWND hwnd, BOOL fRegister) { - APPBARDATA abd; // Specify the structure size and handle to the appbar. @@ -550,8 +554,8 @@ void WindowManager::SetFullScreen(const flutter::EncodableMap& args) { // Previously inspired by how Chromium does this // https://src.chromium.org/viewvc/chrome/trunk/src/ui/views/win/fullscreen_handler.cc?revision=247204&view=markup - // Instead, we use a modified implementation of how the media_kit package implements this - // (we got permission from the author, I believe) + // Instead, we use a modified implementation of how the media_kit package + // implements this (we got permission from the author, I believe) // https://github.com/alexmercerind/media_kit/blob/1226bcff36eab27cb17d60c33e9c15ca489c1f06/media_kit_video/windows/utils.cc // Save current window state if not already fullscreen. @@ -563,26 +567,31 @@ void WindowManager::SetFullScreen(const flutter::EncodableMap& args) { g_title_bar_style_before_fullscreen = title_bar_style_; } - if (isFullScreen) { + g_is_window_fullscreen = isFullScreen; + + if (isFullScreen) { // Set to fullscreen ::SendMessage(mainWindow, WM_SYSCOMMAND, SC_MAXIMIZE, 0); if (!is_frameless_) { - auto monitor = MONITORINFO{}; - auto placement = WINDOWPLACEMENT{}; - monitor.cbSize = sizeof(MONITORINFO); - placement.length = sizeof(WINDOWPLACEMENT); - ::GetWindowPlacement(mainWindow, &placement); - ::GetMonitorInfo(::MonitorFromWindow(mainWindow, MONITOR_DEFAULTTONEAREST), - &monitor); - ::SetWindowLongPtr(mainWindow, GWL_STYLE, g_style_before_fullscreen & ~WS_OVERLAPPEDWINDOW); - ::SetWindowPos(mainWindow, HWND_TOP, monitor.rcMonitor.left, - monitor.rcMonitor.top, monitor.rcMonitor.right - monitor.rcMonitor.left, - monitor.rcMonitor.bottom - monitor.rcMonitor.top, - SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + auto monitor = MONITORINFO{}; + auto placement = WINDOWPLACEMENT{}; + monitor.cbSize = sizeof(MONITORINFO); + placement.length = sizeof(WINDOWPLACEMENT); + ::GetWindowPlacement(mainWindow, &placement); + ::GetMonitorInfo( + ::MonitorFromWindow(mainWindow, MONITOR_DEFAULTTONEAREST), &monitor); + ::SetWindowLongPtr(mainWindow, GWL_STYLE, + g_style_before_fullscreen & ~WS_OVERLAPPEDWINDOW); + ::SetWindowPos(mainWindow, HWND_TOP, monitor.rcMonitor.left, + monitor.rcMonitor.top, + monitor.rcMonitor.right - monitor.rcMonitor.left, + monitor.rcMonitor.bottom - monitor.rcMonitor.top, + SWP_NOOWNERZORDER | SWP_FRAMECHANGED); } - } else { + } else { // Restore from fullscreen if (!g_maximized_before_fullscreen) Restore(); - ::SetWindowLongPtr(mainWindow, GWL_STYLE, g_style_before_fullscreen | WS_OVERLAPPEDWINDOW); + ::SetWindowLongPtr(mainWindow, GWL_STYLE, + g_style_before_fullscreen | WS_OVERLAPPEDWINDOW); if (::IsZoomed(mainWindow)) { // Refresh the parent mainWindow. ::SetWindowPos(mainWindow, nullptr, 0, 0, 0, 0, @@ -590,8 +599,8 @@ void WindowManager::SetFullScreen(const flutter::EncodableMap& args) { SWP_FRAMECHANGED); auto rect = RECT{}; ::GetClientRect(mainWindow, &rect); - auto flutter_view = - ::FindWindowEx(mainWindow, nullptr, kFlutterViewWindowClassName, nullptr); + auto flutter_view = ::FindWindowEx(mainWindow, nullptr, + kFlutterViewWindowClassName, nullptr); ::SetWindowPos(flutter_view, nullptr, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOZORDER); @@ -606,8 +615,6 @@ void WindowManager::SetFullScreen(const flutter::EncodableMap& args) { SWP_NOACTIVATE | SWP_NOZORDER); } } - - g_is_window_fullscreen = isFullScreen; } void WindowManager::SetAspectRatio(const flutter::EncodableMap& args) { @@ -838,22 +845,16 @@ void WindowManager::SetAlwaysOnTop(const flutter::EncodableMap& args) { } bool WindowManager::IsAlwaysOnBottom() { - return is_always_on_bottom_; + return is_always_on_bottom_; } void WindowManager::SetAlwaysOnBottom(const flutter::EncodableMap& args) { is_always_on_bottom_ = std::get(args.at(flutter::EncodableValue("isAlwaysOnBottom"))); - SetWindowPos( - GetMainWindow(), - is_always_on_bottom_ ? HWND_BOTTOM : HWND_NOTOPMOST, - 0, - 0, - 0, - 0, - SWP_NOMOVE | SWP_NOSIZE - ); + SetWindowPos(GetMainWindow(), + is_always_on_bottom_ ? HWND_BOTTOM : HWND_NOTOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE); } std::string WindowManager::GetTitle() { diff --git a/windows/window_manager_plugin.cpp b/windows/window_manager_plugin.cpp index acb2aa43..75050250 100644 --- a/windows/window_manager_plugin.cpp +++ b/windows/window_manager_plugin.cpp @@ -109,13 +109,15 @@ std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, std::optional result = std::nullopt; if (message == WM_DPICHANGED) { - window_manager->pixel_ratio_ = (float) LOWORD(wParam) / USER_DEFAULT_SCREEN_DPI; + window_manager->pixel_ratio_ = + (float)LOWORD(wParam) / USER_DEFAULT_SCREEN_DPI; } if (wParam && message == WM_NCCALCSIZE) { - if (window_manager->IsFullScreen() && window_manager->title_bar_style_ != "normal") { + if (window_manager->IsFullScreen() && + window_manager->title_bar_style_ != "normal") { if (window_manager->is_frameless_) { - NCCALCSIZE_PARAMS* sz = reinterpret_cast(lParam); + NCCALCSIZE_PARAMS* sz = reinterpret_cast(lParam); sz->rgrc[0].left += 8; sz->rgrc[0].top += 8; sz->rgrc[0].right -= 8; @@ -166,21 +168,17 @@ std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, MINMAXINFO* info = reinterpret_cast(lParam); // For the special "unconstrained" values, leave the defaults. if (window_manager->minimum_size_.x != 0) - info->ptMinTrackSize.x = - static_cast (window_manager->minimum_size_.x * - window_manager->pixel_ratio_); + info->ptMinTrackSize.x = static_cast( + window_manager->minimum_size_.x * window_manager->pixel_ratio_); if (window_manager->minimum_size_.y != 0) - info->ptMinTrackSize.y = - static_cast (window_manager->minimum_size_.y * - window_manager->pixel_ratio_); + info->ptMinTrackSize.y = static_cast( + window_manager->minimum_size_.y * window_manager->pixel_ratio_); if (window_manager->maximum_size_.x != -1) - info->ptMaxTrackSize.x = - static_cast (window_manager->maximum_size_.x * - window_manager->pixel_ratio_); + info->ptMaxTrackSize.x = static_cast( + window_manager->maximum_size_.x * window_manager->pixel_ratio_); if (window_manager->maximum_size_.y != -1) - info->ptMaxTrackSize.y = - static_cast (window_manager->maximum_size_.y * - window_manager->pixel_ratio_); + info->ptMaxTrackSize.y = static_cast( + window_manager->maximum_size_.y * window_manager->pixel_ratio_); result = 0; } else if (message == WM_NCACTIVATE) { if (wParam == TRUE) { @@ -268,31 +266,31 @@ std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, rect->bottom = bottom; } } else if (message == WM_SIZE) { - LONG_PTR gwlStyle = - GetWindowLongPtr(window_manager->GetMainWindow(), GWL_STYLE); - if ((gwlStyle & (WS_CAPTION | WS_THICKFRAME)) == 0 && - wParam == SIZE_MAXIMIZED) { + if (window_manager->IsFullScreen() && wParam == SIZE_MAXIMIZED && + window_manager->last_state != STATE_FULLSCREEN_ENTERED) { _EmitEvent("enter-full-screen"); window_manager->last_state = STATE_FULLSCREEN_ENTERED; - } else if (window_manager->last_state == STATE_FULLSCREEN_ENTERED && - wParam == SIZE_RESTORED) { + } else if (!window_manager->IsFullScreen() && wParam == SIZE_RESTORED && + window_manager->last_state == STATE_FULLSCREEN_ENTERED) { window_manager->ForceChildRefresh(); _EmitEvent("leave-full-screen"); window_manager->last_state = STATE_NORMAL; - } else if (wParam == SIZE_MAXIMIZED) { - _EmitEvent("maximize"); - window_manager->last_state = STATE_MAXIMIZED; - } else if (wParam == SIZE_MINIMIZED) { - _EmitEvent("minimize"); - window_manager->last_state = STATE_MINIMIZED; - return 0; - } else if (wParam == SIZE_RESTORED) { - if (window_manager->last_state == STATE_MAXIMIZED) { - _EmitEvent("unmaximize"); - window_manager->last_state = STATE_NORMAL; - } else if (window_manager->last_state == STATE_MINIMIZED) { - _EmitEvent("restore"); - window_manager->last_state = STATE_NORMAL; + } else if (window_manager->last_state != STATE_FULLSCREEN_ENTERED) { + if (wParam == SIZE_MAXIMIZED) { + _EmitEvent("maximize"); + window_manager->last_state = STATE_MAXIMIZED; + } else if (wParam == SIZE_MINIMIZED) { + _EmitEvent("minimize"); + window_manager->last_state = STATE_MINIMIZED; + return 0; + } else if (wParam == SIZE_RESTORED) { + if (window_manager->last_state == STATE_MAXIMIZED) { + _EmitEvent("unmaximize"); + window_manager->last_state = STATE_NORMAL; + } else if (window_manager->last_state == STATE_MINIMIZED) { + _EmitEvent("restore"); + window_manager->last_state = STATE_NORMAL; + } } } } else if (message == WM_CLOSE) { @@ -308,13 +306,13 @@ std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, } } else if (message == WM_WINDOWPOSCHANGED) { if (window_manager->IsAlwaysOnBottom()) { - const flutter::EncodableMap& args = { - {flutter::EncodableValue("isAlwaysOnBottom"), - flutter::EncodableValue(true)}}; - window_manager->SetAlwaysOnBottom(args); - } + const flutter::EncodableMap& args = { + {flutter::EncodableValue("isAlwaysOnBottom"), + flutter::EncodableValue(true)}}; + window_manager->SetAlwaysOnBottom(args); + } } - + return result; } @@ -561,7 +559,7 @@ void WindowManagerPlugin::HandleMethodCall( } else { result->NotImplemented(); } - } +} } // namespace From ef786b1574455ca141cea899ca2db788731163d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Sun, 28 Jan 2024 17:07:11 +0800 Subject: [PATCH 04/50] chore: Replace flutter_lints to mostly_reasonable_lints --- CHANGELOG.md | 6 ++ README-ZH.md | 2 +- README.md | 2 +- analysis_options.yaml | 23 +----- example/analysis_options.yaml | 30 +------ .../integration_test/window_manager_test.dart | 56 +++++++++---- example/lib/main.dart | 11 ++- example/lib/pages/home.dart | 14 ++-- example/lib/utilities/config.dart | 12 +-- example/pubspec.lock | 80 +++++++++---------- example/pubspec.yaml | 69 ++-------------- lib/src/utils/calc_window_position.dart | 6 +- lib/src/widgets/drag_to_move_area.dart | 4 +- lib/src/widgets/drag_to_resize_area.dart | 4 +- lib/src/widgets/virtual_window_frame.dart | 6 +- lib/src/widgets/window_caption.dart | 6 +- lib/src/widgets/window_caption_button.dart | 29 +++---- lib/src/window_listener.dart | 2 +- lib/src/window_manager.dart | 6 +- pubspec.lock | 70 ++++++++-------- pubspec.yaml | 6 +- 21 files changed, 184 insertions(+), 260 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3f63bcf..622a6eba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +### 0.3.8 + +* Updates minimum supported SDK version to Flutter 3.3/Dart 3.0. +* [windows] fix #396 fullscreen/unfullscreen events, disable minimize on fullscreen #409 +* setAlignment support custom Alignment #424 + ### 0.3.7 * [windows] Wrong window position in fullscreen mode with external monitor #405 diff --git a/README-ZH.md b/README-ZH.md index e816f4ce..43405fb2 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -145,7 +145,7 @@ ```yaml dependencies: - window_manager: ^0.3.7 + window_manager: ^0.3.8 ``` 或 diff --git a/README.md b/README.md index d892880b..8089d7d8 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ Add this to your package's `pubspec.yaml` file: ```yaml dependencies: - window_manager: ^0.3.7 + window_manager: ^0.3.8 ``` Or diff --git a/analysis_options.yaml b/analysis_options.yaml index ef8a58c1..095b1d67 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,22 +1 @@ -include: package:flutter_lints/flutter.yaml - -linter: - rules: - ## Error Rules - - always_use_package_imports - ## Style rules - - directives_ordering - - eol_at_end_of_file - - file_names - - flutter_style_todos - - library_names - - library_prefixes - - prefer_is_empty - - prefer_is_not_empty - - prefer_is_not_operator - - prefer_null_aware_method_calls - - prefer_single_quotes - - sort_constructors_first - - sort_unnamed_constructors_first - ## Pub rules - - sort_pub_dependencies +include: package:mostly_reasonable_lints/flutter.yaml diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index 61b6c4de..095b1d67 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -1,29 +1 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml - -linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. - rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options +include: package:mostly_reasonable_lints/flutter.yaml diff --git a/example/integration_test/window_manager_test.dart b/example/integration_test/window_manager_test.dart index 0dbade0e..5901027e 100644 --- a/example/integration_test/window_manager_test.dart +++ b/example/integration_test/window_manager_test.dart @@ -20,13 +20,19 @@ Future main() async { ); testWidgets('getBounds', (tester) async { - expect(await windowManager.getBounds(), - isA().having((r) => r.size, 'size', const Size(640, 480))); + expect( + await windowManager.getBounds(), + isA().having((r) => r.size, 'size', const Size(640, 480)), + ); }); - testWidgets('isAlwaysOnBottom', (tester) async { - expect(await windowManager.isAlwaysOnBottom(), isFalse); - }, skip: Platform.isMacOS || Platform.isWindows); + testWidgets( + 'isAlwaysOnBottom', + (tester) async { + expect(await windowManager.isAlwaysOnBottom(), isFalse); + }, + skip: Platform.isMacOS || Platform.isWindows, + ); testWidgets('isAlwaysOnTop', (tester) async { expect(await windowManager.isAlwaysOnTop(), isFalse); @@ -44,9 +50,13 @@ Future main() async { expect(await windowManager.isFullScreen(), isFalse); }); - testWidgets('hasShadow', (tester) async { - expect(await windowManager.hasShadow(), isTrue); - }, skip: Platform.isLinux); + testWidgets( + 'hasShadow', + (tester) async { + expect(await windowManager.hasShadow(), isTrue); + }, + skip: Platform.isLinux, + ); testWidgets('isMaximizable', (tester) async { expect(await windowManager.isMaximizable(), isTrue); @@ -56,17 +66,25 @@ Future main() async { expect(await windowManager.isMaximized(), isFalse); }); - testWidgets('isMinimizable', (tester) async { - expect(await windowManager.isMinimizable(), isTrue); - }, skip: Platform.isMacOS); + testWidgets( + 'isMinimizable', + (tester) async { + expect(await windowManager.isMinimizable(), isTrue); + }, + skip: Platform.isMacOS, + ); testWidgets('isMinimized', (tester) async { expect(await windowManager.isMinimized(), isFalse); }); - testWidgets('isMovable', (tester) async { - expect(await windowManager.isMovable(), isTrue); - }, skip: Platform.isLinux || Platform.isWindows); + testWidgets( + 'isMovable', + (tester) async { + expect(await windowManager.isMovable(), isTrue); + }, + skip: Platform.isLinux || Platform.isWindows, + ); testWidgets('getOpacity', (tester) async { expect(await windowManager.getOpacity(), 1.0); @@ -88,9 +106,13 @@ Future main() async { expect(await windowManager.getSize(), const Size(640, 480)); }); - testWidgets('isSkipTaskbar', (tester) async { - expect(await windowManager.isSkipTaskbar(), isFalse); - }, skip: Platform.isWindows); + testWidgets( + 'isSkipTaskbar', + (tester) async { + expect(await windowManager.isSkipTaskbar(), isFalse); + }, + skip: Platform.isWindows, + ); testWidgets('getTitle', (tester) async { expect(await windowManager.getTitle(), 'window_manager_test'); diff --git a/example/lib/main.dart b/example/lib/main.dart index 1f18072b..a52a2d64 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,10 +1,9 @@ -import 'package:flutter/material.dart'; import 'package:bot_toast/bot_toast.dart'; +import 'package:flutter/material.dart'; import 'package:window_manager/window_manager.dart'; - -import './pages/home.dart'; -import 'themes/themes.dart'; -import 'utilities/utilities.dart'; +import 'package:window_manager_example/pages/home.dart'; +import 'package:window_manager_example/themes/themes.dart'; +import 'package:window_manager_example/utilities/utilities.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -27,7 +26,7 @@ void main() async { } class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); + const MyApp({super.key}); @override State createState() => _MyAppState(); diff --git a/example/lib/pages/home.dart b/example/lib/pages/home.dart index 75c3c1e8..f50c136d 100644 --- a/example/lib/pages/home.dart +++ b/example/lib/pages/home.dart @@ -8,8 +8,7 @@ import 'package:flutter/material.dart'; import 'package:preference_list/preference_list.dart'; import 'package:tray_manager/tray_manager.dart'; import 'package:window_manager/window_manager.dart'; - -import '../utilities/utilities.dart'; +import 'package:window_manager_example/utilities/utilities.dart'; const _kSizes = [ Size(400, 400), @@ -28,7 +27,7 @@ const _kMaxSizes = [ ]; class HomePage extends StatefulWidget { - const HomePage({Key? key}) : super(key: key); + const HomePage({super.key}); @override State createState() => _HomePageState(); @@ -73,7 +72,7 @@ class _HomePageState extends State with TrayListener, WindowListener { super.dispose(); } - void _init() async { + Future _init() async { await trayManager.setIcon( Platform.isWindows ? 'images/tray_icon_original.ico' @@ -100,7 +99,7 @@ class _HomePageState extends State with TrayListener, WindowListener { setState(() {}); } - void _handleSetIcon(String iconType) async { + Future _handleSetIcon(String iconType) async { _iconType = iconType; String iconPath = Platform.isWindows ? 'images/tray_icon.ico' : 'images/tray_icon.png'; @@ -980,7 +979,8 @@ class _HomePageState extends State with TrayListener, WindowListener { child: const Text('DragToResizeArea'), onTap: () { BotToast.showText( - text: 'DragToResizeArea example'); + text: 'DragToResizeArea example', + ); }, ), ), @@ -1026,7 +1026,7 @@ class _HomePageState extends State with TrayListener, WindowListener { } @override - void onTrayMenuItemClick(MenuItem menuItem) async { + Future onTrayMenuItemClick(MenuItem menuItem) async { switch (menuItem.key) { case 'show_window': await windowManager.focus(); diff --git a/example/lib/utilities/config.dart b/example/lib/utilities/config.dart index b7014219..632f01b9 100644 --- a/example/lib/utilities/config.dart +++ b/example/lib/utilities/config.dart @@ -2,7 +2,7 @@ import 'dart:collection'; import 'package:flutter/material.dart'; -class _ListenerEntry extends LinkedListEntry<_ListenerEntry> { +final class _ListenerEntry extends LinkedListEntry<_ListenerEntry> { _ListenerEntry(this.listener); final VoidCallback listener; } @@ -42,10 +42,12 @@ class _ConfigChangeNotifier implements Listenable { try { if (entry.list != null) entry.listener(); } catch (exception, stack) { - FlutterError.reportError(FlutterErrorDetails( - exception: exception, - stack: stack, - )); + FlutterError.reportError( + FlutterErrorDetails( + exception: exception, + stack: stack, + ), + ); } } } diff --git a/example/pubspec.lock b/example/pubspec.lock index 0bae7635..8d140490 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" cupertino_icons: dependency: "direct main" description: @@ -83,14 +83,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 - url: "https://pub.dev" - source: hosted - version: "2.0.3" flutter_test: dependency: "direct dev" description: flutter @@ -106,38 +98,30 @@ packages: description: flutter source: sdk version: "0.0.0" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" matcher: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" menu_base: dependency: transitive description: @@ -150,10 +134,18 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" + mostly_reasonable_lints: + dependency: "direct dev" + description: + name: mostly_reasonable_lints + sha256: c61cc6b211f54188eef15e0ad7c9b00e8c001478ac253a52036ef7391b532b7c + url: "https://pub.dev" + source: hosted + version: "0.1.1" path: dependency: transitive description: @@ -166,10 +158,10 @@ packages: dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.2" preference_list: dependency: "direct main" description: @@ -211,26 +203,26 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -259,10 +251,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.1" tray_manager: dependency: "direct main" description: @@ -283,10 +275,18 @@ packages: dependency: transitive description: name: vm_service - sha256: f6deed8ed625c52864792459709183da231ebf66ff0cf09e69b573227c377efe + sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 + url: "https://pub.dev" + source: hosted + version: "11.10.0" + web: + dependency: transitive + description: + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "11.3.0" + version: "0.3.0" webdriver: dependency: transitive description: @@ -301,7 +301,7 @@ packages: path: ".." relative: true source: path - version: "0.3.7" + version: "0.3.8" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.3.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index df4ab78d..e5053943 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,83 +1,28 @@ name: window_manager_example description: Demonstrates how to use the window_manager plugin. - -# The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: "none" environment: - sdk: ">=2.18.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: + bot_toast: ^4.0.1 + cupertino_icons: ^1.0.2 flutter: sdk: flutter - - window_manager: - # When depending on this package from a real application you should use: - # window_manager: ^x.y.z - # See https://dart.dev/tools/pub/dependencies#version-constraints - # The example app is bundled with the plugin so we use a path dependency on - # the parent directory to use the current plugin's version. - path: ../ - - bot_toast: ^4.0.1 preference_list: ^0.0.1 tray_manager: ^0.2.0 - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 + window_manager: + path: ../ dev_dependencies: flutter_test: sdk: flutter integration_test: sdk: flutter + mostly_reasonable_lints: ^0.1.1 - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. - flutter_lints: ^2.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true - - # To add assets to your application, add an assets section, like this: assets: - images/ - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages diff --git a/lib/src/utils/calc_window_position.dart b/lib/src/utils/calc_window_position.dart index 907c00df..4e9e3431 100644 --- a/lib/src/utils/calc_window_position.dart +++ b/lib/src/utils/calc_window_position.dart @@ -80,8 +80,10 @@ Future calcWindowPosition( visibleStartY + (visibleHeight - windowSize.height), ); } else { - final left = (visibleWidth - windowSize.width) / 2 + alignment.x * ((visibleWidth - windowSize.width) / 2); - final top = (visibleHeight - windowSize.height) / 2 + alignment.y * ((visibleHeight - windowSize.height) / 2); + final left = (visibleWidth - windowSize.width) / 2 + + alignment.x * ((visibleWidth - windowSize.width) / 2); + final top = (visibleHeight - windowSize.height) / 2 + + alignment.y * ((visibleHeight - windowSize.height) / 2); position = Offset( visibleStartX + left, visibleStartY + top, diff --git a/lib/src/widgets/drag_to_move_area.dart b/lib/src/widgets/drag_to_move_area.dart index e63b8f0d..9c49b205 100644 --- a/lib/src/widgets/drag_to_move_area.dart +++ b/lib/src/widgets/drag_to_move_area.dart @@ -21,9 +21,9 @@ import 'package:window_manager/src/window_manager.dart'; /// {@end-tool} class DragToMoveArea extends StatelessWidget { const DragToMoveArea({ - Key? key, + super.key, required this.child, - }) : super(key: key); + }); final Widget child; diff --git a/lib/src/widgets/drag_to_resize_area.dart b/lib/src/widgets/drag_to_resize_area.dart index 0714d5f3..15237e14 100644 --- a/lib/src/widgets/drag_to_resize_area.dart +++ b/lib/src/widgets/drag_to_resize_area.dart @@ -26,13 +26,13 @@ import 'package:window_manager/src/window_manager.dart'; /// {@end-tool} class DragToResizeArea extends StatelessWidget { const DragToResizeArea({ - Key? key, + super.key, required this.child, this.resizeEdgeColor = Colors.transparent, this.resizeEdgeSize = 8, this.resizeEdgeMargin = EdgeInsets.zero, this.enableResizeEdges, - }) : super(key: key); + }); final Widget child; final double resizeEdgeSize; diff --git a/lib/src/widgets/virtual_window_frame.dart b/lib/src/widgets/virtual_window_frame.dart index 38a9b768..2cac2f7e 100644 --- a/lib/src/widgets/virtual_window_frame.dart +++ b/lib/src/widgets/virtual_window_frame.dart @@ -12,9 +12,9 @@ final _kIsWindows = !kIsWeb && Platform.isWindows; class VirtualWindowFrame extends StatefulWidget { const VirtualWindowFrame({ - Key? key, + super.key, required this.child, - }) : super(key: key); + }); /// The [child] contained by the VirtualWindowFrame. final Widget child; @@ -42,7 +42,7 @@ class _VirtualWindowFrameState extends State } Widget _buildVirtualWindowFrame(BuildContext context) { - return Container( + return DecoratedBox( decoration: BoxDecoration( color: Colors.transparent, border: Border.all( diff --git a/lib/src/widgets/window_caption.dart b/lib/src/widgets/window_caption.dart index 2068dff9..101d4476 100644 --- a/lib/src/widgets/window_caption.dart +++ b/lib/src/widgets/window_caption.dart @@ -25,11 +25,11 @@ const double kWindowCaptionHeight = 32; /// {@end-tool} class WindowCaption extends StatefulWidget { const WindowCaption({ - Key? key, + super.key, this.title, this.backgroundColor, this.brightness, - }) : super(key: key); + }); final Widget? title; final Color? backgroundColor; @@ -54,7 +54,7 @@ class _WindowCaptionState extends State with WindowListener { @override Widget build(BuildContext context) { - return Container( + return DecoratedBox( decoration: BoxDecoration( color: widget.backgroundColor ?? (widget.brightness == Brightness.dark diff --git a/lib/src/widgets/window_caption_button.dart b/lib/src/widgets/window_caption_button.dart index 82165f62..90afef76 100644 --- a/lib/src/widgets/window_caption_button.dart +++ b/lib/src/widgets/window_caption_button.dart @@ -4,11 +4,11 @@ import 'package:flutter/material.dart'; class WindowCaptionButtonIcon extends StatelessWidget { const WindowCaptionButtonIcon({ - Key? key, + super.key, required this.name, this.color, this.package = 'window_manager', - }) : super(key: key); + }); final String name; final Color? color; @@ -29,14 +29,15 @@ class WindowCaptionButtonIcon extends StatelessWidget { // ignore: must_be_immutable class WindowCaptionButton extends StatefulWidget { WindowCaptionButton({ - Key? key, + super.key, this.brightness, this.icon, this.iconName, required this.onPressed, - }) : super(key: key); + }); + WindowCaptionButton.close({ - Key? key, + super.key, this.brightness, this.icon, this.onPressed, @@ -62,32 +63,28 @@ class WindowCaptionButton extends StatefulWidget { hovered: Colors.white, pressed: Colors.white.withOpacity(0.786), disabled: Colors.black.withOpacity(0.3628), - ), - super(key: key); + ); WindowCaptionButton.unmaximize({ - Key? key, + super.key, this.brightness, this.icon, this.onPressed, - }) : iconName = 'images/ic_chrome_unmaximize.png', - super(key: key); + }) : iconName = 'images/ic_chrome_unmaximize.png'; WindowCaptionButton.maximize({ - Key? key, + super.key, this.brightness, this.icon, this.onPressed, - }) : iconName = 'images/ic_chrome_maximize.png', - super(key: key); + }) : iconName = 'images/ic_chrome_maximize.png'; WindowCaptionButton.minimize({ - Key? key, + super.key, this.brightness, this.icon, this.onPressed, - }) : iconName = 'images/ic_chrome_minimize.png', - super(key: key); + }) : iconName = 'images/ic_chrome_minimize.png'; final Brightness? brightness; final Widget? icon; diff --git a/lib/src/window_listener.dart b/lib/src/window_listener.dart index 6b221800..54a18722 100644 --- a/lib/src/window_listener.dart +++ b/lib/src/window_listener.dart @@ -1,4 +1,4 @@ -abstract class WindowListener { +abstract mixin class WindowListener { /// Emitted when the window is going to be closed. void onWindowClose() {} diff --git a/lib/src/window_manager.dart b/lib/src/window_manager.dart index 9736a832..dcfa1c8c 100644 --- a/lib/src/window_manager.dart +++ b/lib/src/window_manager.dart @@ -557,7 +557,7 @@ class WindowManager { bool windowButtonVisibility = true, }) async { final Map arguments = { - 'titleBarStyle': describeEnum(titleBarStyle), + 'titleBarStyle': titleBarStyle.name, 'windowButtonVisibility': windowButtonVisibility, }; await _channel.invokeMethod('setTitleBarStyle', arguments); @@ -683,7 +683,7 @@ class WindowManager { /// Sets the brightness of the window. Future setBrightness(Brightness brightness) async { final Map arguments = { - 'brightness': describeEnum(brightness), + 'brightness': brightness.name, }; await _channel.invokeMethod('setBrightness', arguments); } @@ -720,7 +720,7 @@ class WindowManager { await _channel.invokeMethod( 'startResizing', { - 'resizeEdge': describeEnum(resizeEdge), + 'resizeEdge': resizeEdge.name, 'top': resizeEdge == ResizeEdge.top || resizeEdge == ResizeEdge.topLeft || resizeEdge == ResizeEdge.topRight, diff --git a/pubspec.lock b/pubspec.lock index ac8de0a1..7857a439 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -61,10 +61,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" dependency_validator: dependency: "direct dev" description: @@ -94,14 +94,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 - url: "https://pub.dev" - source: hosted - version: "2.0.3" flutter_test: dependency: "direct dev" description: flutter @@ -123,14 +115,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" json_annotation: dependency: transitive description: @@ -143,10 +127,10 @@ packages: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" logging: dependency: transitive description: @@ -159,26 +143,34 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" + mostly_reasonable_lints: + dependency: "direct dev" + description: + name: mostly_reasonable_lints + sha256: c61cc6b211f54188eef15e0ad7c9b00e8c001478ac253a52036ef7391b532b7c + url: "https://pub.dev" + source: hosted + version: "0.1.1" package_config: dependency: transitive description: @@ -228,26 +220,26 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -268,10 +260,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.1" vector_math: dependency: transitive description: @@ -280,6 +272,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + url: "https://pub.dev" + source: hosted + version: "0.3.0" yaml: dependency: transitive description: @@ -289,5 +289,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index a489f6a7..4b62d8c0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: window_manager description: This plugin allows Flutter desktop apps to resizing and repositioning the window. -version: 0.3.7 +version: 0.3.8 homepage: https://github.com/leanflutter/window_manager platforms: @@ -16,7 +16,7 @@ topics: - desktop-window environment: - sdk: ">=2.18.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" flutter: ">=3.3.0" dependencies: @@ -27,9 +27,9 @@ dependencies: dev_dependencies: dependency_validator: ^3.0.0 - flutter_lints: ^2.0.0 flutter_test: sdk: flutter + mostly_reasonable_lints: ^0.1.1 flutter: plugin: From 87dd7a50b4cb47a375b9fc697f05e56eea0a2ab3 Mon Sep 17 00:00:00 2001 From: Arran Ubels Date: Mon, 25 Mar 2024 23:58:39 +1100 Subject: [PATCH 05/50] Links have changed (#447) I'm not 100% sure that these (despite the title match) are the same as they aren't exactly articles. But the current links 404. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8089d7d8..d1f5e686 100644 --- a/README.md +++ b/README.md @@ -504,8 +504,8 @@ class _HomePageState extends State with WindowListener { ## Articles -- [Click the dock icon to restore after closing the window](https://leanflutter.org/blog/click-dock-icon-to-restore-after-closing-the-window) -- [Making the app single-instanced](https://leanflutter.org/blog/making-the-app-single-instanced) +- [Click the dock icon to restore after closing the window](https://leanflutter.dev/tips-and-tricks/002-click-dock-icon-to-restore-after-closing-the-window/) +- [Making the app single-instanced](https://leanflutter.dev/tips-and-tricks/001-making-the-app-single-instanced/) ## Who's using it? From 69bd48423bfdf02e6a713a957c8af9519ae43046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Sat, 18 May 2024 09:16:31 +0800 Subject: [PATCH 06/50] wip: Update lock files --- example/macos/Podfile.lock | 2 +- .../macos/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- example/pubspec.lock | 78 +++++++++++-------- pubspec.lock | 50 ++++++++---- 5 files changed, 87 insertions(+), 47 deletions(-) diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index 853b7ed8..5326c40f 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -31,4 +31,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 -COCOAPODS: 1.11.3 +COCOAPODS: 1.14.3 diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj index 02283c15..62683b9b 100644 --- a/example/macos/Runner.xcodeproj/project.pbxproj +++ b/example/macos/Runner.xcodeproj/project.pbxproj @@ -202,7 +202,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index fe5b724f..aee170f1 100644 --- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ =3.2.0-194.0.dev <4.0.0" + dart: ">=3.2.0-0 <4.0.0" flutter: ">=3.3.0" diff --git a/pubspec.lock b/pubspec.lock index 7857a439..88d9417f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -123,6 +123,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.8.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" lints: dependency: transitive description: @@ -143,26 +167,26 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" mostly_reasonable_lints: dependency: "direct dev" description: @@ -183,10 +207,10 @@ packages: dependency: "direct main" description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" pub_semver: dependency: transitive description: @@ -272,14 +296,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: + vm_service: dependency: transitive description: - name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "13.0.0" yaml: dependency: transitive description: @@ -289,5 +313,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" + dart: ">=3.2.0-0 <4.0.0" flutter: ">=3.3.0" From e077e8ef9ffd50e46b197121a82d443965d4620f Mon Sep 17 00:00:00 2001 From: Adam Sowa Date: Sat, 18 May 2024 03:25:31 +0200 Subject: [PATCH 07/50] Fix WindowManager.IsFocused() method on Windows (#461) --- windows/window_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/window_manager.cpp b/windows/window_manager.cpp index 3484176d..c4bd8035 100644 --- a/windows/window_manager.cpp +++ b/windows/window_manager.cpp @@ -251,7 +251,7 @@ void WindowManager::Blur() { } bool WindowManager::IsFocused() { - return GetMainWindow() == GetActiveWindow(); + return GetMainWindow() == GetForegroundWindow(); } void WindowManager::Show() { From 8fa23db7f4c2cf8cf3085d5fc06e4ceb7857a2f5 Mon Sep 17 00:00:00 2001 From: LiJianying Date: Sat, 18 May 2024 09:41:06 +0800 Subject: [PATCH 08/50] fix: crash after deconstruction #423 --- example/windows/flutter/CMakeLists.txt | 7 ++++++- windows/window_manager_plugin.cpp | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/example/windows/flutter/CMakeLists.txt b/example/windows/flutter/CMakeLists.txt index d4b57706..e7107414 100644 --- a/example/windows/flutter/CMakeLists.txt +++ b/example/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -91,7 +96,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/windows/window_manager_plugin.cpp b/windows/window_manager_plugin.cpp index 75050250..51db8f7a 100644 --- a/windows/window_manager_plugin.cpp +++ b/windows/window_manager_plugin.cpp @@ -92,9 +92,11 @@ WindowManagerPlugin::WindowManagerPlugin( WindowManagerPlugin::~WindowManagerPlugin() { registrar->UnregisterTopLevelWindowProcDelegate(window_proc_id); + channel = nullptr; } void WindowManagerPlugin::_EmitEvent(std::string eventName) { + if (channel == nullptr) return; flutter::EncodableMap args = flutter::EncodableMap(); args[flutter::EncodableValue("eventName")] = flutter::EncodableValue(eventName); From 7260319f6cb717b11e773a4d6bdfbd3bca754c40 Mon Sep 17 00:00:00 2001 From: LiJianying Date: Sat, 18 May 2024 10:17:40 +0800 Subject: [PATCH 09/50] fix(windows): fix TitleBar buttons does not display correctly #415 --- windows/window_manager.cpp | 44 ++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/windows/window_manager.cpp b/windows/window_manager.cpp index c4bd8035..a27fc4f0 100644 --- a/windows/window_manager.cpp +++ b/windows/window_manager.cpp @@ -28,7 +28,26 @@ #define STATE_FULLSCREEN_ENTERED 3 #define STATE_DOCKED 4 -#define DWMWA_USE_IMMERSIVE_DARK_MODE 19 +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: +/// https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = + L"AppsUseLightTheme"; #define APPBAR_CALLBACK WM_USER + 0x01; @@ -1001,14 +1020,21 @@ void WindowManager::SetOpacity(const flutter::EncodableMap& args) { } void WindowManager::SetBrightness(const flutter::EncodableMap& args) { - std::string brightness = - std::get(args.at(flutter::EncodableValue("brightness"))); - - const BOOL is_dark_mode = brightness == "dark"; - - HWND hWnd = GetMainWindow(); - DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &is_dark_mode, - sizeof(is_dark_mode)); + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = + RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, RRF_RT_REG_DWORD, nullptr, + &light_mode, &light_mode_size); + + if (result == ERROR_SUCCESS) { + std::string brightness = + std::get(args.at(flutter::EncodableValue("brightness"))); + HWND hWnd = GetMainWindow(); + BOOL enable_dark_mode = light_mode == 0 && brightness == "dark"; + DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } } void WindowManager::SetIgnoreMouseEvents(const flutter::EncodableMap& args) { From 3e6261be31ebab4a00f44887eee6af13b512c897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Sat, 18 May 2024 11:53:04 +0800 Subject: [PATCH 10/50] wip: Update build workflow --- .github/workflows/build.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 024228dd..0aaaa26a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,9 @@ jobs: steps: - uses: actions/checkout@v2 - uses: subosito/flutter-action@v2 + with: + channel: stable + flutter-version: 3.19.6 - run: | sudo apt-get update -y sudo apt-get install -y ninja-build libgtk-3-dev libappindicator3-dev xvfb @@ -23,6 +26,9 @@ jobs: steps: - uses: actions/checkout@v2 - uses: subosito/flutter-action@v2 + with: + channel: stable + flutter-version: 3.19.6 - run: flutter config --enable-macos-desktop - run: cd example && flutter build macos -v # Blocked by https://github.com/flutter/flutter/issues/118469 @@ -32,5 +38,8 @@ jobs: steps: - uses: actions/checkout@v2 - uses: subosito/flutter-action@v2 + with: + channel: stable + flutter-version: 3.19.6 - run: cd example && flutter build windows -v - run: cd example && flutter test integration_test -v From 0914c23047d6ca2db52494a7f87060f9ca426f81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Sat, 18 May 2024 12:02:07 +0800 Subject: [PATCH 11/50] v0.3.9 --- CHANGELOG.md | 6 ++++++ README-ZH.md | 2 +- README.md | 2 +- example/pubspec.lock | 2 +- pubspec.lock | 8 ++++---- pubspec.yaml | 2 +- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 622a6eba..5b6fe16a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +### 0.3.9 + +* fix(windows): fix TitleBar buttons does not display correctly #415 +* fix(windows): crash after deconstruction #423 +* fix(windows): WindowManager.IsFocused() method (#461) + ### 0.3.8 * Updates minimum supported SDK version to Flutter 3.3/Dart 3.0. diff --git a/README-ZH.md b/README-ZH.md index 43405fb2..63912133 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -145,7 +145,7 @@ ```yaml dependencies: - window_manager: ^0.3.8 + window_manager: ^0.3.9 ``` 或 diff --git a/README.md b/README.md index d1f5e686..7a1b47e8 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ Add this to your package's `pubspec.yaml` file: ```yaml dependencies: - window_manager: ^0.3.8 + window_manager: ^0.3.9 ``` Or diff --git a/example/pubspec.lock b/example/pubspec.lock index 0a2324c2..4951d6f4 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -317,7 +317,7 @@ packages: path: ".." relative: true source: path - version: "0.3.8" + version: "0.3.9" sdks: dart: ">=3.2.0-0 <4.0.0" flutter: ">=3.3.0" diff --git a/pubspec.lock b/pubspec.lock index 88d9417f..2604638a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.5.0" async: dependency: transitive description: @@ -119,10 +119,10 @@ packages: dependency: transitive description: name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted - version: "4.8.1" + version: "4.9.0" leak_tracker: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 4b62d8c0..b0fdf2c3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: window_manager description: This plugin allows Flutter desktop apps to resizing and repositioning the window. -version: 0.3.8 +version: 0.3.9 homepage: https://github.com/leanflutter/window_manager platforms: From 7cc43867efc7313db6fe7f2c24ca7c0133e9429b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Thu, 13 Jun 2024 22:12:20 +0800 Subject: [PATCH 12/50] wip(example): Bump preference_list to 0.0.2 --- analysis_options.yaml | 2 +- example/analysis_options.yaml | 2 +- example/lib/main.dart | 5 +-- example/lib/pages/home.dart | 2 +- example/lib/themes/dark_theme.dart | 8 ---- example/lib/themes/light_theme.dart | 9 ----- example/lib/themes/themes.dart | 3 -- example/lib/utilities/utilities.dart | 2 - example/lib/{utilities => utils}/config.dart | 0 example/pubspec.lock | 40 ++++++++++---------- example/pubspec.yaml | 6 +-- pubspec.lock | 36 +++++++++--------- pubspec.yaml | 2 +- 13 files changed, 46 insertions(+), 71 deletions(-) delete mode 100644 example/lib/themes/dark_theme.dart delete mode 100644 example/lib/themes/light_theme.dart delete mode 100644 example/lib/themes/themes.dart delete mode 100644 example/lib/utilities/utilities.dart rename example/lib/{utilities => utils}/config.dart (100%) diff --git a/analysis_options.yaml b/analysis_options.yaml index 095b1d67..9033bb29 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1 +1 @@ -include: package:mostly_reasonable_lints/flutter.yaml +include: package:mostly_reasonable_lints/analysis_options.yaml diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index 095b1d67..9033bb29 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -1 +1 @@ -include: package:mostly_reasonable_lints/flutter.yaml +include: package:mostly_reasonable_lints/analysis_options.yaml diff --git a/example/lib/main.dart b/example/lib/main.dart index a52a2d64..a19c042f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -2,8 +2,7 @@ import 'package:bot_toast/bot_toast.dart'; import 'package:flutter/material.dart'; import 'package:window_manager/window_manager.dart'; import 'package:window_manager_example/pages/home.dart'; -import 'package:window_manager_example/themes/themes.dart'; -import 'package:window_manager_example/utilities/utilities.dart'; +import 'package:window_manager_example/utils/config.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -59,8 +58,6 @@ class _MyAppState extends State { return MaterialApp( debugShowCheckedModeBanner: false, - theme: lightThemeData, - darkTheme: darkThemeData, themeMode: _themeMode, builder: (context, child) { child = virtualWindowFrameBuilder(context, child); diff --git a/example/lib/pages/home.dart b/example/lib/pages/home.dart index f50c136d..123e5c2f 100644 --- a/example/lib/pages/home.dart +++ b/example/lib/pages/home.dart @@ -8,7 +8,7 @@ import 'package:flutter/material.dart'; import 'package:preference_list/preference_list.dart'; import 'package:tray_manager/tray_manager.dart'; import 'package:window_manager/window_manager.dart'; -import 'package:window_manager_example/utilities/utilities.dart'; +import 'package:window_manager_example/utils/config.dart'; const _kSizes = [ Size(400, 400), diff --git a/example/lib/themes/dark_theme.dart b/example/lib/themes/dark_theme.dart deleted file mode 100644 index aea58e36..00000000 --- a/example/lib/themes/dark_theme.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:flutter/material.dart'; - -var darkThemeData = ThemeData( - brightness: Brightness.dark, - primaryColor: const Color(0xff416ff4), - canvasColor: const Color(0xff282828), - scaffoldBackgroundColor: const Color(0xff1d1d1d), -); diff --git a/example/lib/themes/light_theme.dart b/example/lib/themes/light_theme.dart deleted file mode 100644 index 151668ac..00000000 --- a/example/lib/themes/light_theme.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:flutter/material.dart'; - -var lightThemeData = ThemeData( - brightness: Brightness.light, - primaryColor: const Color(0xff416ff4), - canvasColor: Colors.white, - scaffoldBackgroundColor: const Color(0xffF7F9FB), - dividerColor: Colors.grey.withOpacity(0.3), -); diff --git a/example/lib/themes/themes.dart b/example/lib/themes/themes.dart deleted file mode 100644 index 76dea945..00000000 --- a/example/lib/themes/themes.dart +++ /dev/null @@ -1,3 +0,0 @@ -// 请按文件名排序放置 -export 'dark_theme.dart'; -export 'light_theme.dart'; diff --git a/example/lib/utilities/utilities.dart b/example/lib/utilities/utilities.dart deleted file mode 100644 index e361f1e0..00000000 --- a/example/lib/utilities/utilities.dart +++ /dev/null @@ -1,2 +0,0 @@ -// 请按文件名排序放置 -export './config.dart'; diff --git a/example/lib/utilities/config.dart b/example/lib/utils/config.dart similarity index 100% rename from example/lib/utilities/config.dart rename to example/lib/utils/config.dart diff --git a/example/pubspec.lock b/example/pubspec.lock index 4951d6f4..95a60188 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -102,34 +102,34 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.0.0" matcher: dependency: transitive description: @@ -158,18 +158,18 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mostly_reasonable_lints: dependency: "direct dev" description: name: mostly_reasonable_lints - sha256: c61cc6b211f54188eef15e0ad7c9b00e8c001478ac253a52036ef7391b532b7c + sha256: e19fec63536866ba307b3dfbc258b4bce9b3745129f038006b56b4067c6293d8 url: "https://pub.dev" source: hosted - version: "0.1.1" + version: "0.1.2" path: dependency: transitive description: @@ -190,10 +190,10 @@ packages: dependency: "direct main" description: name: preference_list - sha256: d3419b2ec57a6ad2156bd682a46cf3194bf162b36bb013790c7b29e6438f107a + sha256: "1429f8fe03605d5eaf6dc333208d7bbf028bae707c3c1c32386939e4d7f58154" url: "https://pub.dev" source: hosted - version: "0.0.1" + version: "0.0.2" process: dependency: transitive description: @@ -275,10 +275,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" tray_manager: dependency: "direct main" description: @@ -299,10 +299,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" webdriver: dependency: transitive description: @@ -319,5 +319,5 @@ packages: source: path version: "0.3.9" sdks: - dart: ">=3.2.0-0 <4.0.0" - flutter: ">=3.3.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index e5053943..ff7a29e2 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -6,11 +6,11 @@ environment: sdk: ">=3.0.0 <4.0.0" dependencies: - bot_toast: ^4.0.1 + bot_toast: ^4.1.3 cupertino_icons: ^1.0.2 flutter: sdk: flutter - preference_list: ^0.0.1 + preference_list: ^0.0.2 tray_manager: ^0.2.0 window_manager: path: ../ @@ -20,7 +20,7 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - mostly_reasonable_lints: ^0.1.1 + mostly_reasonable_lints: ^0.1.2 flutter: uses-material-design: true diff --git a/pubspec.lock b/pubspec.lock index 2604638a..d608ab2e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -127,34 +127,34 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.0.0" logging: dependency: transitive description: @@ -183,18 +183,18 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mostly_reasonable_lints: dependency: "direct dev" description: name: mostly_reasonable_lints - sha256: c61cc6b211f54188eef15e0ad7c9b00e8c001478ac253a52036ef7391b532b7c + sha256: e19fec63536866ba307b3dfbc258b4bce9b3745129f038006b56b4067c6293d8 url: "https://pub.dev" source: hosted - version: "0.1.1" + version: "0.1.2" package_config: dependency: transitive description: @@ -284,10 +284,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" vector_math: dependency: transitive description: @@ -300,10 +300,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" yaml: dependency: transitive description: @@ -313,5 +313,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.0-0 <4.0.0" - flutter: ">=3.3.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index b0fdf2c3..6479ec4a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,7 +29,7 @@ dev_dependencies: dependency_validator: ^3.0.0 flutter_test: sdk: flutter - mostly_reasonable_lints: ^0.1.1 + mostly_reasonable_lints: ^0.1.2 flutter: plugin: From d2b5eab15bd3772174f949f95ca1cb2c43bb0dad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Tue, 25 Jun 2024 09:16:27 +0800 Subject: [PATCH 13/50] wip: Update build workflow --- .github/FUNDING.yml | 2 +- .github/workflows/build.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index cb2bfb17..b2e914fb 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -liberapay: lijy91 +github: lijy91 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0aaaa26a..042c7e16 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,7 @@ jobs: - uses: subosito/flutter-action@v2 with: channel: stable - flutter-version: 3.19.6 + flutter-version: 3.22.2 - run: | sudo apt-get update -y sudo apt-get install -y ninja-build libgtk-3-dev libappindicator3-dev xvfb @@ -28,7 +28,7 @@ jobs: - uses: subosito/flutter-action@v2 with: channel: stable - flutter-version: 3.19.6 + flutter-version: 3.22.2 - run: flutter config --enable-macos-desktop - run: cd example && flutter build macos -v # Blocked by https://github.com/flutter/flutter/issues/118469 @@ -40,6 +40,6 @@ jobs: - uses: subosito/flutter-action@v2 with: channel: stable - flutter-version: 3.19.6 + flutter-version: 3.22.2 - run: cd example && flutter build windows -v - run: cd example && flutter test integration_test -v From 35f3e188d05b15db9151c5886333e20156c90083 Mon Sep 17 00:00:00 2001 From: Rohit Sangwan Date: Sun, 28 Jul 2024 12:35:18 +0530 Subject: [PATCH 14/50] Update README.md (#473) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7a1b47e8..aa67002d 100644 --- a/README.md +++ b/README.md @@ -517,6 +517,7 @@ class _HomePageState extends State with WindowListener { - [Linwood Butterfly](https://github.com/LinwoodCloud/Butterfly) - Open source note taking app written in Flutter - [RustDesk](https://github.com/rustdesk/rustdesk) - Yet another remote desktop software, written in Rust. Works out of the box, no configuration required. - [Ubuntu Desktop Installer](https://github.com/canonical/ubuntu-desktop-installer) - This project is a modern implementation of the Ubuntu Desktop installer. +- [UniControlHub](https://github.com/rohitsangwan01/uni_control_hub) - Seamlessly bridge your Desktop and Mobile devices ## API From fa2a96308c80b11f70ad54f1d1fe74148f143c47 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 8 Aug 2024 20:45:54 +0800 Subject: [PATCH 15/50] fix: windows, window size, fullscree & maximized (#477) Signed-off-by: fufesou --- windows/window_manager_plugin.cpp | 32 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/windows/window_manager_plugin.cpp b/windows/window_manager_plugin.cpp index 51db8f7a..d83e52bd 100644 --- a/windows/window_manager_plugin.cpp +++ b/windows/window_manager_plugin.cpp @@ -61,6 +61,15 @@ class WindowManagerPlugin : public flutter::Plugin { void HandleMethodCall( const flutter::MethodCall& method_call, std::unique_ptr> result); + + void adjustNCCALCSIZE(NCCALCSIZE_PARAMS* sz) { + LONG l = sz->rgrc[0].left; + LONG t = sz->rgrc[0].top; + sz->rgrc[0].left -= l; + sz->rgrc[0].top -= t; + sz->rgrc[0].right += l; + sz->rgrc[0].bottom += t; + } }; // static @@ -119,43 +128,30 @@ std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, if (window_manager->IsFullScreen() && window_manager->title_bar_style_ != "normal") { if (window_manager->is_frameless_) { - NCCALCSIZE_PARAMS* sz = reinterpret_cast(lParam); - sz->rgrc[0].left += 8; - sz->rgrc[0].top += 8; - sz->rgrc[0].right -= 8; - sz->rgrc[0].bottom -= 8; + adjustNCCALCSIZE(reinterpret_cast(lParam)); } return 0; } // This must always be before handling title_bar_style_ == "hidden" so // the `if TitleBarStyle.hidden` doesn't get executed. if (window_manager->is_frameless_) { - NCCALCSIZE_PARAMS* sz = reinterpret_cast(lParam); if (window_manager->IsMaximized()) { - // Add borders when maximized so app doesn't get cut off. - sz->rgrc[0].left += 8; - sz->rgrc[0].top += 8; - sz->rgrc[0].right -= 8; - sz->rgrc[0].bottom -= 9; + adjustNCCALCSIZE(reinterpret_cast(lParam)); } return 0; } // This must always be last. if (wParam && window_manager->title_bar_style_ == "hidden") { - NCCALCSIZE_PARAMS* sz = reinterpret_cast(lParam); - - // Add 8 pixel to the top border when maximized so the app isn't cut off if (window_manager->IsMaximized()) { - sz->rgrc[0].top += 8; + // Adjust the borders when maximized so the app isn't cut off + adjustNCCALCSIZE(reinterpret_cast(lParam)); } else { + NCCALCSIZE_PARAMS* sz = reinterpret_cast(lParam); // on windows 10, if set to 0, there's a white line at the top // of the app and I've yet to find a way to remove that. sz->rgrc[0].top += IsWindows11OrGreater() ? 0 : 1; } - sz->rgrc[0].right -= 8; - sz->rgrc[0].bottom -= 8; - sz->rgrc[0].left -= -8; // Previously (WVR_HREDRAW | WVR_VREDRAW), but returning 0 or 1 doesn't // actually break anything so I've set it to 0. Unless someone pointed a From 2006a44722673040ec1da3e46d9a4c23dfbef3b8 Mon Sep 17 00:00:00 2001 From: LiJianying Date: Thu, 8 Aug 2024 20:46:08 +0800 Subject: [PATCH 16/50] chore: Remove the deprecated isBezeled property (#468) --- macos/Classes/WindowManager.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/macos/Classes/WindowManager.swift b/macos/Classes/WindowManager.swift index 5c69abcb..50f6e39b 100644 --- a/macos/Classes/WindowManager.swift +++ b/macos/Classes/WindowManager.swift @@ -433,7 +433,6 @@ public class WindowManager: NSObject, NSWindowDelegate { let progressIndicator: NSProgressIndicator = NSProgressIndicator.init(frame: frame) progressIndicator.style = .bar progressIndicator.isIndeterminate = false - progressIndicator.isBezeled = true progressIndicator.minValue = 0 progressIndicator.maxValue = 1 progressIndicator.isHidden = false From fcb6a7f520ff2671cd7e7f92916ddf086c57d67c Mon Sep 17 00:00:00 2001 From: LiJianying Date: Thu, 8 Aug 2024 20:46:20 +0800 Subject: [PATCH 17/50] chore: Use custom paint icons to replace png icons (#467) --- example/lib/pages/home.dart | 1 + images/ic_chrome_close.png | Bin 298 -> 0 bytes images/ic_chrome_maximize.png | Bin 271 -> 0 bytes images/ic_chrome_minimize.png | Bin 166 -> 0 bytes images/ic_chrome_unmaximize.png | Bin 366 -> 0 bytes lib/src/widgets/window_caption_button.dart | 327 ++++++++++++++++++--- pubspec.yaml | 5 - 7 files changed, 295 insertions(+), 38 deletions(-) delete mode 100644 images/ic_chrome_close.png delete mode 100644 images/ic_chrome_maximize.png delete mode 100644 images/ic_chrome_minimize.png delete mode 100644 images/ic_chrome_unmaximize.png diff --git a/example/lib/pages/home.dart b/example/lib/pages/home.dart index 123e5c2f..d9149ec7 100644 --- a/example/lib/pages/home.dart +++ b/example/lib/pages/home.dart @@ -133,6 +133,7 @@ class _HomePageState extends State with TrayListener, WindowListener { ? Brightness.light : Brightness.dark, ); + setState(() {}); }, ), ], diff --git a/images/ic_chrome_close.png b/images/ic_chrome_close.png deleted file mode 100644 index 1949491fe9516a8989f036896a3e154583671459..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 298 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9GG!XV7ZFl!D-1!HlL zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#{hlt4Ar*{oryS%xWWeJ(cawvghgkxP*(;7I z%I3!&a=33h!C_g%eLqxLv1>(a@XB^OO|M(O>fQT-7xi8hbjmR7Tat2N>-_h#^ADce z={LD0dEw54mq(i%BE&yi>$&aJy=vUvZ?72OwP rt9`;_78!G{oGxgyvF^gLRTYevucl@fym;yd^a_KgtDnm{r-UW|gdBMj diff --git a/images/ic_chrome_maximize.png b/images/ic_chrome_maximize.png deleted file mode 100644 index 672e9e49d2f0c4c8aa44b496beb98f32cb36720e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 271 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9GG!XV7ZFl!D-1!HlL zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#C7v#hAr*{or=I3*HV|+*uE846bl1_#!^(qm z$)pR8ml&r^^5c=c&?xDx@KN-WSImWd-EXh1IbJQve2OuxYD0^6YEPwu1INO&{Fi(4 zkKf8&_%onj`G@1R?It?a@o%=?u{RezxATCa7DwWH^LvJCQ>AyY6@>+-TS%G)Fq`gJ z<;Z7Q7CGan>EdtaPw39*y12wq>Zic?O{Vxj;5 diff --git a/images/ic_chrome_minimize.png b/images/ic_chrome_minimize.png deleted file mode 100644 index 05534b43693dcb6e9046de97b68d446ca86a90ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9GG!XV7ZFl!D-1!HlL zyA#8@b22Z19F}xPUq=Rpjs4tz5?O(KYfl%)kP61PXE$;(C|yla;*+ zW=hxpU2mD7l*aHO(`_*+;r|Be)g=BbKD#i_?TzLRQ&XK { ), child: Center( child: WindowCaptionButtonIcon( - name: widget.iconName!, color: iconColor, + createPainter: (color) { + switch (widget.iconName) { + case _kIconChromeMinimize: + return _IconChromeMinimizePainter(color!); + case _kIconChromeMaximize: + return _IconChromeMaximizePainter(color!); + case _kIconChromeUnmaximize: + return _IconChromeUnmaximizePainter(color!); + case _kIconChromeClose: + return _IconChromeClosePainter(color!); + default: + return _IconChromeClosePainter(color!); + } + }, ), ), ), diff --git a/pubspec.yaml b/pubspec.yaml index 6479ec4a..203461a5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,8 +40,3 @@ flutter: pluginClass: WindowManagerPlugin windows: pluginClass: WindowManagerPlugin - assets: - - images/ic_chrome_close.png - - images/ic_chrome_maximize.png - - images/ic_chrome_minimize.png - - images/ic_chrome_unmaximize.png From 85fc90ff13ef284b8d4ff98334f27fc0478d8059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Thu, 8 Aug 2024 20:54:48 +0800 Subject: [PATCH 18/50] v0.4.0 --- CHANGELOG.md | 6 +++++ README-ZH.md | 3 ++- README.md | 2 +- example/macos/Runner/AppDelegate.swift | 2 +- example/pubspec.lock | 34 +++++++++++++------------- example/pubspec.yaml | 2 +- pubspec.lock | 24 +++++++++--------- pubspec.yaml | 2 +- 8 files changed, 41 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b6fe16a..b8cf0160 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +### 0.4.0 + +* chore: Use custom paint icons to replace png icons (#467) +* chore: Remove the deprecated isBezeled property (#468) +* fix: windows, window size, fullscree & maximized (#477) + ### 0.3.9 * fix(windows): fix TitleBar buttons does not display correctly #415 diff --git a/README-ZH.md b/README-ZH.md index 63912133..c468c8d4 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -145,7 +145,7 @@ ```yaml dependencies: - window_manager: ^0.3.9 + window_manager: ^0.4.0 ``` 或 @@ -516,6 +516,7 @@ class _HomePageState extends State with WindowListener { - [Linwood Butterfly](https://github.com/LinwoodCloud/Butterfly) - 用 Flutter 编写的开源笔记应用 - [RustDesk](https://github.com/rustdesk/rustdesk) - 远程桌面软件,开箱即用,无需任何配置。您完全掌控数据,不用担心安全问题。 - [Ubuntu Desktop Installer](https://github.com/canonical/ubuntu-desktop-installer) - This project is a modern implementation of the Ubuntu Desktop installer. +- [UniControlHub](https://github.com/rohitsangwan01/uni_control_hub) - Seamlessly bridge your Desktop and Mobile devices ## API diff --git a/README.md b/README.md index aa67002d..40543773 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ Add this to your package's `pubspec.yaml` file: ```yaml dependencies: - window_manager: ^0.3.9 + window_manager: ^0.4.0 ``` Or diff --git a/example/macos/Runner/AppDelegate.swift b/example/macos/Runner/AppDelegate.swift index 814924b8..2aa1f82a 100644 --- a/example/macos/Runner/AppDelegate.swift +++ b/example/macos/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return false diff --git a/example/pubspec.lock b/example/pubspec.lock index 95a60188..289eb496 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -102,18 +102,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -142,10 +142,10 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" menu_base: dependency: transitive description: @@ -158,10 +158,10 @@ packages: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" mostly_reasonable_lints: dependency: "direct dev" description: @@ -182,10 +182,10 @@ packages: dependency: transitive description: name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.5" preference_list: dependency: "direct main" description: @@ -275,18 +275,18 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" tray_manager: dependency: "direct main" description: name: tray_manager - sha256: e0ac9a88b2700f366b8629b97e8663b6ef450a2f169560a685dc167bfe9c9c29 + sha256: c9a63fd88bd3546287a7eb8ccc978d707eef82c775397af17dda3a4f4c039e64 url: "https://pub.dev" source: hosted - version: "0.2.2" + version: "0.2.3" vector_math: dependency: transitive description: @@ -299,10 +299,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.4" webdriver: dependency: transitive description: @@ -317,7 +317,7 @@ packages: path: ".." relative: true source: path - version: "0.3.9" + version: "0.4.0" sdks: dart: ">=3.3.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index ff7a29e2..c1feaa7d 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: flutter: sdk: flutter preference_list: ^0.0.2 - tray_manager: ^0.2.0 + tray_manager: ^0.2.3 window_manager: path: ../ diff --git a/pubspec.lock b/pubspec.lock index d608ab2e..34b4ace8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -127,18 +127,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -175,18 +175,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" mostly_reasonable_lints: dependency: "direct dev" description: @@ -284,10 +284,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" vector_math: dependency: transitive description: @@ -300,10 +300,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.4" yaml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 203461a5..5e872d0b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: window_manager description: This plugin allows Flutter desktop apps to resizing and repositioning the window. -version: 0.3.9 +version: 0.4.0 homepage: https://github.com/leanflutter/window_manager platforms: From 3e8379e247f02e60a665f27679b02181e4512ee1 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sun, 18 Aug 2024 10:27:59 +0800 Subject: [PATCH 19/50] fix: win, adjustNCCALCSIZE with monitor coords (#482) * fix: win, adjustNCCALCSIZE with monitor coords Signed-off-by: fufesou * #483 Signed-off-by: fufesou --------- Signed-off-by: fufesou --- windows/window_manager_plugin.cpp | 35 ++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/windows/window_manager_plugin.cpp b/windows/window_manager_plugin.cpp index d83e52bd..02e5a5c9 100644 --- a/windows/window_manager_plugin.cpp +++ b/windows/window_manager_plugin.cpp @@ -62,9 +62,24 @@ class WindowManagerPlugin : public flutter::Plugin { const flutter::MethodCall& method_call, std::unique_ptr> result); - void adjustNCCALCSIZE(NCCALCSIZE_PARAMS* sz) { - LONG l = sz->rgrc[0].left; - LONG t = sz->rgrc[0].top; + void adjustNCCALCSIZE(HWND hwnd, NCCALCSIZE_PARAMS* sz) { + LONG l = 8; + LONG t = 8; + + HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + if (monitor != NULL) { + MONITORINFO monitorInfo; + monitorInfo.cbSize = sizeof(MONITORINFO); + if (TRUE == GetMonitorInfo(monitor, &monitorInfo)) { + l = sz->rgrc[0].left - monitorInfo.rcWork.left; + t = sz->rgrc[0].top - monitorInfo.rcWork.top; + } else { + // GetMonitorInfo failed, use (8, 8) as default value + } + } else { + // unreachable code + } + sz->rgrc[0].left -= l; sz->rgrc[0].top -= t; sz->rgrc[0].right += l; @@ -105,7 +120,8 @@ WindowManagerPlugin::~WindowManagerPlugin() { } void WindowManagerPlugin::_EmitEvent(std::string eventName) { - if (channel == nullptr) return; + if (channel == nullptr) + return; flutter::EncodableMap args = flutter::EncodableMap(); args[flutter::EncodableValue("eventName")] = flutter::EncodableValue(eventName); @@ -128,7 +144,7 @@ std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, if (window_manager->IsFullScreen() && window_manager->title_bar_style_ != "normal") { if (window_manager->is_frameless_) { - adjustNCCALCSIZE(reinterpret_cast(lParam)); + adjustNCCALCSIZE(hWnd, reinterpret_cast(lParam)); } return 0; } @@ -136,7 +152,7 @@ std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, // the `if TitleBarStyle.hidden` doesn't get executed. if (window_manager->is_frameless_) { if (window_manager->IsMaximized()) { - adjustNCCALCSIZE(reinterpret_cast(lParam)); + adjustNCCALCSIZE(hWnd, reinterpret_cast(lParam)); } return 0; } @@ -145,12 +161,17 @@ std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, if (wParam && window_manager->title_bar_style_ == "hidden") { if (window_manager->IsMaximized()) { // Adjust the borders when maximized so the app isn't cut off - adjustNCCALCSIZE(reinterpret_cast(lParam)); + adjustNCCALCSIZE(hWnd, reinterpret_cast(lParam)); } else { NCCALCSIZE_PARAMS* sz = reinterpret_cast(lParam); // on windows 10, if set to 0, there's a white line at the top // of the app and I've yet to find a way to remove that. sz->rgrc[0].top += IsWindows11OrGreater() ? 0 : 1; + // The following lines are required for resizing the window. + // https://github.com/leanflutter/window_manager/issues/483 + sz->rgrc[0].right -= 8; + sz->rgrc[0].bottom -= 8; + sz->rgrc[0].left -= -8; } // Previously (WVR_HREDRAW | WVR_VREDRAW), but returning 0 or 1 doesn't From 9f174e9b9771b322c10650bf6717770e5294281a Mon Sep 17 00:00:00 2001 From: alevlako Date: Sun, 18 Aug 2024 05:30:23 +0300 Subject: [PATCH 20/50] Update window_manager_plugin.cpp (#486) Fix 439 issue: [Windows] Restoring window from minimized state triggers onWindowBlur instead of onWindowFocus. --- windows/window_manager_plugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/window_manager_plugin.cpp b/windows/window_manager_plugin.cpp index 02e5a5c9..ca608cbb 100644 --- a/windows/window_manager_plugin.cpp +++ b/windows/window_manager_plugin.cpp @@ -200,7 +200,7 @@ std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, window_manager->maximum_size_.y * window_manager->pixel_ratio_); result = 0; } else if (message == WM_NCACTIVATE) { - if (wParam == TRUE) { + if (wParam != 0) { _EmitEvent("focus"); } else { _EmitEvent("blur"); From 7f47e58aa24b2db2cb8a7d642c2fc0fd6a089f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Sun, 18 Aug 2024 10:58:36 +0800 Subject: [PATCH 21/50] v0.4.2 --- CHANGELOG.md | 5 +++++ README-ZH.md | 2 +- README.md | 2 +- example/pubspec.lock | 2 +- pubspec.yaml | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8cf0160..de14d5b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +### 0.4.2 + +* [windows] Update window_manager_plugin.cpp for fix #439 issue #486 +* [windows] fix: win, adjustNCCALCSIZE with monitor coords #482 + ### 0.4.0 * chore: Use custom paint icons to replace png icons (#467) diff --git a/README-ZH.md b/README-ZH.md index c468c8d4..a2dd03bb 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -145,7 +145,7 @@ ```yaml dependencies: - window_manager: ^0.4.0 + window_manager: ^0.4.2 ``` 或 diff --git a/README.md b/README.md index 40543773..4b0e570e 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ Add this to your package's `pubspec.yaml` file: ```yaml dependencies: - window_manager: ^0.4.0 + window_manager: ^0.4.2 ``` Or diff --git a/example/pubspec.lock b/example/pubspec.lock index 289eb496..bc926d26 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -317,7 +317,7 @@ packages: path: ".." relative: true source: path - version: "0.4.0" + version: "0.4.2" sdks: dart: ">=3.3.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index 5e872d0b..9cdb4391 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: window_manager description: This plugin allows Flutter desktop apps to resizing and repositioning the window. -version: 0.4.0 +version: 0.4.2 homepage: https://github.com/leanflutter/window_manager platforms: From 73c39f406c56de682b6c34d8cc744cecdcb471bb Mon Sep 17 00:00:00 2001 From: Mohammed CHAHBOUN <69054810+M97Chahboun@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:18:30 +0100 Subject: [PATCH 22/50] Update README.md (#494) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4b0e570e..9ac2e7ea 100644 --- a/README.md +++ b/README.md @@ -518,6 +518,7 @@ class _HomePageState extends State with WindowListener { - [RustDesk](https://github.com/rustdesk/rustdesk) - Yet another remote desktop software, written in Rust. Works out of the box, no configuration required. - [Ubuntu Desktop Installer](https://github.com/canonical/ubuntu-desktop-installer) - This project is a modern implementation of the Ubuntu Desktop installer. - [UniControlHub](https://github.com/rohitsangwan01/uni_control_hub) - Seamlessly bridge your Desktop and Mobile devices +- [EyesCare](bixat.dev/products/EyesCare) - A light-weight application following 20 rule adherence for optimum eye health ## API From 29f494a07d27ee8eab99efd1f85b1c4296ada956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Sat, 26 Oct 2024 18:11:23 +0800 Subject: [PATCH 23/50] wip: Add window_manager_linux package --- packages/window_manager_linux/.gitignore | 29 +++++++ packages/window_manager_linux/.metadata | 27 +++++++ packages/window_manager_linux/CHANGELOG.md | 3 + packages/window_manager_linux/LICENSE | 1 + packages/window_manager_linux/README.md | 18 +++++ .../analysis_options.yaml | 4 + .../lib/window_manager_linux.dart | 14 ++++ .../window_manager_linux_method_channel.dart | 17 +++++ ...ndow_manager_linux_platform_interface.dart | 29 +++++++ packages/window_manager_linux/pubspec.yaml | 75 +++++++++++++++++++ ...dow_manager_linux_method_channel_test.dart | 27 +++++++ .../test/window_manager_linux_test.dart | 29 +++++++ 12 files changed, 273 insertions(+) create mode 100644 packages/window_manager_linux/.gitignore create mode 100644 packages/window_manager_linux/.metadata create mode 100644 packages/window_manager_linux/CHANGELOG.md create mode 100644 packages/window_manager_linux/LICENSE create mode 100644 packages/window_manager_linux/README.md create mode 100644 packages/window_manager_linux/analysis_options.yaml create mode 100644 packages/window_manager_linux/lib/window_manager_linux.dart create mode 100644 packages/window_manager_linux/lib/window_manager_linux_method_channel.dart create mode 100644 packages/window_manager_linux/lib/window_manager_linux_platform_interface.dart create mode 100644 packages/window_manager_linux/pubspec.yaml create mode 100644 packages/window_manager_linux/test/window_manager_linux_method_channel_test.dart create mode 100644 packages/window_manager_linux/test/window_manager_linux_test.dart diff --git a/packages/window_manager_linux/.gitignore b/packages/window_manager_linux/.gitignore new file mode 100644 index 00000000..ac5aa989 --- /dev/null +++ b/packages/window_manager_linux/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/packages/window_manager_linux/.metadata b/packages/window_manager_linux/.metadata new file mode 100644 index 00000000..17ceedf5 --- /dev/null +++ b/packages/window_manager_linux/.metadata @@ -0,0 +1,27 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" + channel: "stable" + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 + base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 + + # 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/packages/window_manager_linux/CHANGELOG.md b/packages/window_manager_linux/CHANGELOG.md new file mode 100644 index 00000000..41cc7d81 --- /dev/null +++ b/packages/window_manager_linux/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/packages/window_manager_linux/LICENSE b/packages/window_manager_linux/LICENSE new file mode 100644 index 00000000..ba75c69f --- /dev/null +++ b/packages/window_manager_linux/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/packages/window_manager_linux/README.md b/packages/window_manager_linux/README.md new file mode 100644 index 00000000..c196a807 --- /dev/null +++ b/packages/window_manager_linux/README.md @@ -0,0 +1,18 @@ +# window_manager_linux + +A new Flutter plugin project. + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/to/develop-plugins), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + +The plugin project was generated without specifying the `--platforms` flag, no platforms are currently supported. +To add platforms, run `flutter create -t plugin --platforms .` in this directory. +You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/to/pubspec-plugin-platforms. diff --git a/packages/window_manager_linux/analysis_options.yaml b/packages/window_manager_linux/analysis_options.yaml new file mode 100644 index 00000000..a5744c1c --- /dev/null +++ b/packages/window_manager_linux/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/window_manager_linux/lib/window_manager_linux.dart b/packages/window_manager_linux/lib/window_manager_linux.dart new file mode 100644 index 00000000..8efb5817 --- /dev/null +++ b/packages/window_manager_linux/lib/window_manager_linux.dart @@ -0,0 +1,14 @@ +// You have generated a new plugin project without specifying the `--platforms` +// flag. A plugin project with no platform support was generated. To add a +// platform, run `flutter create -t plugin --platforms .` under the +// same directory. You can also find a detailed instruction on how to add +// platforms in the `pubspec.yaml` at +// https://flutter.dev/to/pubspec-plugin-platforms. + +import 'window_manager_linux_platform_interface.dart'; + +class WindowManagerLinux { + Future getPlatformVersion() { + return WindowManagerLinuxPlatform.instance.getPlatformVersion(); + } +} diff --git a/packages/window_manager_linux/lib/window_manager_linux_method_channel.dart b/packages/window_manager_linux/lib/window_manager_linux_method_channel.dart new file mode 100644 index 00000000..fdc4cbd5 --- /dev/null +++ b/packages/window_manager_linux/lib/window_manager_linux_method_channel.dart @@ -0,0 +1,17 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'window_manager_linux_platform_interface.dart'; + +/// An implementation of [WindowManagerLinuxPlatform] that uses method channels. +class MethodChannelWindowManagerLinux extends WindowManagerLinuxPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('window_manager_linux'); + + @override + Future getPlatformVersion() async { + final version = await methodChannel.invokeMethod('getPlatformVersion'); + return version; + } +} diff --git a/packages/window_manager_linux/lib/window_manager_linux_platform_interface.dart b/packages/window_manager_linux/lib/window_manager_linux_platform_interface.dart new file mode 100644 index 00000000..d2f89a0c --- /dev/null +++ b/packages/window_manager_linux/lib/window_manager_linux_platform_interface.dart @@ -0,0 +1,29 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'window_manager_linux_method_channel.dart'; + +abstract class WindowManagerLinuxPlatform extends PlatformInterface { + /// Constructs a WindowManagerLinuxPlatform. + WindowManagerLinuxPlatform() : super(token: _token); + + static final Object _token = Object(); + + static WindowManagerLinuxPlatform _instance = MethodChannelWindowManagerLinux(); + + /// The default instance of [WindowManagerLinuxPlatform] to use. + /// + /// Defaults to [MethodChannelWindowManagerLinux]. + static WindowManagerLinuxPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [WindowManagerLinuxPlatform] when + /// they register themselves. + static set instance(WindowManagerLinuxPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + Future getPlatformVersion() { + throw UnimplementedError('platformVersion() has not been implemented.'); + } +} diff --git a/packages/window_manager_linux/pubspec.yaml b/packages/window_manager_linux/pubspec.yaml new file mode 100644 index 00000000..c78ff213 --- /dev/null +++ b/packages/window_manager_linux/pubspec.yaml @@ -0,0 +1,75 @@ +name: window_manager_linux +description: "A new Flutter plugin project." +version: 0.0.1 +homepage: + +environment: + sdk: ^3.5.3 + flutter: '>=3.3.0' + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^4.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + # This plugin project was generated without specifying any + # platforms with the `--platform` argument. If you see the `some_platform` map below, remove it and + # then add platforms following the instruction here: + # https://flutter.dev/to/pubspec-plugin-platforms + # ------------------- + some_platform: + pluginClass: somePluginClass + # ------------------- + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/to/asset-from-package + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/to/font-from-package diff --git a/packages/window_manager_linux/test/window_manager_linux_method_channel_test.dart b/packages/window_manager_linux/test/window_manager_linux_method_channel_test.dart new file mode 100644 index 00000000..8cb565d7 --- /dev/null +++ b/packages/window_manager_linux/test/window_manager_linux_method_channel_test.dart @@ -0,0 +1,27 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:window_manager_linux/window_manager_linux_method_channel.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + MethodChannelWindowManagerLinux platform = MethodChannelWindowManagerLinux(); + const MethodChannel channel = MethodChannel('window_manager_linux'); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler( + channel, + (MethodCall methodCall) async { + return '42'; + }, + ); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('getPlatformVersion', () async { + expect(await platform.getPlatformVersion(), '42'); + }); +} diff --git a/packages/window_manager_linux/test/window_manager_linux_test.dart b/packages/window_manager_linux/test/window_manager_linux_test.dart new file mode 100644 index 00000000..3fbc0c58 --- /dev/null +++ b/packages/window_manager_linux/test/window_manager_linux_test.dart @@ -0,0 +1,29 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:window_manager_linux/window_manager_linux.dart'; +import 'package:window_manager_linux/window_manager_linux_platform_interface.dart'; +import 'package:window_manager_linux/window_manager_linux_method_channel.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockWindowManagerLinuxPlatform + with MockPlatformInterfaceMixin + implements WindowManagerLinuxPlatform { + + @override + Future getPlatformVersion() => Future.value('42'); +} + +void main() { + final WindowManagerLinuxPlatform initialPlatform = WindowManagerLinuxPlatform.instance; + + test('$MethodChannelWindowManagerLinux is the default instance', () { + expect(initialPlatform, isInstanceOf()); + }); + + test('getPlatformVersion', () async { + WindowManagerLinux windowManagerLinuxPlugin = WindowManagerLinux(); + MockWindowManagerLinuxPlatform fakePlatform = MockWindowManagerLinuxPlatform(); + WindowManagerLinuxPlatform.instance = fakePlatform; + + expect(await windowManagerLinuxPlugin.getPlatformVersion(), '42'); + }); +} From 0ffd6d34b011c1d34466c46343686c6e4a7b2aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Sat, 26 Oct 2024 18:11:42 +0800 Subject: [PATCH 24/50] wip: Add window_manager_macos package --- packages/window_manager_macos/.gitignore | 29 ++++++++ packages/window_manager_macos/.metadata | 30 ++++++++ packages/window_manager_macos/CHANGELOG.md | 3 + packages/window_manager_macos/LICENSE | 1 + packages/window_manager_macos/README.md | 15 ++++ .../analysis_options.yaml | 4 ++ .../lib/window_manager_macos.dart | 8 +++ .../window_manager_macos_method_channel.dart | 17 +++++ ...ndow_manager_macos_platform_interface.dart | 29 ++++++++ .../Classes/WindowManagerMacosPlugin.swift | 19 +++++ .../macos/window_manager_macos.podspec | 23 +++++++ packages/window_manager_macos/pubspec.yaml | 69 +++++++++++++++++++ ...dow_manager_macos_method_channel_test.dart | 27 ++++++++ .../test/window_manager_macos_test.dart | 29 ++++++++ 14 files changed, 303 insertions(+) create mode 100644 packages/window_manager_macos/.gitignore create mode 100644 packages/window_manager_macos/.metadata create mode 100644 packages/window_manager_macos/CHANGELOG.md create mode 100644 packages/window_manager_macos/LICENSE create mode 100644 packages/window_manager_macos/README.md create mode 100644 packages/window_manager_macos/analysis_options.yaml create mode 100644 packages/window_manager_macos/lib/window_manager_macos.dart create mode 100644 packages/window_manager_macos/lib/window_manager_macos_method_channel.dart create mode 100644 packages/window_manager_macos/lib/window_manager_macos_platform_interface.dart create mode 100644 packages/window_manager_macos/macos/Classes/WindowManagerMacosPlugin.swift create mode 100644 packages/window_manager_macos/macos/window_manager_macos.podspec create mode 100644 packages/window_manager_macos/pubspec.yaml create mode 100644 packages/window_manager_macos/test/window_manager_macos_method_channel_test.dart create mode 100644 packages/window_manager_macos/test/window_manager_macos_test.dart diff --git a/packages/window_manager_macos/.gitignore b/packages/window_manager_macos/.gitignore new file mode 100644 index 00000000..ac5aa989 --- /dev/null +++ b/packages/window_manager_macos/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/packages/window_manager_macos/.metadata b/packages/window_manager_macos/.metadata new file mode 100644 index 00000000..b2763f65 --- /dev/null +++ b/packages/window_manager_macos/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" + channel: "stable" + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 + base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 + - platform: macos + create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 + base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 + + # 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/packages/window_manager_macos/CHANGELOG.md b/packages/window_manager_macos/CHANGELOG.md new file mode 100644 index 00000000..41cc7d81 --- /dev/null +++ b/packages/window_manager_macos/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/packages/window_manager_macos/LICENSE b/packages/window_manager_macos/LICENSE new file mode 100644 index 00000000..ba75c69f --- /dev/null +++ b/packages/window_manager_macos/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/packages/window_manager_macos/README.md b/packages/window_manager_macos/README.md new file mode 100644 index 00000000..38f19e82 --- /dev/null +++ b/packages/window_manager_macos/README.md @@ -0,0 +1,15 @@ +# window_manager_macos + +A new Flutter plugin project. + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/to/develop-plugins), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + diff --git a/packages/window_manager_macos/analysis_options.yaml b/packages/window_manager_macos/analysis_options.yaml new file mode 100644 index 00000000..a5744c1c --- /dev/null +++ b/packages/window_manager_macos/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/window_manager_macos/lib/window_manager_macos.dart b/packages/window_manager_macos/lib/window_manager_macos.dart new file mode 100644 index 00000000..6fb70378 --- /dev/null +++ b/packages/window_manager_macos/lib/window_manager_macos.dart @@ -0,0 +1,8 @@ + +import 'window_manager_macos_platform_interface.dart'; + +class WindowManagerMacos { + Future getPlatformVersion() { + return WindowManagerMacosPlatform.instance.getPlatformVersion(); + } +} diff --git a/packages/window_manager_macos/lib/window_manager_macos_method_channel.dart b/packages/window_manager_macos/lib/window_manager_macos_method_channel.dart new file mode 100644 index 00000000..64fc778d --- /dev/null +++ b/packages/window_manager_macos/lib/window_manager_macos_method_channel.dart @@ -0,0 +1,17 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'window_manager_macos_platform_interface.dart'; + +/// An implementation of [WindowManagerMacosPlatform] that uses method channels. +class MethodChannelWindowManagerMacos extends WindowManagerMacosPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('window_manager_macos'); + + @override + Future getPlatformVersion() async { + final version = await methodChannel.invokeMethod('getPlatformVersion'); + return version; + } +} diff --git a/packages/window_manager_macos/lib/window_manager_macos_platform_interface.dart b/packages/window_manager_macos/lib/window_manager_macos_platform_interface.dart new file mode 100644 index 00000000..0ed17eda --- /dev/null +++ b/packages/window_manager_macos/lib/window_manager_macos_platform_interface.dart @@ -0,0 +1,29 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'window_manager_macos_method_channel.dart'; + +abstract class WindowManagerMacosPlatform extends PlatformInterface { + /// Constructs a WindowManagerMacosPlatform. + WindowManagerMacosPlatform() : super(token: _token); + + static final Object _token = Object(); + + static WindowManagerMacosPlatform _instance = MethodChannelWindowManagerMacos(); + + /// The default instance of [WindowManagerMacosPlatform] to use. + /// + /// Defaults to [MethodChannelWindowManagerMacos]. + static WindowManagerMacosPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [WindowManagerMacosPlatform] when + /// they register themselves. + static set instance(WindowManagerMacosPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + Future getPlatformVersion() { + throw UnimplementedError('platformVersion() has not been implemented.'); + } +} diff --git a/packages/window_manager_macos/macos/Classes/WindowManagerMacosPlugin.swift b/packages/window_manager_macos/macos/Classes/WindowManagerMacosPlugin.swift new file mode 100644 index 00000000..1452f679 --- /dev/null +++ b/packages/window_manager_macos/macos/Classes/WindowManagerMacosPlugin.swift @@ -0,0 +1,19 @@ +import Cocoa +import FlutterMacOS + +public class WindowManagerMacosPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "window_manager_macos", binaryMessenger: registrar.messenger) + let instance = WindowManagerMacosPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "getPlatformVersion": + result("macOS " + ProcessInfo.processInfo.operatingSystemVersionString) + default: + result(FlutterMethodNotImplemented) + } + } +} diff --git a/packages/window_manager_macos/macos/window_manager_macos.podspec b/packages/window_manager_macos/macos/window_manager_macos.podspec new file mode 100644 index 00000000..21585b55 --- /dev/null +++ b/packages/window_manager_macos/macos/window_manager_macos.podspec @@ -0,0 +1,23 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint window_manager_macos.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'window_manager_macos' + s.version = '0.0.1' + s.summary = 'A new Flutter plugin project.' + s.description = <<-DESC +A new Flutter plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'FlutterMacOS' + + s.platform = :osx, '10.11' + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + s.swift_version = '5.0' +end diff --git a/packages/window_manager_macos/pubspec.yaml b/packages/window_manager_macos/pubspec.yaml new file mode 100644 index 00000000..d5964af2 --- /dev/null +++ b/packages/window_manager_macos/pubspec.yaml @@ -0,0 +1,69 @@ +name: window_manager_macos +description: "A new Flutter plugin project." +version: 0.0.1 +homepage: + +environment: + sdk: ^3.5.3 + flutter: '>=3.3.0' + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^4.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + macos: + pluginClass: WindowManagerMacosPlugin + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/to/asset-from-package + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/to/font-from-package diff --git a/packages/window_manager_macos/test/window_manager_macos_method_channel_test.dart b/packages/window_manager_macos/test/window_manager_macos_method_channel_test.dart new file mode 100644 index 00000000..35a2bc23 --- /dev/null +++ b/packages/window_manager_macos/test/window_manager_macos_method_channel_test.dart @@ -0,0 +1,27 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:window_manager_macos/window_manager_macos_method_channel.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + MethodChannelWindowManagerMacos platform = MethodChannelWindowManagerMacos(); + const MethodChannel channel = MethodChannel('window_manager_macos'); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler( + channel, + (MethodCall methodCall) async { + return '42'; + }, + ); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('getPlatformVersion', () async { + expect(await platform.getPlatformVersion(), '42'); + }); +} diff --git a/packages/window_manager_macos/test/window_manager_macos_test.dart b/packages/window_manager_macos/test/window_manager_macos_test.dart new file mode 100644 index 00000000..1db03bca --- /dev/null +++ b/packages/window_manager_macos/test/window_manager_macos_test.dart @@ -0,0 +1,29 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:window_manager_macos/window_manager_macos.dart'; +import 'package:window_manager_macos/window_manager_macos_platform_interface.dart'; +import 'package:window_manager_macos/window_manager_macos_method_channel.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockWindowManagerMacosPlatform + with MockPlatformInterfaceMixin + implements WindowManagerMacosPlatform { + + @override + Future getPlatformVersion() => Future.value('42'); +} + +void main() { + final WindowManagerMacosPlatform initialPlatform = WindowManagerMacosPlatform.instance; + + test('$MethodChannelWindowManagerMacos is the default instance', () { + expect(initialPlatform, isInstanceOf()); + }); + + test('getPlatformVersion', () async { + WindowManagerMacos windowManagerMacosPlugin = WindowManagerMacos(); + MockWindowManagerMacosPlatform fakePlatform = MockWindowManagerMacosPlatform(); + WindowManagerMacosPlatform.instance = fakePlatform; + + expect(await windowManagerMacosPlugin.getPlatformVersion(), '42'); + }); +} From 9900e55d1b6a8bec952ba1ae1eb90f2c91a94841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Sat, 26 Oct 2024 18:11:59 +0800 Subject: [PATCH 25/50] wip: Add window_manager_windows package --- packages/window_manager_windows/.gitignore | 29 +++++++ packages/window_manager_windows/.metadata | 27 +++++++ packages/window_manager_windows/CHANGELOG.md | 3 + packages/window_manager_windows/LICENSE | 1 + packages/window_manager_windows/README.md | 18 +++++ .../analysis_options.yaml | 4 + .../lib/window_manager_windows.dart | 14 ++++ ...window_manager_windows_method_channel.dart | 17 +++++ ...ow_manager_windows_platform_interface.dart | 29 +++++++ packages/window_manager_windows/pubspec.yaml | 75 +++++++++++++++++++ ...w_manager_windows_method_channel_test.dart | 27 +++++++ .../test/window_manager_windows_test.dart | 29 +++++++ 12 files changed, 273 insertions(+) create mode 100644 packages/window_manager_windows/.gitignore create mode 100644 packages/window_manager_windows/.metadata create mode 100644 packages/window_manager_windows/CHANGELOG.md create mode 100644 packages/window_manager_windows/LICENSE create mode 100644 packages/window_manager_windows/README.md create mode 100644 packages/window_manager_windows/analysis_options.yaml create mode 100644 packages/window_manager_windows/lib/window_manager_windows.dart create mode 100644 packages/window_manager_windows/lib/window_manager_windows_method_channel.dart create mode 100644 packages/window_manager_windows/lib/window_manager_windows_platform_interface.dart create mode 100644 packages/window_manager_windows/pubspec.yaml create mode 100644 packages/window_manager_windows/test/window_manager_windows_method_channel_test.dart create mode 100644 packages/window_manager_windows/test/window_manager_windows_test.dart diff --git a/packages/window_manager_windows/.gitignore b/packages/window_manager_windows/.gitignore new file mode 100644 index 00000000..ac5aa989 --- /dev/null +++ b/packages/window_manager_windows/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/packages/window_manager_windows/.metadata b/packages/window_manager_windows/.metadata new file mode 100644 index 00000000..17ceedf5 --- /dev/null +++ b/packages/window_manager_windows/.metadata @@ -0,0 +1,27 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" + channel: "stable" + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 + base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 + + # 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/packages/window_manager_windows/CHANGELOG.md b/packages/window_manager_windows/CHANGELOG.md new file mode 100644 index 00000000..41cc7d81 --- /dev/null +++ b/packages/window_manager_windows/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/packages/window_manager_windows/LICENSE b/packages/window_manager_windows/LICENSE new file mode 100644 index 00000000..ba75c69f --- /dev/null +++ b/packages/window_manager_windows/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/packages/window_manager_windows/README.md b/packages/window_manager_windows/README.md new file mode 100644 index 00000000..f3a2d790 --- /dev/null +++ b/packages/window_manager_windows/README.md @@ -0,0 +1,18 @@ +# window_manager_windows + +A new Flutter plugin project. + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/to/develop-plugins), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + +The plugin project was generated without specifying the `--platforms` flag, no platforms are currently supported. +To add platforms, run `flutter create -t plugin --platforms .` in this directory. +You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/to/pubspec-plugin-platforms. diff --git a/packages/window_manager_windows/analysis_options.yaml b/packages/window_manager_windows/analysis_options.yaml new file mode 100644 index 00000000..a5744c1c --- /dev/null +++ b/packages/window_manager_windows/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/window_manager_windows/lib/window_manager_windows.dart b/packages/window_manager_windows/lib/window_manager_windows.dart new file mode 100644 index 00000000..6ea6f783 --- /dev/null +++ b/packages/window_manager_windows/lib/window_manager_windows.dart @@ -0,0 +1,14 @@ +// You have generated a new plugin project without specifying the `--platforms` +// flag. A plugin project with no platform support was generated. To add a +// platform, run `flutter create -t plugin --platforms .` under the +// same directory. You can also find a detailed instruction on how to add +// platforms in the `pubspec.yaml` at +// https://flutter.dev/to/pubspec-plugin-platforms. + +import 'window_manager_windows_platform_interface.dart'; + +class WindowManagerWindows { + Future getPlatformVersion() { + return WindowManagerWindowsPlatform.instance.getPlatformVersion(); + } +} diff --git a/packages/window_manager_windows/lib/window_manager_windows_method_channel.dart b/packages/window_manager_windows/lib/window_manager_windows_method_channel.dart new file mode 100644 index 00000000..2f8143cf --- /dev/null +++ b/packages/window_manager_windows/lib/window_manager_windows_method_channel.dart @@ -0,0 +1,17 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'window_manager_windows_platform_interface.dart'; + +/// An implementation of [WindowManagerWindowsPlatform] that uses method channels. +class MethodChannelWindowManagerWindows extends WindowManagerWindowsPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('window_manager_windows'); + + @override + Future getPlatformVersion() async { + final version = await methodChannel.invokeMethod('getPlatformVersion'); + return version; + } +} diff --git a/packages/window_manager_windows/lib/window_manager_windows_platform_interface.dart b/packages/window_manager_windows/lib/window_manager_windows_platform_interface.dart new file mode 100644 index 00000000..69ab6a31 --- /dev/null +++ b/packages/window_manager_windows/lib/window_manager_windows_platform_interface.dart @@ -0,0 +1,29 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'window_manager_windows_method_channel.dart'; + +abstract class WindowManagerWindowsPlatform extends PlatformInterface { + /// Constructs a WindowManagerWindowsPlatform. + WindowManagerWindowsPlatform() : super(token: _token); + + static final Object _token = Object(); + + static WindowManagerWindowsPlatform _instance = MethodChannelWindowManagerWindows(); + + /// The default instance of [WindowManagerWindowsPlatform] to use. + /// + /// Defaults to [MethodChannelWindowManagerWindows]. + static WindowManagerWindowsPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [WindowManagerWindowsPlatform] when + /// they register themselves. + static set instance(WindowManagerWindowsPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + Future getPlatformVersion() { + throw UnimplementedError('platformVersion() has not been implemented.'); + } +} diff --git a/packages/window_manager_windows/pubspec.yaml b/packages/window_manager_windows/pubspec.yaml new file mode 100644 index 00000000..5824b0f0 --- /dev/null +++ b/packages/window_manager_windows/pubspec.yaml @@ -0,0 +1,75 @@ +name: window_manager_windows +description: "A new Flutter plugin project." +version: 0.0.1 +homepage: + +environment: + sdk: ^3.5.3 + flutter: '>=3.3.0' + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^4.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + # This plugin project was generated without specifying any + # platforms with the `--platform` argument. If you see the `some_platform` map below, remove it and + # then add platforms following the instruction here: + # https://flutter.dev/to/pubspec-plugin-platforms + # ------------------- + some_platform: + pluginClass: somePluginClass + # ------------------- + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/to/asset-from-package + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/to/font-from-package diff --git a/packages/window_manager_windows/test/window_manager_windows_method_channel_test.dart b/packages/window_manager_windows/test/window_manager_windows_method_channel_test.dart new file mode 100644 index 00000000..a85b6f1b --- /dev/null +++ b/packages/window_manager_windows/test/window_manager_windows_method_channel_test.dart @@ -0,0 +1,27 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:window_manager_windows/window_manager_windows_method_channel.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + MethodChannelWindowManagerWindows platform = MethodChannelWindowManagerWindows(); + const MethodChannel channel = MethodChannel('window_manager_windows'); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler( + channel, + (MethodCall methodCall) async { + return '42'; + }, + ); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); + }); + + test('getPlatformVersion', () async { + expect(await platform.getPlatformVersion(), '42'); + }); +} diff --git a/packages/window_manager_windows/test/window_manager_windows_test.dart b/packages/window_manager_windows/test/window_manager_windows_test.dart new file mode 100644 index 00000000..6b643b0a --- /dev/null +++ b/packages/window_manager_windows/test/window_manager_windows_test.dart @@ -0,0 +1,29 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:window_manager_windows/window_manager_windows.dart'; +import 'package:window_manager_windows/window_manager_windows_platform_interface.dart'; +import 'package:window_manager_windows/window_manager_windows_method_channel.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockWindowManagerWindowsPlatform + with MockPlatformInterfaceMixin + implements WindowManagerWindowsPlatform { + + @override + Future getPlatformVersion() => Future.value('42'); +} + +void main() { + final WindowManagerWindowsPlatform initialPlatform = WindowManagerWindowsPlatform.instance; + + test('$MethodChannelWindowManagerWindows is the default instance', () { + expect(initialPlatform, isInstanceOf()); + }); + + test('getPlatformVersion', () async { + WindowManagerWindows windowManagerWindowsPlugin = WindowManagerWindows(); + MockWindowManagerWindowsPlatform fakePlatform = MockWindowManagerWindowsPlatform(); + WindowManagerWindowsPlatform.instance = fakePlatform; + + expect(await windowManagerWindowsPlugin.getPlatformVersion(), '42'); + }); +} From db6e0674e26620805f3dbef93200de7fad7c1b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Sat, 26 Oct 2024 18:17:53 +0800 Subject: [PATCH 26/50] wip --- example/pubspec.lock | 4 ++-- packages/window_manager_linux/CHANGELOG.md | 2 +- packages/window_manager_linux/LICENSE | 22 +++++++++++++++++++- packages/window_manager_linux/README.md | 18 ++++++---------- packages/window_manager_linux/pubspec.yaml | 4 ++-- packages/window_manager_macos/CHANGELOG.md | 2 +- packages/window_manager_macos/LICENSE | 22 +++++++++++++++++++- packages/window_manager_macos/README.md | 15 ++++++------- packages/window_manager_macos/pubspec.yaml | 4 ++-- packages/window_manager_windows/CHANGELOG.md | 2 +- packages/window_manager_windows/LICENSE | 22 +++++++++++++++++++- packages/window_manager_windows/README.md | 18 ++++++---------- packages/window_manager_windows/pubspec.yaml | 4 ++-- pubspec.lock | 4 ++-- 14 files changed, 94 insertions(+), 49 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index bc926d26..46218dd2 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -299,10 +299,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.4" + version: "14.2.5" webdriver: dependency: transitive description: diff --git a/packages/window_manager_linux/CHANGELOG.md b/packages/window_manager_linux/CHANGELOG.md index 41cc7d81..d0bd041d 100644 --- a/packages/window_manager_linux/CHANGELOG.md +++ b/packages/window_manager_linux/CHANGELOG.md @@ -1,3 +1,3 @@ ## 0.0.1 -* TODO: Describe initial release. +* Initial release. diff --git a/packages/window_manager_linux/LICENSE b/packages/window_manager_linux/LICENSE index ba75c69f..9b1046a8 100644 --- a/packages/window_manager_linux/LICENSE +++ b/packages/window_manager_linux/LICENSE @@ -1 +1,21 @@ -TODO: Add your license here. +MIT License + +Copyright (c) 2024 LiJianying + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/window_manager_linux/README.md b/packages/window_manager_linux/README.md index c196a807..2445cc0e 100644 --- a/packages/window_manager_linux/README.md +++ b/packages/window_manager_linux/README.md @@ -1,18 +1,12 @@ # window_manager_linux -A new Flutter plugin project. +[![pub version][pub-image]][pub-url] -## Getting Started +[pub-image]: https://img.shields.io/pub/v/window_manager_linux.svg +[pub-url]: https://pub.dev/packages/window_manager_linux -This project is a starting point for a Flutter -[plug-in package](https://flutter.dev/to/develop-plugins), -a specialized package that includes platform-specific implementation code for -Android and/or iOS. +The Windows implementation of [window_manager](https://pub.dev/packages/window_manager). -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +## License -The plugin project was generated without specifying the `--platforms` flag, no platforms are currently supported. -To add platforms, run `flutter create -t plugin --platforms .` in this directory. -You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/to/pubspec-plugin-platforms. +[MIT](./LICENSE) diff --git a/packages/window_manager_linux/pubspec.yaml b/packages/window_manager_linux/pubspec.yaml index c78ff213..bc14f77d 100644 --- a/packages/window_manager_linux/pubspec.yaml +++ b/packages/window_manager_linux/pubspec.yaml @@ -1,7 +1,7 @@ name: window_manager_linux -description: "A new Flutter plugin project." +description: Linux implementation of the window_manager plugin. version: 0.0.1 -homepage: +repository: https://github.com/leanflutter/window_manager/tree/main/packages/window_manager_linux environment: sdk: ^3.5.3 diff --git a/packages/window_manager_macos/CHANGELOG.md b/packages/window_manager_macos/CHANGELOG.md index 41cc7d81..d0bd041d 100644 --- a/packages/window_manager_macos/CHANGELOG.md +++ b/packages/window_manager_macos/CHANGELOG.md @@ -1,3 +1,3 @@ ## 0.0.1 -* TODO: Describe initial release. +* Initial release. diff --git a/packages/window_manager_macos/LICENSE b/packages/window_manager_macos/LICENSE index ba75c69f..9b1046a8 100644 --- a/packages/window_manager_macos/LICENSE +++ b/packages/window_manager_macos/LICENSE @@ -1 +1,21 @@ -TODO: Add your license here. +MIT License + +Copyright (c) 2024 LiJianying + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/window_manager_macos/README.md b/packages/window_manager_macos/README.md index 38f19e82..d4e2f8b5 100644 --- a/packages/window_manager_macos/README.md +++ b/packages/window_manager_macos/README.md @@ -1,15 +1,12 @@ # window_manager_macos -A new Flutter plugin project. +[![pub version][pub-image]][pub-url] -## Getting Started +[pub-image]: https://img.shields.io/pub/v/window_manager_macos.svg +[pub-url]: https://pub.dev/packages/window_manager_macos -This project is a starting point for a Flutter -[plug-in package](https://flutter.dev/to/develop-plugins), -a specialized package that includes platform-specific implementation code for -Android and/or iOS. +The Windows implementation of [window_manager](https://pub.dev/packages/window_manager). -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +## License +[MIT](./LICENSE) diff --git a/packages/window_manager_macos/pubspec.yaml b/packages/window_manager_macos/pubspec.yaml index d5964af2..31fe4c05 100644 --- a/packages/window_manager_macos/pubspec.yaml +++ b/packages/window_manager_macos/pubspec.yaml @@ -1,7 +1,7 @@ name: window_manager_macos -description: "A new Flutter plugin project." +description: macOS implementation of the window_manager plugin. version: 0.0.1 -homepage: +repository: https://github.com/leanflutter/window_manager/tree/main/packages/window_manager_macos environment: sdk: ^3.5.3 diff --git a/packages/window_manager_windows/CHANGELOG.md b/packages/window_manager_windows/CHANGELOG.md index 41cc7d81..d0bd041d 100644 --- a/packages/window_manager_windows/CHANGELOG.md +++ b/packages/window_manager_windows/CHANGELOG.md @@ -1,3 +1,3 @@ ## 0.0.1 -* TODO: Describe initial release. +* Initial release. diff --git a/packages/window_manager_windows/LICENSE b/packages/window_manager_windows/LICENSE index ba75c69f..9b1046a8 100644 --- a/packages/window_manager_windows/LICENSE +++ b/packages/window_manager_windows/LICENSE @@ -1 +1,21 @@ -TODO: Add your license here. +MIT License + +Copyright (c) 2024 LiJianying + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/window_manager_windows/README.md b/packages/window_manager_windows/README.md index f3a2d790..b2c9ce25 100644 --- a/packages/window_manager_windows/README.md +++ b/packages/window_manager_windows/README.md @@ -1,18 +1,12 @@ # window_manager_windows -A new Flutter plugin project. +[![pub version][pub-image]][pub-url] -## Getting Started +[pub-image]: https://img.shields.io/pub/v/window_manager_windows.svg +[pub-url]: https://pub.dev/packages/window_manager_windows -This project is a starting point for a Flutter -[plug-in package](https://flutter.dev/to/develop-plugins), -a specialized package that includes platform-specific implementation code for -Android and/or iOS. +The Windows implementation of [window_manager](https://pub.dev/packages/window_manager). -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +## License -The plugin project was generated without specifying the `--platforms` flag, no platforms are currently supported. -To add platforms, run `flutter create -t plugin --platforms .` in this directory. -You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/to/pubspec-plugin-platforms. +[MIT](./LICENSE) diff --git a/packages/window_manager_windows/pubspec.yaml b/packages/window_manager_windows/pubspec.yaml index 5824b0f0..891dc2f8 100644 --- a/packages/window_manager_windows/pubspec.yaml +++ b/packages/window_manager_windows/pubspec.yaml @@ -1,7 +1,7 @@ name: window_manager_windows -description: "A new Flutter plugin project." +description: Windows implementation of the window_manager plugin. version: 0.0.1 -homepage: +repository: https://github.com/leanflutter/window_manager/tree/main/packages/window_manager_windows environment: sdk: ^3.5.3 diff --git a/pubspec.lock b/pubspec.lock index 34b4ace8..7ad2eb91 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -300,10 +300,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.4" + version: "14.2.5" yaml: dependency: transitive description: From 42dcf641723b03b6b9b5d0e3d01f4801f143df0c Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sun, 27 Oct 2024 09:31:26 +0800 Subject: [PATCH 27/50] fix: get window monitor from minimized state (#495) Signed-off-by: fufesou --- windows/window_manager_plugin.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/windows/window_manager_plugin.cpp b/windows/window_manager_plugin.cpp index ca608cbb..61b85523 100644 --- a/windows/window_manager_plugin.cpp +++ b/windows/window_manager_plugin.cpp @@ -66,7 +66,12 @@ class WindowManagerPlugin : public flutter::Plugin { LONG l = 8; LONG t = 8; - HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + // HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + // Don't use `MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST)` above. + // Because if the window is restored from minimized state, the window is not in the correct monitor. + // The monitor is always the left-most monitor. + // https://github.com/leanflutter/window_manager/issues/489 + HMONITOR monitor = MonitorFromRect(&sz->rgrc[0], MONITOR_DEFAULTTONEAREST); if (monitor != NULL) { MONITORINFO monitorInfo; monitorInfo.cbSize = sizeof(MONITORINFO); From 32bf6ab7aeeff06e38d4ba849066f29ae13f5362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=AA=20Trung=20=C4=90=C3=B4?= Date: Sun, 27 Oct 2024 10:37:49 +0900 Subject: [PATCH 28/50] fix: scale ratio on dpi change (#496) --- windows/window_manager_plugin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/window_manager_plugin.cpp b/windows/window_manager_plugin.cpp index 61b85523..7d4a55eb 100644 --- a/windows/window_manager_plugin.cpp +++ b/windows/window_manager_plugin.cpp @@ -143,6 +143,7 @@ std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, if (message == WM_DPICHANGED) { window_manager->pixel_ratio_ = (float)LOWORD(wParam) / USER_DEFAULT_SCREEN_DPI; + window_manager->ForceChildRefresh(); } if (wParam && message == WM_NCCALCSIZE) { From 21b3493425a78225aa1427851840c21d94e58f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Sun, 27 Oct 2024 10:00:51 +0800 Subject: [PATCH 29/50] wip: Move source to packages/window_manager --- .gitignore | 11 +- LICENSE | 2 +- example/pubspec.lock | 323 ----- melos.yaml | 35 + packages/window_manager/.gitignore | 30 + .../window_manager/.metadata | 0 .../window_manager/CHANGELOG.md | 5 + packages/window_manager/LICENSE | 21 + packages/window_manager/README-ZH.md | 1 + packages/window_manager/README.md | 1 + .../window_manager/analysis_options.yaml | 0 .../dart_dependency_validator.yaml | 0 .../window_manager/example}/.gitignore | 0 .../window_manager/example}/.metadata | 0 .../window_manager/example}/README.md | 0 .../example}/analysis_options.yaml | 0 .../example}/images/tray_icon.ico | Bin .../example}/images/tray_icon.png | Bin .../example}/images/tray_icon_original.ico | Bin .../example}/images/tray_icon_original.png | Bin .../integration_test/window_manager_test.dart | 0 .../window_manager/example}/lib/main.dart | 0 .../example}/lib/pages/home.dart | 0 .../example}/lib/utils/config.dart | 0 .../window_manager/example}/linux/.gitignore | 0 .../example}/linux/CMakeLists.txt | 0 .../example}/linux/flutter/CMakeLists.txt | 0 .../window_manager/example}/linux/main.cc | 0 .../example}/linux/my_application.cc | 0 .../example}/linux/my_application.h | 0 .../window_manager/example}/macos/.gitignore | 0 .../macos/Flutter/Flutter-Debug.xcconfig | 0 .../macos/Flutter/Flutter-Release.xcconfig | 0 .../Flutter/GeneratedPluginRegistrant.swift | 16 + .../window_manager/example}/macos/Podfile | 0 .../example}/macos/Podfile.lock | 10 +- .../macos/Runner.xcodeproj/project.pbxproj | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/xcschemes/Runner.xcscheme | 0 .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../example}/macos/Runner/AppDelegate.swift | 0 .../AppIcon.appiconset/Contents.json | 0 .../AppIcon.appiconset/app_icon_1024.png | Bin .../AppIcon.appiconset/app_icon_128.png | Bin .../AppIcon.appiconset/app_icon_16.png | Bin .../AppIcon.appiconset/app_icon_256.png | Bin .../AppIcon.appiconset/app_icon_32.png | Bin .../AppIcon.appiconset/app_icon_512.png | Bin .../AppIcon.appiconset/app_icon_64.png | Bin .../macos/Runner/Base.lproj/MainMenu.xib | 0 .../macos/Runner/Configs/AppInfo.xcconfig | 0 .../macos/Runner/Configs/Debug.xcconfig | 0 .../macos/Runner/Configs/Release.xcconfig | 0 .../macos/Runner/Configs/Warnings.xcconfig | 0 .../macos/Runner/DebugProfile.entitlements | 0 .../example}/macos/Runner/Info.plist | 0 .../macos/Runner/MainFlutterWindow.swift | 0 .../macos/Runner/Release.entitlements | 0 .../window_manager/example}/pubspec.yaml | 0 .../example}/test/widget_test.dart | 0 .../example}/windows/.gitignore | 34 +- .../example}/windows/CMakeLists.txt | 190 +-- .../example}/windows/flutter/CMakeLists.txt | 216 +-- .../example}/windows/runner/CMakeLists.txt | 34 +- .../example}/windows/runner/Runner.rc | 242 ++-- .../windows/runner/flutter_window.cpp | 122 +- .../example}/windows/runner/flutter_window.h | 66 +- .../example}/windows/runner/main.cpp | 86 +- .../example}/windows/runner/resource.h | 32 +- .../windows/runner/resources/app_icon.ico | Bin .../windows/runner/runner.exe.manifest | 40 +- .../example}/windows/runner/utils.cpp | 128 +- .../example}/windows/runner/utils.h | 38 +- .../example}/windows/runner/win32_window.cpp | 490 +++---- .../example}/windows/runner/win32_window.h | 196 +-- .../window_manager/lib}/src/resize_edge.dart | 0 .../lib}/src/title_bar_style.dart | 0 .../lib}/src/utils/calc_window_position.dart | 0 .../lib}/src/widgets/drag_to_move_area.dart | 0 .../lib}/src/widgets/drag_to_resize_area.dart | 0 .../src/widgets/virtual_window_frame.dart | 0 .../lib}/src/widgets/window_caption.dart | 0 .../src/widgets/window_caption_button.dart | 0 .../lib}/src/window_listener.dart | 0 .../lib}/src/window_manager.dart | 0 .../lib}/src/window_options.dart | 0 .../window_manager/lib}/window_manager.dart | 0 .../window_manager/linux}/CMakeLists.txt | 0 .../window_manager/window_manager_plugin.h | 0 .../linux}/window_manager_plugin.cc | 0 .../macos}/Classes/WindowManager.swift | 0 .../macos}/Classes/WindowManagerPlugin.swift | 0 .../macos}/window_manager.podspec | 0 packages/window_manager/pubspec.yaml | 42 + .../test}/window_manager_test.dart | 0 .../window_manager/windows}/.gitignore | 34 +- .../window_manager/windows}/CMakeLists.txt | 52 +- .../window_manager/window_manager_plugin.h | 50 +- .../windows}/window_manager.cpp | 0 .../windows}/window_manager_plugin.cpp | 1192 ++++++++--------- pubspec.lock | 276 ++-- pubspec.yaml | 39 +- 103 files changed, 1935 insertions(+), 2119 deletions(-) delete mode 100644 example/pubspec.lock create mode 100644 melos.yaml create mode 100644 packages/window_manager/.gitignore rename .metadata => packages/window_manager/.metadata (100%) rename CHANGELOG.md => packages/window_manager/CHANGELOG.md (98%) create mode 100644 packages/window_manager/LICENSE create mode 120000 packages/window_manager/README-ZH.md create mode 120000 packages/window_manager/README.md rename analysis_options.yaml => packages/window_manager/analysis_options.yaml (100%) rename dart_dependency_validator.yaml => packages/window_manager/dart_dependency_validator.yaml (100%) rename {example => packages/window_manager/example}/.gitignore (100%) rename {example => packages/window_manager/example}/.metadata (100%) rename {example => packages/window_manager/example}/README.md (100%) rename {example => packages/window_manager/example}/analysis_options.yaml (100%) rename {example => packages/window_manager/example}/images/tray_icon.ico (100%) rename {example => packages/window_manager/example}/images/tray_icon.png (100%) rename {example => packages/window_manager/example}/images/tray_icon_original.ico (100%) rename {example => packages/window_manager/example}/images/tray_icon_original.png (100%) rename {example => packages/window_manager/example}/integration_test/window_manager_test.dart (100%) rename {example => packages/window_manager/example}/lib/main.dart (100%) rename {example => packages/window_manager/example}/lib/pages/home.dart (100%) rename {example => packages/window_manager/example}/lib/utils/config.dart (100%) rename {example => packages/window_manager/example}/linux/.gitignore (100%) rename {example => packages/window_manager/example}/linux/CMakeLists.txt (100%) rename {example => packages/window_manager/example}/linux/flutter/CMakeLists.txt (100%) rename {example => packages/window_manager/example}/linux/main.cc (100%) rename {example => packages/window_manager/example}/linux/my_application.cc (100%) rename {example => packages/window_manager/example}/linux/my_application.h (100%) rename {example => packages/window_manager/example}/macos/.gitignore (100%) rename {example => packages/window_manager/example}/macos/Flutter/Flutter-Debug.xcconfig (100%) rename {example => packages/window_manager/example}/macos/Flutter/Flutter-Release.xcconfig (100%) create mode 100644 packages/window_manager/example/macos/Flutter/GeneratedPluginRegistrant.swift rename {example => packages/window_manager/example}/macos/Podfile (100%) rename {example => packages/window_manager/example}/macos/Podfile.lock (73%) rename {example => packages/window_manager/example}/macos/Runner.xcodeproj/project.pbxproj (100%) rename {example => packages/window_manager/example}/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {example => packages/window_manager/example}/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (100%) rename {example => packages/window_manager/example}/macos/Runner.xcworkspace/contents.xcworkspacedata (100%) rename {example => packages/window_manager/example}/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {example => packages/window_manager/example}/macos/Runner/AppDelegate.swift (100%) rename {example => packages/window_manager/example}/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {example => packages/window_manager/example}/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png (100%) rename {example => packages/window_manager/example}/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png (100%) rename {example => packages/window_manager/example}/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png (100%) rename {example => packages/window_manager/example}/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png (100%) rename {example => packages/window_manager/example}/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png (100%) rename {example => packages/window_manager/example}/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png (100%) rename {example => packages/window_manager/example}/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png (100%) rename {example => packages/window_manager/example}/macos/Runner/Base.lproj/MainMenu.xib (100%) rename {example => packages/window_manager/example}/macos/Runner/Configs/AppInfo.xcconfig (100%) rename {example => packages/window_manager/example}/macos/Runner/Configs/Debug.xcconfig (100%) rename {example => packages/window_manager/example}/macos/Runner/Configs/Release.xcconfig (100%) rename {example => packages/window_manager/example}/macos/Runner/Configs/Warnings.xcconfig (100%) rename {example => packages/window_manager/example}/macos/Runner/DebugProfile.entitlements (100%) rename {example => packages/window_manager/example}/macos/Runner/Info.plist (100%) rename {example => packages/window_manager/example}/macos/Runner/MainFlutterWindow.swift (100%) rename {example => packages/window_manager/example}/macos/Runner/Release.entitlements (100%) rename {example => packages/window_manager/example}/pubspec.yaml (100%) rename {example => packages/window_manager/example}/test/widget_test.dart (100%) rename {example => packages/window_manager/example}/windows/.gitignore (94%) rename {example => packages/window_manager/example}/windows/CMakeLists.txt (97%) rename {example => packages/window_manager/example}/windows/flutter/CMakeLists.txt (97%) rename {example => packages/window_manager/example}/windows/runner/CMakeLists.txt (97%) rename {example => packages/window_manager/example}/windows/runner/Runner.rc (96%) rename {example => packages/window_manager/example}/windows/runner/flutter_window.cpp (96%) rename {example => packages/window_manager/example}/windows/runner/flutter_window.h (96%) rename {example => packages/window_manager/example}/windows/runner/main.cpp (96%) rename {example => packages/window_manager/example}/windows/runner/resource.h (96%) rename {example => packages/window_manager/example}/windows/runner/resources/app_icon.ico (100%) rename {example => packages/window_manager/example}/windows/runner/runner.exe.manifest (97%) rename {example => packages/window_manager/example}/windows/runner/utils.cpp (96%) rename {example => packages/window_manager/example}/windows/runner/utils.h (97%) rename {example => packages/window_manager/example}/windows/runner/win32_window.cpp (96%) rename {example => packages/window_manager/example}/windows/runner/win32_window.h (97%) rename {lib => packages/window_manager/lib}/src/resize_edge.dart (100%) rename {lib => packages/window_manager/lib}/src/title_bar_style.dart (100%) rename {lib => packages/window_manager/lib}/src/utils/calc_window_position.dart (100%) rename {lib => packages/window_manager/lib}/src/widgets/drag_to_move_area.dart (100%) rename {lib => packages/window_manager/lib}/src/widgets/drag_to_resize_area.dart (100%) rename {lib => packages/window_manager/lib}/src/widgets/virtual_window_frame.dart (100%) rename {lib => packages/window_manager/lib}/src/widgets/window_caption.dart (100%) rename {lib => packages/window_manager/lib}/src/widgets/window_caption_button.dart (100%) rename {lib => packages/window_manager/lib}/src/window_listener.dart (100%) rename {lib => packages/window_manager/lib}/src/window_manager.dart (100%) rename {lib => packages/window_manager/lib}/src/window_options.dart (100%) rename {lib => packages/window_manager/lib}/window_manager.dart (100%) rename {linux => packages/window_manager/linux}/CMakeLists.txt (100%) rename {linux => packages/window_manager/linux}/include/window_manager/window_manager_plugin.h (100%) rename {linux => packages/window_manager/linux}/window_manager_plugin.cc (100%) rename {macos => packages/window_manager/macos}/Classes/WindowManager.swift (100%) rename {macos => packages/window_manager/macos}/Classes/WindowManagerPlugin.swift (100%) rename {macos => packages/window_manager/macos}/window_manager.podspec (100%) create mode 100644 packages/window_manager/pubspec.yaml rename {test => packages/window_manager/test}/window_manager_test.dart (100%) rename {windows => packages/window_manager/windows}/.gitignore (94%) rename {windows => packages/window_manager/windows}/CMakeLists.txt (97%) rename {windows => packages/window_manager/windows}/include/window_manager/window_manager_plugin.h (95%) rename {windows => packages/window_manager/windows}/window_manager.cpp (100%) rename {windows => packages/window_manager/windows}/window_manager_plugin.cpp (97%) diff --git a/.gitignore b/.gitignore index f890f640..fa08f054 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,6 @@ -.DS_Store .dart_tool/ .idea/ -.packages -.pub/ -.vscode/ -build/ -Generated* -generated* + +*.iml +pubspec_overrides.yaml +pubspec.lock diff --git a/LICENSE b/LICENSE index 03d8b0a2..46e9d56a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 LiJianying +Copyright (c) 2022-present LiJianying Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/pubspec.lock b/example/pubspec.lock deleted file mode 100644 index 46218dd2..00000000 --- a/example/pubspec.lock +++ /dev/null @@ -1,323 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - bot_toast: - dependency: "direct main" - description: - name: bot_toast - sha256: "6b93030a99a98335b8827ecd83021e92e885ffc61d261d3825ffdecdd17f3bdf" - url: "https://pub.dev" - source: hosted - version: "4.1.3" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.dev" - source: hosted - version: "1.18.0" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://pub.dev" - source: hosted - version: "1.0.8" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - file: - dependency: transitive - description: - name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_driver: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - fuchsia_remote_debug_protocol: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - integration_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" - url: "https://pub.dev" - source: hosted - version: "10.0.5" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" - url: "https://pub.dev" - source: hosted - version: "3.0.5" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - lints: - dependency: transitive - description: - name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb - url: "https://pub.dev" - source: hosted - version: "0.12.16+1" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec - url: "https://pub.dev" - source: hosted - version: "0.11.1" - menu_base: - dependency: transitive - description: - name: menu_base - sha256: "820368014a171bd1241030278e6c2617354f492f5c703d7b7d4570a6b8b84405" - url: "https://pub.dev" - source: hosted - version: "0.1.1" - meta: - dependency: transitive - description: - name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 - url: "https://pub.dev" - source: hosted - version: "1.15.0" - mostly_reasonable_lints: - dependency: "direct dev" - description: - name: mostly_reasonable_lints - sha256: e19fec63536866ba307b3dfbc258b4bce9b3745129f038006b56b4067c6293d8 - url: "https://pub.dev" - source: hosted - version: "0.1.2" - path: - dependency: transitive - description: - name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://pub.dev" - source: hosted - version: "1.9.0" - platform: - dependency: transitive - description: - name: platform - sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" - url: "https://pub.dev" - source: hosted - version: "3.1.5" - preference_list: - dependency: "direct main" - description: - name: preference_list - sha256: "1429f8fe03605d5eaf6dc333208d7bbf028bae707c3c1c32386939e4d7f58154" - url: "https://pub.dev" - source: hosted - version: "0.0.2" - process: - dependency: transitive - description: - name: process - sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" - url: "https://pub.dev" - source: hosted - version: "5.0.2" - screen_retriever: - dependency: transitive - description: - name: screen_retriever - sha256: "6ee02c8a1158e6dae7ca430da79436e3b1c9563c8cf02f524af997c201ac2b90" - url: "https://pub.dev" - source: hosted - version: "0.1.9" - shortid: - dependency: transitive - description: - name: shortid - sha256: d0b40e3dbb50497dad107e19c54ca7de0d1a274eb9b4404991e443dadb9ebedb - url: "https://pub.dev" - source: hosted - version: "0.1.2" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" - source: hosted - version: "1.11.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - sync_http: - dependency: transitive - description: - name: sync_http - sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" - url: "https://pub.dev" - source: hosted - version: "0.3.1" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" - url: "https://pub.dev" - source: hosted - version: "0.7.2" - tray_manager: - dependency: "direct main" - description: - name: tray_manager - sha256: c9a63fd88bd3546287a7eb8ccc978d707eef82c775397af17dda3a4f4c039e64 - url: "https://pub.dev" - source: hosted - version: "0.2.3" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" - url: "https://pub.dev" - source: hosted - version: "14.2.5" - webdriver: - dependency: transitive - description: - name: webdriver - sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" - url: "https://pub.dev" - source: hosted - version: "3.0.3" - window_manager: - dependency: "direct main" - description: - path: ".." - relative: true - source: path - version: "0.4.2" -sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" diff --git a/melos.yaml b/melos.yaml new file mode 100644 index 00000000..0c8036c9 --- /dev/null +++ b/melos.yaml @@ -0,0 +1,35 @@ +name: window_manager_workspace +repository: https://github.com/leanflutter/window_manager + +packages: + - examples/** + - packages/** + +command: + bootstrap: + # Uses the pubspec_overrides.yaml instead of having Melos modifying the lock file. + usePubspecOverrides: true + +scripts: + analyze: + exec: flutter analyze --fatal-infos + description: Run `flutter analyze` for all packages. + + test: + exec: flutter test + description: Run `flutter test` for a specific package. + packageFilters: + dirExists: + - test + + format: + exec: dart format . --fix + description: Run `dart format` for all packages. + + format-check: + exec: dart format . --fix --set-exit-if-changed + description: Run `dart format` checks for all packages. + + fix: + exec: dart fix . --apply + description: Run `dart fix` for all packages. diff --git a/packages/window_manager/.gitignore b/packages/window_manager/.gitignore new file mode 100644 index 00000000..96486fd9 --- /dev/null +++ b/packages/window_manager/.gitignore @@ -0,0 +1,30 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/.metadata b/packages/window_manager/.metadata similarity index 100% rename from .metadata rename to packages/window_manager/.metadata diff --git a/CHANGELOG.md b/packages/window_manager/CHANGELOG.md similarity index 98% rename from CHANGELOG.md rename to packages/window_manager/CHANGELOG.md index de14d5b1..a101a0e8 100644 --- a/CHANGELOG.md +++ b/packages/window_manager/CHANGELOG.md @@ -1,3 +1,8 @@ +### 0.4.3 + +* [windows] fix: scale ratio on dpi change (#496) +* [windows] fix: get window monitor from minimized state (#495) + ### 0.4.2 * [windows] Update window_manager_plugin.cpp for fix #439 issue #486 diff --git a/packages/window_manager/LICENSE b/packages/window_manager/LICENSE new file mode 100644 index 00000000..46e9d56a --- /dev/null +++ b/packages/window_manager/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022-present LiJianying + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/packages/window_manager/README-ZH.md b/packages/window_manager/README-ZH.md new file mode 120000 index 00000000..7da944f1 --- /dev/null +++ b/packages/window_manager/README-ZH.md @@ -0,0 +1 @@ +../../README-ZH.md \ No newline at end of file diff --git a/packages/window_manager/README.md b/packages/window_manager/README.md new file mode 120000 index 00000000..fe840054 --- /dev/null +++ b/packages/window_manager/README.md @@ -0,0 +1 @@ +../../README.md \ No newline at end of file diff --git a/analysis_options.yaml b/packages/window_manager/analysis_options.yaml similarity index 100% rename from analysis_options.yaml rename to packages/window_manager/analysis_options.yaml diff --git a/dart_dependency_validator.yaml b/packages/window_manager/dart_dependency_validator.yaml similarity index 100% rename from dart_dependency_validator.yaml rename to packages/window_manager/dart_dependency_validator.yaml diff --git a/example/.gitignore b/packages/window_manager/example/.gitignore similarity index 100% rename from example/.gitignore rename to packages/window_manager/example/.gitignore diff --git a/example/.metadata b/packages/window_manager/example/.metadata similarity index 100% rename from example/.metadata rename to packages/window_manager/example/.metadata diff --git a/example/README.md b/packages/window_manager/example/README.md similarity index 100% rename from example/README.md rename to packages/window_manager/example/README.md diff --git a/example/analysis_options.yaml b/packages/window_manager/example/analysis_options.yaml similarity index 100% rename from example/analysis_options.yaml rename to packages/window_manager/example/analysis_options.yaml diff --git a/example/images/tray_icon.ico b/packages/window_manager/example/images/tray_icon.ico similarity index 100% rename from example/images/tray_icon.ico rename to packages/window_manager/example/images/tray_icon.ico diff --git a/example/images/tray_icon.png b/packages/window_manager/example/images/tray_icon.png similarity index 100% rename from example/images/tray_icon.png rename to packages/window_manager/example/images/tray_icon.png diff --git a/example/images/tray_icon_original.ico b/packages/window_manager/example/images/tray_icon_original.ico similarity index 100% rename from example/images/tray_icon_original.ico rename to packages/window_manager/example/images/tray_icon_original.ico diff --git a/example/images/tray_icon_original.png b/packages/window_manager/example/images/tray_icon_original.png similarity index 100% rename from example/images/tray_icon_original.png rename to packages/window_manager/example/images/tray_icon_original.png diff --git a/example/integration_test/window_manager_test.dart b/packages/window_manager/example/integration_test/window_manager_test.dart similarity index 100% rename from example/integration_test/window_manager_test.dart rename to packages/window_manager/example/integration_test/window_manager_test.dart diff --git a/example/lib/main.dart b/packages/window_manager/example/lib/main.dart similarity index 100% rename from example/lib/main.dart rename to packages/window_manager/example/lib/main.dart diff --git a/example/lib/pages/home.dart b/packages/window_manager/example/lib/pages/home.dart similarity index 100% rename from example/lib/pages/home.dart rename to packages/window_manager/example/lib/pages/home.dart diff --git a/example/lib/utils/config.dart b/packages/window_manager/example/lib/utils/config.dart similarity index 100% rename from example/lib/utils/config.dart rename to packages/window_manager/example/lib/utils/config.dart diff --git a/example/linux/.gitignore b/packages/window_manager/example/linux/.gitignore similarity index 100% rename from example/linux/.gitignore rename to packages/window_manager/example/linux/.gitignore diff --git a/example/linux/CMakeLists.txt b/packages/window_manager/example/linux/CMakeLists.txt similarity index 100% rename from example/linux/CMakeLists.txt rename to packages/window_manager/example/linux/CMakeLists.txt diff --git a/example/linux/flutter/CMakeLists.txt b/packages/window_manager/example/linux/flutter/CMakeLists.txt similarity index 100% rename from example/linux/flutter/CMakeLists.txt rename to packages/window_manager/example/linux/flutter/CMakeLists.txt diff --git a/example/linux/main.cc b/packages/window_manager/example/linux/main.cc similarity index 100% rename from example/linux/main.cc rename to packages/window_manager/example/linux/main.cc diff --git a/example/linux/my_application.cc b/packages/window_manager/example/linux/my_application.cc similarity index 100% rename from example/linux/my_application.cc rename to packages/window_manager/example/linux/my_application.cc diff --git a/example/linux/my_application.h b/packages/window_manager/example/linux/my_application.h similarity index 100% rename from example/linux/my_application.h rename to packages/window_manager/example/linux/my_application.h diff --git a/example/macos/.gitignore b/packages/window_manager/example/macos/.gitignore similarity index 100% rename from example/macos/.gitignore rename to packages/window_manager/example/macos/.gitignore diff --git a/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/window_manager/example/macos/Flutter/Flutter-Debug.xcconfig similarity index 100% rename from example/macos/Flutter/Flutter-Debug.xcconfig rename to packages/window_manager/example/macos/Flutter/Flutter-Debug.xcconfig diff --git a/example/macos/Flutter/Flutter-Release.xcconfig b/packages/window_manager/example/macos/Flutter/Flutter-Release.xcconfig similarity index 100% rename from example/macos/Flutter/Flutter-Release.xcconfig rename to packages/window_manager/example/macos/Flutter/Flutter-Release.xcconfig diff --git a/packages/window_manager/example/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/window_manager/example/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 00000000..641dc94f --- /dev/null +++ b/packages/window_manager/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,16 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import screen_retriever_macos +import tray_manager +import window_manager + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin")) + TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin")) + WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) +} diff --git a/example/macos/Podfile b/packages/window_manager/example/macos/Podfile similarity index 100% rename from example/macos/Podfile rename to packages/window_manager/example/macos/Podfile diff --git a/example/macos/Podfile.lock b/packages/window_manager/example/macos/Podfile.lock similarity index 73% rename from example/macos/Podfile.lock rename to packages/window_manager/example/macos/Podfile.lock index 5326c40f..f7e4c3ee 100644 --- a/example/macos/Podfile.lock +++ b/packages/window_manager/example/macos/Podfile.lock @@ -1,6 +1,6 @@ PODS: - FlutterMacOS (1.0.0) - - screen_retriever (0.0.1): + - screen_retriever_macos (0.0.1): - FlutterMacOS - tray_manager (0.0.1): - FlutterMacOS @@ -9,15 +9,15 @@ PODS: DEPENDENCIES: - FlutterMacOS (from `Flutter/ephemeral`) - - screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`) + - screen_retriever_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos`) - tray_manager (from `Flutter/ephemeral/.symlinks/plugins/tray_manager/macos`) - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`) EXTERNAL SOURCES: FlutterMacOS: :path: Flutter/ephemeral - screen_retriever: - :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos + screen_retriever_macos: + :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos tray_manager: :path: Flutter/ephemeral/.symlinks/plugins/tray_manager/macos window_manager: @@ -25,7 +25,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 + screen_retriever_macos: 776e0fa5d42c6163d2bf772d22478df4b302b161 tray_manager: 9064e219c56d75c476e46b9a21182087930baf90 window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/packages/window_manager/example/macos/Runner.xcodeproj/project.pbxproj similarity index 100% rename from example/macos/Runner.xcodeproj/project.pbxproj rename to packages/window_manager/example/macos/Runner.xcodeproj/project.pbxproj diff --git a/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/window_manager/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/window_manager/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/window_manager/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to packages/window_manager/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/window_manager/example/macos/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/macos/Runner.xcworkspace/contents.xcworkspacedata rename to packages/window_manager/example/macos/Runner.xcworkspace/contents.xcworkspacedata diff --git a/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/window_manager/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/window_manager/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/macos/Runner/AppDelegate.swift b/packages/window_manager/example/macos/Runner/AppDelegate.swift similarity index 100% rename from example/macos/Runner/AppDelegate.swift rename to packages/window_manager/example/macos/Runner/AppDelegate.swift diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png similarity index 100% rename from example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png rename to packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png similarity index 100% rename from example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png rename to packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png similarity index 100% rename from example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png rename to packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png similarity index 100% rename from example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png rename to packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png similarity index 100% rename from example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png rename to packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png similarity index 100% rename from example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png rename to packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png similarity index 100% rename from example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png rename to packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png diff --git a/example/macos/Runner/Base.lproj/MainMenu.xib b/packages/window_manager/example/macos/Runner/Base.lproj/MainMenu.xib similarity index 100% rename from example/macos/Runner/Base.lproj/MainMenu.xib rename to packages/window_manager/example/macos/Runner/Base.lproj/MainMenu.xib diff --git a/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/window_manager/example/macos/Runner/Configs/AppInfo.xcconfig similarity index 100% rename from example/macos/Runner/Configs/AppInfo.xcconfig rename to packages/window_manager/example/macos/Runner/Configs/AppInfo.xcconfig diff --git a/example/macos/Runner/Configs/Debug.xcconfig b/packages/window_manager/example/macos/Runner/Configs/Debug.xcconfig similarity index 100% rename from example/macos/Runner/Configs/Debug.xcconfig rename to packages/window_manager/example/macos/Runner/Configs/Debug.xcconfig diff --git a/example/macos/Runner/Configs/Release.xcconfig b/packages/window_manager/example/macos/Runner/Configs/Release.xcconfig similarity index 100% rename from example/macos/Runner/Configs/Release.xcconfig rename to packages/window_manager/example/macos/Runner/Configs/Release.xcconfig diff --git a/example/macos/Runner/Configs/Warnings.xcconfig b/packages/window_manager/example/macos/Runner/Configs/Warnings.xcconfig similarity index 100% rename from example/macos/Runner/Configs/Warnings.xcconfig rename to packages/window_manager/example/macos/Runner/Configs/Warnings.xcconfig diff --git a/example/macos/Runner/DebugProfile.entitlements b/packages/window_manager/example/macos/Runner/DebugProfile.entitlements similarity index 100% rename from example/macos/Runner/DebugProfile.entitlements rename to packages/window_manager/example/macos/Runner/DebugProfile.entitlements diff --git a/example/macos/Runner/Info.plist b/packages/window_manager/example/macos/Runner/Info.plist similarity index 100% rename from example/macos/Runner/Info.plist rename to packages/window_manager/example/macos/Runner/Info.plist diff --git a/example/macos/Runner/MainFlutterWindow.swift b/packages/window_manager/example/macos/Runner/MainFlutterWindow.swift similarity index 100% rename from example/macos/Runner/MainFlutterWindow.swift rename to packages/window_manager/example/macos/Runner/MainFlutterWindow.swift diff --git a/example/macos/Runner/Release.entitlements b/packages/window_manager/example/macos/Runner/Release.entitlements similarity index 100% rename from example/macos/Runner/Release.entitlements rename to packages/window_manager/example/macos/Runner/Release.entitlements diff --git a/example/pubspec.yaml b/packages/window_manager/example/pubspec.yaml similarity index 100% rename from example/pubspec.yaml rename to packages/window_manager/example/pubspec.yaml diff --git a/example/test/widget_test.dart b/packages/window_manager/example/test/widget_test.dart similarity index 100% rename from example/test/widget_test.dart rename to packages/window_manager/example/test/widget_test.dart diff --git a/example/windows/.gitignore b/packages/window_manager/example/windows/.gitignore similarity index 94% rename from example/windows/.gitignore rename to packages/window_manager/example/windows/.gitignore index ec4098aa..d492d0d9 100644 --- a/example/windows/.gitignore +++ b/packages/window_manager/example/windows/.gitignore @@ -1,17 +1,17 @@ -flutter/ephemeral/ - -# Visual Studio user-specific files. -*.suo -*.user -*.userosscache -*.sln.docstates - -# Visual Studio build-related files. -x64/ -x86/ - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/example/windows/CMakeLists.txt b/packages/window_manager/example/windows/CMakeLists.txt similarity index 97% rename from example/windows/CMakeLists.txt rename to packages/window_manager/example/windows/CMakeLists.txt index 65a82a22..471f7dde 100644 --- a/example/windows/CMakeLists.txt +++ b/packages/window_manager/example/windows/CMakeLists.txt @@ -1,95 +1,95 @@ -cmake_minimum_required(VERSION 3.14) -project(window_manager_example LANGUAGES CXX) - -set(BINARY_NAME "window_manager_example") - -cmake_policy(SET CMP0063 NEW) - -set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") - -# Configure build options. -get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(IS_MULTICONFIG) - set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" - CACHE STRING "" FORCE) -else() - if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") - endif() -endif() - -set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") -set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") -set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") -set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") - -# Use Unicode for all projects. -add_definitions(-DUNICODE -D_UNICODE) - -# Compilation settings that should be applied to most targets. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_17) - target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") - target_compile_options(${TARGET} PRIVATE /EHsc) - target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") - target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") -endfunction() - -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") - -# Flutter library and tool build rules. -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# Application build -add_subdirectory("runner") - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# Support files are copied into place next to the executable, so that it can -# run in place. This is done instead of making a separate bundle (as on Linux) -# so that building and running from within Visual Studio will work. -set(BUILD_BUNDLE_DIR "$") -# Make the "install" step default, as it's required to run. -set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -if(PLUGIN_BUNDLED_LIBRARIES) - install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - CONFIGURATIONS Profile;Release - COMPONENT Runtime) +cmake_minimum_required(VERSION 3.14) +project(window_manager_example LANGUAGES CXX) + +set(BINARY_NAME "window_manager_example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/example/windows/flutter/CMakeLists.txt b/packages/window_manager/example/windows/flutter/CMakeLists.txt similarity index 97% rename from example/windows/flutter/CMakeLists.txt rename to packages/window_manager/example/windows/flutter/CMakeLists.txt index e7107414..4f2af69b 100644 --- a/example/windows/flutter/CMakeLists.txt +++ b/packages/window_manager/example/windows/flutter/CMakeLists.txt @@ -1,108 +1,108 @@ -cmake_minimum_required(VERSION 3.14) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. -set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") - -# Set fallback configurations for older versions of the flutter tool. -if (NOT DEFINED FLUTTER_TARGET_PLATFORM) - set(FLUTTER_TARGET_PLATFORM "windows-x64") -endif() - -# === Flutter Library === -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "flutter_export.h" - "flutter_windows.h" - "flutter_messenger.h" - "flutter_plugin_registrar.h" - "flutter_texture_registrar.h" -) -list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") -add_dependencies(flutter flutter_assemble) - -# === Wrapper === -list(APPEND CPP_WRAPPER_SOURCES_CORE - "core_implementations.cc" - "standard_codec.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_PLUGIN - "plugin_registrar.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_APP - "flutter_engine.cc" - "flutter_view_controller.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") - -# Wrapper sources needed for a plugin. -add_library(flutter_wrapper_plugin STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} -) -apply_standard_settings(flutter_wrapper_plugin) -set_target_properties(flutter_wrapper_plugin PROPERTIES - POSITION_INDEPENDENT_CODE ON) -set_target_properties(flutter_wrapper_plugin PROPERTIES - CXX_VISIBILITY_PRESET hidden) -target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) -target_include_directories(flutter_wrapper_plugin PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_plugin flutter_assemble) - -# Wrapper sources needed for the runner. -add_library(flutter_wrapper_app STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_APP} -) -apply_standard_settings(flutter_wrapper_app) -target_link_libraries(flutter_wrapper_app PUBLIC flutter) -target_include_directories(flutter_wrapper_app PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_app flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") -set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} - ${PHONY_OUTPUT} - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - ${FLUTTER_TARGET_PLATFORM} $ - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} -) +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/example/windows/runner/CMakeLists.txt b/packages/window_manager/example/windows/runner/CMakeLists.txt similarity index 97% rename from example/windows/runner/CMakeLists.txt rename to packages/window_manager/example/windows/runner/CMakeLists.txt index 945bda7a..de2d8916 100644 --- a/example/windows/runner/CMakeLists.txt +++ b/packages/window_manager/example/windows/runner/CMakeLists.txt @@ -1,17 +1,17 @@ -cmake_minimum_required(VERSION 3.14) -project(runner LANGUAGES CXX) - -add_executable(${BINARY_NAME} WIN32 - "flutter_window.cpp" - "main.cpp" - "utils.cpp" - "win32_window.cpp" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" - "Runner.rc" - "runner.exe.manifest" -) -apply_standard_settings(${BINARY_NAME}) -target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") -target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) -target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") -add_dependencies(${BINARY_NAME} flutter_assemble) +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/example/windows/runner/Runner.rc b/packages/window_manager/example/windows/runner/Runner.rc similarity index 96% rename from example/windows/runner/Runner.rc rename to packages/window_manager/example/windows/runner/Runner.rc index 9b09cecb..90549426 100644 --- a/example/windows/runner/Runner.rc +++ b/packages/window_manager/example/windows/runner/Runner.rc @@ -1,121 +1,121 @@ -// Microsoft Visual C++ generated resource script. -// -#pragma code_page(65001) -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_APP_ICON ICON "resources\\app_icon.ico" - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) -#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD -#else -#define VERSION_AS_NUMBER 1,0,0,0 -#endif - -#if defined(FLUTTER_VERSION) -#define VERSION_AS_STRING FLUTTER_VERSION -#else -#define VERSION_AS_STRING "1.0.0" -#endif - -VS_VERSION_INFO VERSIONINFO - FILEVERSION VERSION_AS_NUMBER - PRODUCTVERSION VERSION_AS_NUMBER - FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifdef _DEBUG - FILEFLAGS VS_FF_DEBUG -#else - FILEFLAGS 0x0L -#endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_APP - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904e4" - BEGIN - VALUE "CompanyName", "org.leanflutter.plugins" "\0" - VALUE "FileDescription", "window_manager_example" "\0" - VALUE "FileVersion", VERSION_AS_STRING "\0" - VALUE "InternalName", "window_manager_example" "\0" - VALUE "LegalCopyright", "Copyright (C) 2022 org.leanflutter.plugins. All rights reserved." "\0" - VALUE "OriginalFilename", "window_manager_example.exe" "\0" - VALUE "ProductName", "window_manager_example" "\0" - VALUE "ProductVersion", VERSION_AS_STRING "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "org.leanflutter.plugins" "\0" + VALUE "FileDescription", "window_manager_example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "window_manager_example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 org.leanflutter.plugins. All rights reserved." "\0" + VALUE "OriginalFilename", "window_manager_example.exe" "\0" + VALUE "ProductName", "window_manager_example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/example/windows/runner/flutter_window.cpp b/packages/window_manager/example/windows/runner/flutter_window.cpp similarity index 96% rename from example/windows/runner/flutter_window.cpp rename to packages/window_manager/example/windows/runner/flutter_window.cpp index 3a11b51d..b43b9095 100644 --- a/example/windows/runner/flutter_window.cpp +++ b/packages/window_manager/example/windows/runner/flutter_window.cpp @@ -1,61 +1,61 @@ -#include "flutter_window.h" - -#include - -#include "flutter/generated_plugin_registrant.h" - -FlutterWindow::FlutterWindow(const flutter::DartProject& project) - : project_(project) {} - -FlutterWindow::~FlutterWindow() {} - -bool FlutterWindow::OnCreate() { - if (!Win32Window::OnCreate()) { - return false; - } - - RECT frame = GetClientArea(); - - // The size here must match the window dimensions to avoid unnecessary surface - // creation / destruction in the startup path. - flutter_controller_ = std::make_unique( - frame.right - frame.left, frame.bottom - frame.top, project_); - // Ensure that basic setup of the controller was successful. - if (!flutter_controller_->engine() || !flutter_controller_->view()) { - return false; - } - RegisterPlugins(flutter_controller_->engine()); - SetChildContent(flutter_controller_->view()->GetNativeWindow()); - return true; -} - -void FlutterWindow::OnDestroy() { - if (flutter_controller_) { - flutter_controller_ = nullptr; - } - - Win32Window::OnDestroy(); -} - -LRESULT -FlutterWindow::MessageHandler(HWND hwnd, UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - // Give Flutter, including plugins, an opportunity to handle window messages. - if (flutter_controller_) { - std::optional result = - flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, - lparam); - if (result) { - return *result; - } - } - - switch (message) { - case WM_FONTCHANGE: - flutter_controller_->engine()->ReloadSystemFonts(); - break; - } - - return Win32Window::MessageHandler(hwnd, message, wparam, lparam); -} +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/example/windows/runner/flutter_window.h b/packages/window_manager/example/windows/runner/flutter_window.h similarity index 96% rename from example/windows/runner/flutter_window.h rename to packages/window_manager/example/windows/runner/flutter_window.h index 28c23839..6da0652f 100644 --- a/example/windows/runner/flutter_window.h +++ b/packages/window_manager/example/windows/runner/flutter_window.h @@ -1,33 +1,33 @@ -#ifndef RUNNER_FLUTTER_WINDOW_H_ -#define RUNNER_FLUTTER_WINDOW_H_ - -#include -#include - -#include - -#include "win32_window.h" - -// A window that does nothing but host a Flutter view. -class FlutterWindow : public Win32Window { - public: - // Creates a new FlutterWindow hosting a Flutter view running |project|. - explicit FlutterWindow(const flutter::DartProject& project); - virtual ~FlutterWindow(); - - protected: - // Win32Window: - bool OnCreate() override; - void OnDestroy() override; - LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, - LPARAM const lparam) noexcept override; - - private: - // The project to run. - flutter::DartProject project_; - - // The Flutter instance hosted by this window. - std::unique_ptr flutter_controller_; -}; - -#endif // RUNNER_FLUTTER_WINDOW_H_ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/example/windows/runner/main.cpp b/packages/window_manager/example/windows/runner/main.cpp similarity index 96% rename from example/windows/runner/main.cpp rename to packages/window_manager/example/windows/runner/main.cpp index aa4e9ec5..f310af45 100644 --- a/example/windows/runner/main.cpp +++ b/packages/window_manager/example/windows/runner/main.cpp @@ -1,43 +1,43 @@ -#include -#include -#include - -#include "flutter_window.h" -#include "utils.h" - -int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { - // Attach to console when present (e.g., 'flutter run') or create a - // new console when running with a debugger. - if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { - CreateAndAttachConsole(); - } - - // Initialize COM, so that it is available for use in the library and/or - // plugins. - ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - - flutter::DartProject project(L"data"); - - std::vector command_line_arguments = - GetCommandLineArguments(); - - project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); - - FlutterWindow window(project); - Win32Window::Point origin(10, 10); - Win32Window::Size size(1280, 720); - if (!window.CreateAndShow(L"window_manager_example", origin, size)) { - return EXIT_FAILURE; - } - window.SetQuitOnClose(true); - - ::MSG msg; - while (::GetMessage(&msg, nullptr, 0, 0)) { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } - - ::CoUninitialize(); - return EXIT_SUCCESS; -} +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"window_manager_example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/example/windows/runner/resource.h b/packages/window_manager/example/windows/runner/resource.h similarity index 96% rename from example/windows/runner/resource.h rename to packages/window_manager/example/windows/runner/resource.h index ddc7f3ef..66a65d1e 100644 --- a/example/windows/runner/resource.h +++ b/packages/window_manager/example/windows/runner/resource.h @@ -1,16 +1,16 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by Runner.rc -// -#define IDI_APP_ICON 101 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/example/windows/runner/resources/app_icon.ico b/packages/window_manager/example/windows/runner/resources/app_icon.ico similarity index 100% rename from example/windows/runner/resources/app_icon.ico rename to packages/window_manager/example/windows/runner/resources/app_icon.ico diff --git a/example/windows/runner/runner.exe.manifest b/packages/window_manager/example/windows/runner/runner.exe.manifest similarity index 97% rename from example/windows/runner/runner.exe.manifest rename to packages/window_manager/example/windows/runner/runner.exe.manifest index 2c680b8b..c977c4a4 100644 --- a/example/windows/runner/runner.exe.manifest +++ b/packages/window_manager/example/windows/runner/runner.exe.manifest @@ -1,20 +1,20 @@ - - - - - PerMonitorV2 - - - - - - - - - - - - - - - + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/example/windows/runner/utils.cpp b/packages/window_manager/example/windows/runner/utils.cpp similarity index 96% rename from example/windows/runner/utils.cpp rename to packages/window_manager/example/windows/runner/utils.cpp index 05b53c01..d19bdbbc 100644 --- a/example/windows/runner/utils.cpp +++ b/packages/window_manager/example/windows/runner/utils.cpp @@ -1,64 +1,64 @@ -#include "utils.h" - -#include -#include -#include -#include - -#include - -void CreateAndAttachConsole() { - if (::AllocConsole()) { - FILE *unused; - if (freopen_s(&unused, "CONOUT$", "w", stdout)) { - _dup2(_fileno(stdout), 1); - } - if (freopen_s(&unused, "CONOUT$", "w", stderr)) { - _dup2(_fileno(stdout), 2); - } - std::ios::sync_with_stdio(); - FlutterDesktopResyncOutputStreams(); - } -} - -std::vector GetCommandLineArguments() { - // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. - int argc; - wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); - if (argv == nullptr) { - return std::vector(); - } - - std::vector command_line_arguments; - - // Skip the first argument as it's the binary name. - for (int i = 1; i < argc; i++) { - command_line_arguments.push_back(Utf8FromUtf16(argv[i])); - } - - ::LocalFree(argv); - - return command_line_arguments; -} - -std::string Utf8FromUtf16(const wchar_t* utf16_string) { - if (utf16_string == nullptr) { - return std::string(); - } - int target_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, nullptr, 0, nullptr, nullptr); - if (target_length == 0) { - return std::string(); - } - std::string utf8_string; - utf8_string.resize(target_length); - int converted_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, utf8_string.data(), - target_length, nullptr, nullptr); - if (converted_length == 0) { - return std::string(); - } - return utf8_string; -} +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/example/windows/runner/utils.h b/packages/window_manager/example/windows/runner/utils.h similarity index 97% rename from example/windows/runner/utils.h rename to packages/window_manager/example/windows/runner/utils.h index 3f0e05cb..3879d547 100644 --- a/example/windows/runner/utils.h +++ b/packages/window_manager/example/windows/runner/utils.h @@ -1,19 +1,19 @@ -#ifndef RUNNER_UTILS_H_ -#define RUNNER_UTILS_H_ - -#include -#include - -// Creates a console for the process, and redirects stdout and stderr to -// it for both the runner and the Flutter library. -void CreateAndAttachConsole(); - -// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string -// encoded in UTF-8. Returns an empty std::string on failure. -std::string Utf8FromUtf16(const wchar_t* utf16_string); - -// Gets the command line arguments passed in as a std::vector, -// encoded in UTF-8. Returns an empty std::vector on failure. -std::vector GetCommandLineArguments(); - -#endif // RUNNER_UTILS_H_ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/example/windows/runner/win32_window.cpp b/packages/window_manager/example/windows/runner/win32_window.cpp similarity index 96% rename from example/windows/runner/win32_window.cpp rename to packages/window_manager/example/windows/runner/win32_window.cpp index f18c269b..3273c2c0 100644 --- a/example/windows/runner/win32_window.cpp +++ b/packages/window_manager/example/windows/runner/win32_window.cpp @@ -1,245 +1,245 @@ -#include "win32_window.h" - -#include - -#include "resource.h" - -namespace { - -constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; - -// The number of Win32Window objects that currently exist. -static int g_active_window_count = 0; - -using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); - -// Scale helper to convert logical scaler values to physical using passed in -// scale factor -int Scale(int source, double scale_factor) { - return static_cast(source * scale_factor); -} - -// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. -// This API is only needed for PerMonitor V1 awareness mode. -void EnableFullDpiSupportIfAvailable(HWND hwnd) { - HMODULE user32_module = LoadLibraryA("User32.dll"); - if (!user32_module) { - return; - } - auto enable_non_client_dpi_scaling = - reinterpret_cast( - GetProcAddress(user32_module, "EnableNonClientDpiScaling")); - if (enable_non_client_dpi_scaling != nullptr) { - enable_non_client_dpi_scaling(hwnd); - FreeLibrary(user32_module); - } -} - -} // namespace - -// Manages the Win32Window's window class registration. -class WindowClassRegistrar { - public: - ~WindowClassRegistrar() = default; - - // Returns the singleton registar instance. - static WindowClassRegistrar* GetInstance() { - if (!instance_) { - instance_ = new WindowClassRegistrar(); - } - return instance_; - } - - // Returns the name of the window class, registering the class if it hasn't - // previously been registered. - const wchar_t* GetWindowClass(); - - // Unregisters the window class. Should only be called if there are no - // instances of the window. - void UnregisterWindowClass(); - - private: - WindowClassRegistrar() = default; - - static WindowClassRegistrar* instance_; - - bool class_registered_ = false; -}; - -WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; - -const wchar_t* WindowClassRegistrar::GetWindowClass() { - if (!class_registered_) { - WNDCLASS window_class{}; - window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); - window_class.lpszClassName = kWindowClassName; - window_class.style = CS_HREDRAW | CS_VREDRAW; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = GetModuleHandle(nullptr); - window_class.hIcon = - LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); - window_class.hbrBackground = 0; - window_class.lpszMenuName = nullptr; - window_class.lpfnWndProc = Win32Window::WndProc; - RegisterClass(&window_class); - class_registered_ = true; - } - return kWindowClassName; -} - -void WindowClassRegistrar::UnregisterWindowClass() { - UnregisterClass(kWindowClassName, nullptr); - class_registered_ = false; -} - -Win32Window::Win32Window() { - ++g_active_window_count; -} - -Win32Window::~Win32Window() { - --g_active_window_count; - Destroy(); -} - -bool Win32Window::CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size) { - Destroy(); - - const wchar_t* window_class = - WindowClassRegistrar::GetInstance()->GetWindowClass(); - - const POINT target_point = {static_cast(origin.x), - static_cast(origin.y)}; - HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); - UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); - double scale_factor = dpi / 96.0; - - HWND window = CreateWindow( - window_class, title.c_str(), WS_OVERLAPPEDWINDOW, - Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), - Scale(size.width, scale_factor), Scale(size.height, scale_factor), - nullptr, nullptr, GetModuleHandle(nullptr), this); - - if (!window) { - return false; - } - - return OnCreate(); -} - -// static -LRESULT CALLBACK Win32Window::WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - if (message == WM_NCCREATE) { - auto window_struct = reinterpret_cast(lparam); - SetWindowLongPtr(window, GWLP_USERDATA, - reinterpret_cast(window_struct->lpCreateParams)); - - auto that = static_cast(window_struct->lpCreateParams); - EnableFullDpiSupportIfAvailable(window); - that->window_handle_ = window; - } else if (Win32Window* that = GetThisFromHandle(window)) { - return that->MessageHandler(window, message, wparam, lparam); - } - - return DefWindowProc(window, message, wparam, lparam); -} - -LRESULT -Win32Window::MessageHandler(HWND hwnd, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - switch (message) { - case WM_DESTROY: - window_handle_ = nullptr; - Destroy(); - if (quit_on_close_) { - PostQuitMessage(0); - } - return 0; - - case WM_DPICHANGED: { - auto newRectSize = reinterpret_cast(lparam); - LONG newWidth = newRectSize->right - newRectSize->left; - LONG newHeight = newRectSize->bottom - newRectSize->top; - - SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, - newHeight, SWP_NOZORDER | SWP_NOACTIVATE); - - return 0; - } - case WM_SIZE: { - RECT rect = GetClientArea(); - if (child_content_ != nullptr) { - // Size and position the child window. - MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, - rect.bottom - rect.top, TRUE); - } - return 0; - } - - case WM_ACTIVATE: - if (child_content_ != nullptr) { - SetFocus(child_content_); - } - return 0; - } - - return DefWindowProc(window_handle_, message, wparam, lparam); -} - -void Win32Window::Destroy() { - OnDestroy(); - - if (window_handle_) { - DestroyWindow(window_handle_); - window_handle_ = nullptr; - } - if (g_active_window_count == 0) { - WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); - } -} - -Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { - return reinterpret_cast( - GetWindowLongPtr(window, GWLP_USERDATA)); -} - -void Win32Window::SetChildContent(HWND content) { - child_content_ = content; - SetParent(content, window_handle_); - RECT frame = GetClientArea(); - - MoveWindow(content, frame.left, frame.top, frame.right - frame.left, - frame.bottom - frame.top, true); - - SetFocus(child_content_); -} - -RECT Win32Window::GetClientArea() { - RECT frame; - GetClientRect(window_handle_, &frame); - return frame; -} - -HWND Win32Window::GetHandle() { - return window_handle_; -} - -void Win32Window::SetQuitOnClose(bool quit_on_close) { - quit_on_close_ = quit_on_close; -} - -bool Win32Window::OnCreate() { - // No-op; provided for subclasses. - return true; -} - -void Win32Window::OnDestroy() { - // No-op; provided for subclasses. -} +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/example/windows/runner/win32_window.h b/packages/window_manager/example/windows/runner/win32_window.h similarity index 97% rename from example/windows/runner/win32_window.h rename to packages/window_manager/example/windows/runner/win32_window.h index d9bcac1b..17ba4311 100644 --- a/example/windows/runner/win32_window.h +++ b/packages/window_manager/example/windows/runner/win32_window.h @@ -1,98 +1,98 @@ -#ifndef RUNNER_WIN32_WINDOW_H_ -#define RUNNER_WIN32_WINDOW_H_ - -#include - -#include -#include -#include - -// A class abstraction for a high DPI-aware Win32 Window. Intended to be -// inherited from by classes that wish to specialize with custom -// rendering and input handling -class Win32Window { - public: - struct Point { - unsigned int x; - unsigned int y; - Point(unsigned int x, unsigned int y) : x(x), y(y) {} - }; - - struct Size { - unsigned int width; - unsigned int height; - Size(unsigned int width, unsigned int height) - : width(width), height(height) {} - }; - - Win32Window(); - virtual ~Win32Window(); - - // Creates and shows a win32 window with |title| and position and size using - // |origin| and |size|. New windows are created on the default monitor. Window - // sizes are specified to the OS in physical pixels, hence to ensure a - // consistent size to will treat the width height passed in to this function - // as logical pixels and scale to appropriate for the default monitor. Returns - // true if the window was created successfully. - bool CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size); - - // Release OS resources associated with window. - void Destroy(); - - // Inserts |content| into the window tree. - void SetChildContent(HWND content); - - // Returns the backing Window handle to enable clients to set icon and other - // window properties. Returns nullptr if the window has been destroyed. - HWND GetHandle(); - - // If true, closing this window will quit the application. - void SetQuitOnClose(bool quit_on_close); - - // Return a RECT representing the bounds of the current client area. - RECT GetClientArea(); - - protected: - // Processes and route salient window messages for mouse handling, - // size change and DPI. Delegates handling of these to member overloads that - // inheriting classes can handle. - virtual LRESULT MessageHandler(HWND window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Called when CreateAndShow is called, allowing subclass window-related - // setup. Subclasses should return false if setup fails. - virtual bool OnCreate(); - - // Called when Destroy is called. - virtual void OnDestroy(); - - private: - friend class WindowClassRegistrar; - - // OS callback called by message pump. Handles the WM_NCCREATE message which - // is passed when the non-client area is being created and enables automatic - // non-client DPI scaling so that the non-client area automatically - // responsponds to changes in DPI. All other messages are handled by - // MessageHandler. - static LRESULT CALLBACK WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Retrieves a class instance pointer for |window| - static Win32Window* GetThisFromHandle(HWND const window) noexcept; - - bool quit_on_close_ = false; - - // window handle for top level window. - HWND window_handle_ = nullptr; - - // window handle for hosted content. - HWND child_content_ = nullptr; -}; - -#endif // RUNNER_WIN32_WINDOW_H_ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/lib/src/resize_edge.dart b/packages/window_manager/lib/src/resize_edge.dart similarity index 100% rename from lib/src/resize_edge.dart rename to packages/window_manager/lib/src/resize_edge.dart diff --git a/lib/src/title_bar_style.dart b/packages/window_manager/lib/src/title_bar_style.dart similarity index 100% rename from lib/src/title_bar_style.dart rename to packages/window_manager/lib/src/title_bar_style.dart diff --git a/lib/src/utils/calc_window_position.dart b/packages/window_manager/lib/src/utils/calc_window_position.dart similarity index 100% rename from lib/src/utils/calc_window_position.dart rename to packages/window_manager/lib/src/utils/calc_window_position.dart diff --git a/lib/src/widgets/drag_to_move_area.dart b/packages/window_manager/lib/src/widgets/drag_to_move_area.dart similarity index 100% rename from lib/src/widgets/drag_to_move_area.dart rename to packages/window_manager/lib/src/widgets/drag_to_move_area.dart diff --git a/lib/src/widgets/drag_to_resize_area.dart b/packages/window_manager/lib/src/widgets/drag_to_resize_area.dart similarity index 100% rename from lib/src/widgets/drag_to_resize_area.dart rename to packages/window_manager/lib/src/widgets/drag_to_resize_area.dart diff --git a/lib/src/widgets/virtual_window_frame.dart b/packages/window_manager/lib/src/widgets/virtual_window_frame.dart similarity index 100% rename from lib/src/widgets/virtual_window_frame.dart rename to packages/window_manager/lib/src/widgets/virtual_window_frame.dart diff --git a/lib/src/widgets/window_caption.dart b/packages/window_manager/lib/src/widgets/window_caption.dart similarity index 100% rename from lib/src/widgets/window_caption.dart rename to packages/window_manager/lib/src/widgets/window_caption.dart diff --git a/lib/src/widgets/window_caption_button.dart b/packages/window_manager/lib/src/widgets/window_caption_button.dart similarity index 100% rename from lib/src/widgets/window_caption_button.dart rename to packages/window_manager/lib/src/widgets/window_caption_button.dart diff --git a/lib/src/window_listener.dart b/packages/window_manager/lib/src/window_listener.dart similarity index 100% rename from lib/src/window_listener.dart rename to packages/window_manager/lib/src/window_listener.dart diff --git a/lib/src/window_manager.dart b/packages/window_manager/lib/src/window_manager.dart similarity index 100% rename from lib/src/window_manager.dart rename to packages/window_manager/lib/src/window_manager.dart diff --git a/lib/src/window_options.dart b/packages/window_manager/lib/src/window_options.dart similarity index 100% rename from lib/src/window_options.dart rename to packages/window_manager/lib/src/window_options.dart diff --git a/lib/window_manager.dart b/packages/window_manager/lib/window_manager.dart similarity index 100% rename from lib/window_manager.dart rename to packages/window_manager/lib/window_manager.dart diff --git a/linux/CMakeLists.txt b/packages/window_manager/linux/CMakeLists.txt similarity index 100% rename from linux/CMakeLists.txt rename to packages/window_manager/linux/CMakeLists.txt diff --git a/linux/include/window_manager/window_manager_plugin.h b/packages/window_manager/linux/include/window_manager/window_manager_plugin.h similarity index 100% rename from linux/include/window_manager/window_manager_plugin.h rename to packages/window_manager/linux/include/window_manager/window_manager_plugin.h diff --git a/linux/window_manager_plugin.cc b/packages/window_manager/linux/window_manager_plugin.cc similarity index 100% rename from linux/window_manager_plugin.cc rename to packages/window_manager/linux/window_manager_plugin.cc diff --git a/macos/Classes/WindowManager.swift b/packages/window_manager/macos/Classes/WindowManager.swift similarity index 100% rename from macos/Classes/WindowManager.swift rename to packages/window_manager/macos/Classes/WindowManager.swift diff --git a/macos/Classes/WindowManagerPlugin.swift b/packages/window_manager/macos/Classes/WindowManagerPlugin.swift similarity index 100% rename from macos/Classes/WindowManagerPlugin.swift rename to packages/window_manager/macos/Classes/WindowManagerPlugin.swift diff --git a/macos/window_manager.podspec b/packages/window_manager/macos/window_manager.podspec similarity index 100% rename from macos/window_manager.podspec rename to packages/window_manager/macos/window_manager.podspec diff --git a/packages/window_manager/pubspec.yaml b/packages/window_manager/pubspec.yaml new file mode 100644 index 00000000..a881ece3 --- /dev/null +++ b/packages/window_manager/pubspec.yaml @@ -0,0 +1,42 @@ +name: window_manager +description: This plugin allows Flutter desktop apps to resizing and repositioning the window. +version: 0.4.3 +homepage: https://github.com/leanflutter/window_manager + +platforms: + linux: + macos: + windows: + +topics: + - window + - window-resize + - window-manager + - desktop + - desktop-window + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.3.0" + +dependencies: + flutter: + sdk: flutter + path: ^1.8.2 + screen_retriever: ^0.2.0 + +dev_dependencies: + dependency_validator: ^3.0.0 + flutter_test: + sdk: flutter + mostly_reasonable_lints: ^0.1.2 + +flutter: + plugin: + platforms: + linux: + pluginClass: WindowManagerPlugin + macos: + pluginClass: WindowManagerPlugin + windows: + pluginClass: WindowManagerPlugin diff --git a/test/window_manager_test.dart b/packages/window_manager/test/window_manager_test.dart similarity index 100% rename from test/window_manager_test.dart rename to packages/window_manager/test/window_manager_test.dart diff --git a/windows/.gitignore b/packages/window_manager/windows/.gitignore similarity index 94% rename from windows/.gitignore rename to packages/window_manager/windows/.gitignore index 808064a0..b3eb2be1 100644 --- a/windows/.gitignore +++ b/packages/window_manager/windows/.gitignore @@ -1,17 +1,17 @@ -flutter/ - -# Visual Studio user-specific files. -*.suo -*.user -*.userosscache -*.sln.docstates - -# Visual Studio build-related files. -x64/ -x86/ - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ +flutter/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/windows/CMakeLists.txt b/packages/window_manager/windows/CMakeLists.txt similarity index 97% rename from windows/CMakeLists.txt rename to packages/window_manager/windows/CMakeLists.txt index a5375226..cb09c10a 100644 --- a/windows/CMakeLists.txt +++ b/packages/window_manager/windows/CMakeLists.txt @@ -1,26 +1,26 @@ -cmake_minimum_required(VERSION 3.15) -set(PROJECT_NAME "window_manager") -project(${PROJECT_NAME} LANGUAGES CXX) - -# This value is used when generating builds using this plugin, so it must -# not be changed -set(PLUGIN_NAME "window_manager_plugin") - -add_library(${PLUGIN_NAME} SHARED - "window_manager.cpp" - "window_manager_plugin.cpp" -) -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_compile_definitions(${PLUGIN_NAME} PRIVATE _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING) -target_include_directories(${PLUGIN_NAME} INTERFACE - "${CMAKE_CURRENT_SOURCE_DIR}/include") -target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) - -# List of absolute paths to libraries that should be bundled with the plugin -set(window_manager_bundled_libraries - "" - PARENT_SCOPE -) +cmake_minimum_required(VERSION 3.15) +set(PROJECT_NAME "window_manager") +project(${PROJECT_NAME} LANGUAGES CXX) + +# This value is used when generating builds using this plugin, so it must +# not be changed +set(PLUGIN_NAME "window_manager_plugin") + +add_library(${PLUGIN_NAME} SHARED + "window_manager.cpp" + "window_manager_plugin.cpp" +) +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_compile_definitions(${PLUGIN_NAME} PRIVATE _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) + +# List of absolute paths to libraries that should be bundled with the plugin +set(window_manager_bundled_libraries + "" + PARENT_SCOPE +) diff --git a/windows/include/window_manager/window_manager_plugin.h b/packages/window_manager/windows/include/window_manager/window_manager_plugin.h similarity index 95% rename from windows/include/window_manager/window_manager_plugin.h rename to packages/window_manager/windows/include/window_manager/window_manager_plugin.h index c4c9e839..8de48046 100644 --- a/windows/include/window_manager/window_manager_plugin.h +++ b/packages/window_manager/windows/include/window_manager/window_manager_plugin.h @@ -1,25 +1,25 @@ -#ifndef FLUTTER_PLUGIN_WINDOW_MANAGER_PLUGIN_H_ -#define FLUTTER_PLUGIN_WINDOW_MANAGER_PLUGIN_H_ - -#include - -#include - -#ifdef FLUTTER_PLUGIN_IMPL -#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) -#else -#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) -#endif - -#if defined(__cplusplus) -extern "C" { -#endif - -FLUTTER_PLUGIN_EXPORT void WindowManagerPluginRegisterWithRegistrar( - FlutterDesktopPluginRegistrarRef registrar); - -#if defined(__cplusplus) -} // extern "C" -#endif - -#endif // FLUTTER_PLUGIN_WINDOW_MANAGER_PLUGIN_H_ +#ifndef FLUTTER_PLUGIN_WINDOW_MANAGER_PLUGIN_H_ +#define FLUTTER_PLUGIN_WINDOW_MANAGER_PLUGIN_H_ + +#include + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void WindowManagerPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_WINDOW_MANAGER_PLUGIN_H_ diff --git a/windows/window_manager.cpp b/packages/window_manager/windows/window_manager.cpp similarity index 100% rename from windows/window_manager.cpp rename to packages/window_manager/windows/window_manager.cpp diff --git a/windows/window_manager_plugin.cpp b/packages/window_manager/windows/window_manager_plugin.cpp similarity index 97% rename from windows/window_manager_plugin.cpp rename to packages/window_manager/windows/window_manager_plugin.cpp index 7d4a55eb..36e767ab 100644 --- a/windows/window_manager_plugin.cpp +++ b/packages/window_manager/windows/window_manager_plugin.cpp @@ -1,596 +1,596 @@ -#include "include/window_manager/window_manager_plugin.h" - -// This must be included before many other Windows headers. -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include "window_manager.cpp" - -namespace { - -bool IsWindows11OrGreater() { - DWORD dwVersion = 0; - DWORD dwBuild = 0; - -#pragma warning(push) -#pragma warning(disable : 4996) - dwVersion = GetVersion(); - // Get the build number. - if (dwVersion < 0x80000000) - dwBuild = (DWORD)(HIWORD(dwVersion)); -#pragma warning(pop) - - return dwBuild < 22000; -} - -std::unique_ptr< - flutter::MethodChannel, - std::default_delete>> - channel = nullptr; - -class WindowManagerPlugin : public flutter::Plugin { - public: - static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); - - WindowManagerPlugin(flutter::PluginRegistrarWindows* registrar); - - virtual ~WindowManagerPlugin(); - - private: - WindowManager* window_manager; - flutter::PluginRegistrarWindows* registrar; - - // The ID of the WindowProc delegate registration. - int window_proc_id = -1; - - void WindowManagerPlugin::_EmitEvent(std::string eventName); - // Called for top-level WindowProc delegation. - std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, - UINT message, - WPARAM wParam, - LPARAM lParam); - // Called when a method is called on this plugin's channel from Dart. - void HandleMethodCall( - const flutter::MethodCall& method_call, - std::unique_ptr> result); - - void adjustNCCALCSIZE(HWND hwnd, NCCALCSIZE_PARAMS* sz) { - LONG l = 8; - LONG t = 8; - - // HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); - // Don't use `MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST)` above. - // Because if the window is restored from minimized state, the window is not in the correct monitor. - // The monitor is always the left-most monitor. - // https://github.com/leanflutter/window_manager/issues/489 - HMONITOR monitor = MonitorFromRect(&sz->rgrc[0], MONITOR_DEFAULTTONEAREST); - if (monitor != NULL) { - MONITORINFO monitorInfo; - monitorInfo.cbSize = sizeof(MONITORINFO); - if (TRUE == GetMonitorInfo(monitor, &monitorInfo)) { - l = sz->rgrc[0].left - monitorInfo.rcWork.left; - t = sz->rgrc[0].top - monitorInfo.rcWork.top; - } else { - // GetMonitorInfo failed, use (8, 8) as default value - } - } else { - // unreachable code - } - - sz->rgrc[0].left -= l; - sz->rgrc[0].top -= t; - sz->rgrc[0].right += l; - sz->rgrc[0].bottom += t; - } -}; - -// static -void WindowManagerPlugin::RegisterWithRegistrar( - flutter::PluginRegistrarWindows* registrar) { - channel = std::make_unique>( - registrar->messenger(), "window_manager", - &flutter::StandardMethodCodec::GetInstance()); - - auto plugin = std::make_unique(registrar); - - channel->SetMethodCallHandler( - [plugin_pointer = plugin.get()](const auto& call, auto result) { - plugin_pointer->HandleMethodCall(call, std::move(result)); - }); - - registrar->AddPlugin(std::move(plugin)); -} - -WindowManagerPlugin::WindowManagerPlugin( - flutter::PluginRegistrarWindows* registrar) - : registrar(registrar) { - window_manager = new WindowManager(); - window_proc_id = registrar->RegisterTopLevelWindowProcDelegate( - [this](HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { - return HandleWindowProc(hWnd, message, wParam, lParam); - }); -} - -WindowManagerPlugin::~WindowManagerPlugin() { - registrar->UnregisterTopLevelWindowProcDelegate(window_proc_id); - channel = nullptr; -} - -void WindowManagerPlugin::_EmitEvent(std::string eventName) { - if (channel == nullptr) - return; - flutter::EncodableMap args = flutter::EncodableMap(); - args[flutter::EncodableValue("eventName")] = - flutter::EncodableValue(eventName); - channel->InvokeMethod("onEvent", - std::make_unique(args)); -} - -std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, - UINT message, - WPARAM wParam, - LPARAM lParam) { - std::optional result = std::nullopt; - - if (message == WM_DPICHANGED) { - window_manager->pixel_ratio_ = - (float)LOWORD(wParam) / USER_DEFAULT_SCREEN_DPI; - window_manager->ForceChildRefresh(); - } - - if (wParam && message == WM_NCCALCSIZE) { - if (window_manager->IsFullScreen() && - window_manager->title_bar_style_ != "normal") { - if (window_manager->is_frameless_) { - adjustNCCALCSIZE(hWnd, reinterpret_cast(lParam)); - } - return 0; - } - // This must always be before handling title_bar_style_ == "hidden" so - // the `if TitleBarStyle.hidden` doesn't get executed. - if (window_manager->is_frameless_) { - if (window_manager->IsMaximized()) { - adjustNCCALCSIZE(hWnd, reinterpret_cast(lParam)); - } - return 0; - } - - // This must always be last. - if (wParam && window_manager->title_bar_style_ == "hidden") { - if (window_manager->IsMaximized()) { - // Adjust the borders when maximized so the app isn't cut off - adjustNCCALCSIZE(hWnd, reinterpret_cast(lParam)); - } else { - NCCALCSIZE_PARAMS* sz = reinterpret_cast(lParam); - // on windows 10, if set to 0, there's a white line at the top - // of the app and I've yet to find a way to remove that. - sz->rgrc[0].top += IsWindows11OrGreater() ? 0 : 1; - // The following lines are required for resizing the window. - // https://github.com/leanflutter/window_manager/issues/483 - sz->rgrc[0].right -= 8; - sz->rgrc[0].bottom -= 8; - sz->rgrc[0].left -= -8; - } - - // Previously (WVR_HREDRAW | WVR_VREDRAW), but returning 0 or 1 doesn't - // actually break anything so I've set it to 0. Unless someone pointed a - // problem in the future. - return 0; - } - } else if (message == WM_NCHITTEST) { - if (!window_manager->is_resizable_) { - return HTNOWHERE; - } - } else if (message == WM_GETMINMAXINFO) { - MINMAXINFO* info = reinterpret_cast(lParam); - // For the special "unconstrained" values, leave the defaults. - if (window_manager->minimum_size_.x != 0) - info->ptMinTrackSize.x = static_cast( - window_manager->minimum_size_.x * window_manager->pixel_ratio_); - if (window_manager->minimum_size_.y != 0) - info->ptMinTrackSize.y = static_cast( - window_manager->minimum_size_.y * window_manager->pixel_ratio_); - if (window_manager->maximum_size_.x != -1) - info->ptMaxTrackSize.x = static_cast( - window_manager->maximum_size_.x * window_manager->pixel_ratio_); - if (window_manager->maximum_size_.y != -1) - info->ptMaxTrackSize.y = static_cast( - window_manager->maximum_size_.y * window_manager->pixel_ratio_); - result = 0; - } else if (message == WM_NCACTIVATE) { - if (wParam != 0) { - _EmitEvent("focus"); - } else { - _EmitEvent("blur"); - } - - if (window_manager->title_bar_style_ == "hidden" || - window_manager->is_frameless_) - return 1; - } else if (message == WM_EXITSIZEMOVE) { - if (window_manager->is_resizing_) { - _EmitEvent("resized"); - window_manager->is_resizing_ = false; - } - if (window_manager->is_moving_) { - _EmitEvent("moved"); - window_manager->is_moving_ = false; - } - return false; - } else if (message == WM_MOVING) { - window_manager->is_moving_ = true; - _EmitEvent("move"); - return false; - } else if (message == WM_SIZING) { - window_manager->is_resizing_ = true; - _EmitEvent("resize"); - - if (window_manager->aspect_ratio_ > 0) { - RECT* rect = (LPRECT)lParam; - - double aspect_ratio = window_manager->aspect_ratio_; - - int new_width = static_cast(rect->right - rect->left); - int new_height = static_cast(rect->bottom - rect->top); - - bool is_resizing_horizontally = - wParam == WMSZ_LEFT || wParam == WMSZ_RIGHT || - wParam == WMSZ_TOPLEFT || wParam == WMSZ_BOTTOMLEFT; - - if (is_resizing_horizontally) { - new_height = static_cast(new_width / aspect_ratio); - } else { - new_width = static_cast(new_height * aspect_ratio); - } - - int left = rect->left; - int top = rect->top; - int right = rect->right; - int bottom = rect->bottom; - - switch (wParam) { - case WMSZ_RIGHT: - case WMSZ_BOTTOM: - right = new_width + left; - bottom = top + new_height; - break; - case WMSZ_TOP: - right = new_width + left; - top = bottom - new_height; - break; - case WMSZ_LEFT: - case WMSZ_TOPLEFT: - left = right - new_width; - top = bottom - new_height; - break; - case WMSZ_TOPRIGHT: - right = left + new_width; - top = bottom - new_height; - break; - case WMSZ_BOTTOMLEFT: - left = right - new_width; - bottom = top + new_height; - break; - case WMSZ_BOTTOMRIGHT: - right = left + new_width; - bottom = top + new_height; - break; - } - - rect->left = left; - rect->top = top; - rect->right = right; - rect->bottom = bottom; - } - } else if (message == WM_SIZE) { - if (window_manager->IsFullScreen() && wParam == SIZE_MAXIMIZED && - window_manager->last_state != STATE_FULLSCREEN_ENTERED) { - _EmitEvent("enter-full-screen"); - window_manager->last_state = STATE_FULLSCREEN_ENTERED; - } else if (!window_manager->IsFullScreen() && wParam == SIZE_RESTORED && - window_manager->last_state == STATE_FULLSCREEN_ENTERED) { - window_manager->ForceChildRefresh(); - _EmitEvent("leave-full-screen"); - window_manager->last_state = STATE_NORMAL; - } else if (window_manager->last_state != STATE_FULLSCREEN_ENTERED) { - if (wParam == SIZE_MAXIMIZED) { - _EmitEvent("maximize"); - window_manager->last_state = STATE_MAXIMIZED; - } else if (wParam == SIZE_MINIMIZED) { - _EmitEvent("minimize"); - window_manager->last_state = STATE_MINIMIZED; - return 0; - } else if (wParam == SIZE_RESTORED) { - if (window_manager->last_state == STATE_MAXIMIZED) { - _EmitEvent("unmaximize"); - window_manager->last_state = STATE_NORMAL; - } else if (window_manager->last_state == STATE_MINIMIZED) { - _EmitEvent("restore"); - window_manager->last_state = STATE_NORMAL; - } - } - } - } else if (message == WM_CLOSE) { - _EmitEvent("close"); - if (window_manager->IsPreventClose()) { - return -1; - } - } else if (message == WM_SHOWWINDOW) { - if (wParam == TRUE) { - _EmitEvent("show"); - } else { - _EmitEvent("hide"); - } - } else if (message == WM_WINDOWPOSCHANGED) { - if (window_manager->IsAlwaysOnBottom()) { - const flutter::EncodableMap& args = { - {flutter::EncodableValue("isAlwaysOnBottom"), - flutter::EncodableValue(true)}}; - window_manager->SetAlwaysOnBottom(args); - } - } - - return result; -} - -void WindowManagerPlugin::HandleMethodCall( - const flutter::MethodCall& method_call, - std::unique_ptr> result) { - std::string method_name = method_call.method_name(); - - if (method_name.compare("ensureInitialized") == 0) { - window_manager->native_window = - ::GetAncestor(registrar->GetView()->GetNativeWindow(), GA_ROOT); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("waitUntilReadyToShow") == 0) { - window_manager->WaitUntilReadyToShow(); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("setAsFrameless") == 0) { - window_manager->SetAsFrameless(); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("destroy") == 0) { - window_manager->Destroy(); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("close") == 0) { - window_manager->Close(); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("isPreventClose") == 0) { - auto value = window_manager->IsPreventClose(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("setPreventClose") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetPreventClose(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("focus") == 0) { - window_manager->Focus(); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("blur") == 0) { - window_manager->Blur(); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("isFocused") == 0) { - bool value = window_manager->IsFocused(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("show") == 0) { - window_manager->Show(); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("hide") == 0) { - window_manager->Hide(); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("isVisible") == 0) { - bool value = window_manager->IsVisible(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("isMaximized") == 0) { - bool value = window_manager->IsMaximized(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("maximize") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->Maximize(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("unmaximize") == 0) { - window_manager->Unmaximize(); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("isMinimized") == 0) { - bool value = window_manager->IsMinimized(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("minimize") == 0) { - window_manager->Minimize(); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("restore") == 0) { - window_manager->Restore(); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("isDockable") == 0) { - bool value = window_manager->IsDockable(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("isDocked") == 0) { - int value = window_manager->IsDocked(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("dock") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->Dock(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("undock") == 0) { - bool value = window_manager->Undock(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("isFullScreen") == 0) { - bool value = window_manager->IsFullScreen(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("setFullScreen") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetFullScreen(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("setAspectRatio") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetAspectRatio(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("setBackgroundColor") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetBackgroundColor(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("getBounds") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - flutter::EncodableMap value = window_manager->GetBounds(args); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("setBounds") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetBounds(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("setMinimumSize") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetMinimumSize(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("setMaximumSize") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetMaximumSize(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("isResizable") == 0) { - bool value = window_manager->IsResizable(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("setResizable") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetResizable(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("isMinimizable") == 0) { - bool value = window_manager->IsMinimizable(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("setMinimizable") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetMinimizable(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("isMaximizable") == 0) { - bool value = window_manager->IsMaximizable(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("setMaximizable") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetMaximizable(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("isClosable") == 0) { - bool value = window_manager->IsClosable(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("setClosable") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetClosable(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("isAlwaysOnTop") == 0) { - bool value = window_manager->IsAlwaysOnTop(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("setAlwaysOnTop") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetAlwaysOnTop(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("isAlwaysOnBottom") == 0) { - bool value = window_manager->IsAlwaysOnBottom(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("setAlwaysOnBottom") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetAlwaysOnBottom(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("getTitle") == 0) { - std::string value = window_manager->GetTitle(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("setTitle") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetTitle(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("setTitleBarStyle") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetTitleBarStyle(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("getTitleBarHeight") == 0) { - int value = window_manager->GetTitleBarHeight(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("isSkipTaskbar") == 0) { - bool value = window_manager->IsSkipTaskbar(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("setSkipTaskbar") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetSkipTaskbar(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("setProgressBar") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetProgressBar(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("setIcon") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetIcon(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("hasShadow") == 0) { - bool value = window_manager->HasShadow(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("setHasShadow") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetHasShadow(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("getOpacity") == 0) { - double value = window_manager->GetOpacity(); - result->Success(flutter::EncodableValue(value)); - } else if (method_name.compare("setOpacity") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetOpacity(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("setBrightness") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetBrightness(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("setIgnoreMouseEvents") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->SetIgnoreMouseEvents(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("popUpWindowMenu") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->PopUpWindowMenu(args); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("startDragging") == 0) { - window_manager->StartDragging(); - result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("startResizing") == 0) { - const flutter::EncodableMap& args = - std::get(*method_call.arguments()); - window_manager->StartResizing(args); - result->Success(flutter::EncodableValue(true)); - } else { - result->NotImplemented(); - } -} - -} // namespace - -void WindowManagerPluginRegisterWithRegistrar( - FlutterDesktopPluginRegistrarRef registrar) { - WindowManagerPlugin::RegisterWithRegistrar( - flutter::PluginRegistrarManager::GetInstance() - ->GetRegistrar(registrar)); -} +#include "include/window_manager/window_manager_plugin.h" + +// This must be included before many other Windows headers. +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "window_manager.cpp" + +namespace { + +bool IsWindows11OrGreater() { + DWORD dwVersion = 0; + DWORD dwBuild = 0; + +#pragma warning(push) +#pragma warning(disable : 4996) + dwVersion = GetVersion(); + // Get the build number. + if (dwVersion < 0x80000000) + dwBuild = (DWORD)(HIWORD(dwVersion)); +#pragma warning(pop) + + return dwBuild < 22000; +} + +std::unique_ptr< + flutter::MethodChannel, + std::default_delete>> + channel = nullptr; + +class WindowManagerPlugin : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); + + WindowManagerPlugin(flutter::PluginRegistrarWindows* registrar); + + virtual ~WindowManagerPlugin(); + + private: + WindowManager* window_manager; + flutter::PluginRegistrarWindows* registrar; + + // The ID of the WindowProc delegate registration. + int window_proc_id = -1; + + void WindowManagerPlugin::_EmitEvent(std::string eventName); + // Called for top-level WindowProc delegation. + std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam); + // Called when a method is called on this plugin's channel from Dart. + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + void adjustNCCALCSIZE(HWND hwnd, NCCALCSIZE_PARAMS* sz) { + LONG l = 8; + LONG t = 8; + + // HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + // Don't use `MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST)` above. + // Because if the window is restored from minimized state, the window is not in the correct monitor. + // The monitor is always the left-most monitor. + // https://github.com/leanflutter/window_manager/issues/489 + HMONITOR monitor = MonitorFromRect(&sz->rgrc[0], MONITOR_DEFAULTTONEAREST); + if (monitor != NULL) { + MONITORINFO monitorInfo; + monitorInfo.cbSize = sizeof(MONITORINFO); + if (TRUE == GetMonitorInfo(monitor, &monitorInfo)) { + l = sz->rgrc[0].left - monitorInfo.rcWork.left; + t = sz->rgrc[0].top - monitorInfo.rcWork.top; + } else { + // GetMonitorInfo failed, use (8, 8) as default value + } + } else { + // unreachable code + } + + sz->rgrc[0].left -= l; + sz->rgrc[0].top -= t; + sz->rgrc[0].right += l; + sz->rgrc[0].bottom += t; + } +}; + +// static +void WindowManagerPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows* registrar) { + channel = std::make_unique>( + registrar->messenger(), "window_manager", + &flutter::StandardMethodCodec::GetInstance()); + + auto plugin = std::make_unique(registrar); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto& call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); +} + +WindowManagerPlugin::WindowManagerPlugin( + flutter::PluginRegistrarWindows* registrar) + : registrar(registrar) { + window_manager = new WindowManager(); + window_proc_id = registrar->RegisterTopLevelWindowProcDelegate( + [this](HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + return HandleWindowProc(hWnd, message, wParam, lParam); + }); +} + +WindowManagerPlugin::~WindowManagerPlugin() { + registrar->UnregisterTopLevelWindowProcDelegate(window_proc_id); + channel = nullptr; +} + +void WindowManagerPlugin::_EmitEvent(std::string eventName) { + if (channel == nullptr) + return; + flutter::EncodableMap args = flutter::EncodableMap(); + args[flutter::EncodableValue("eventName")] = + flutter::EncodableValue(eventName); + channel->InvokeMethod("onEvent", + std::make_unique(args)); +} + +std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam) { + std::optional result = std::nullopt; + + if (message == WM_DPICHANGED) { + window_manager->pixel_ratio_ = + (float)LOWORD(wParam) / USER_DEFAULT_SCREEN_DPI; + window_manager->ForceChildRefresh(); + } + + if (wParam && message == WM_NCCALCSIZE) { + if (window_manager->IsFullScreen() && + window_manager->title_bar_style_ != "normal") { + if (window_manager->is_frameless_) { + adjustNCCALCSIZE(hWnd, reinterpret_cast(lParam)); + } + return 0; + } + // This must always be before handling title_bar_style_ == "hidden" so + // the `if TitleBarStyle.hidden` doesn't get executed. + if (window_manager->is_frameless_) { + if (window_manager->IsMaximized()) { + adjustNCCALCSIZE(hWnd, reinterpret_cast(lParam)); + } + return 0; + } + + // This must always be last. + if (wParam && window_manager->title_bar_style_ == "hidden") { + if (window_manager->IsMaximized()) { + // Adjust the borders when maximized so the app isn't cut off + adjustNCCALCSIZE(hWnd, reinterpret_cast(lParam)); + } else { + NCCALCSIZE_PARAMS* sz = reinterpret_cast(lParam); + // on windows 10, if set to 0, there's a white line at the top + // of the app and I've yet to find a way to remove that. + sz->rgrc[0].top += IsWindows11OrGreater() ? 0 : 1; + // The following lines are required for resizing the window. + // https://github.com/leanflutter/window_manager/issues/483 + sz->rgrc[0].right -= 8; + sz->rgrc[0].bottom -= 8; + sz->rgrc[0].left -= -8; + } + + // Previously (WVR_HREDRAW | WVR_VREDRAW), but returning 0 or 1 doesn't + // actually break anything so I've set it to 0. Unless someone pointed a + // problem in the future. + return 0; + } + } else if (message == WM_NCHITTEST) { + if (!window_manager->is_resizable_) { + return HTNOWHERE; + } + } else if (message == WM_GETMINMAXINFO) { + MINMAXINFO* info = reinterpret_cast(lParam); + // For the special "unconstrained" values, leave the defaults. + if (window_manager->minimum_size_.x != 0) + info->ptMinTrackSize.x = static_cast( + window_manager->minimum_size_.x * window_manager->pixel_ratio_); + if (window_manager->minimum_size_.y != 0) + info->ptMinTrackSize.y = static_cast( + window_manager->minimum_size_.y * window_manager->pixel_ratio_); + if (window_manager->maximum_size_.x != -1) + info->ptMaxTrackSize.x = static_cast( + window_manager->maximum_size_.x * window_manager->pixel_ratio_); + if (window_manager->maximum_size_.y != -1) + info->ptMaxTrackSize.y = static_cast( + window_manager->maximum_size_.y * window_manager->pixel_ratio_); + result = 0; + } else if (message == WM_NCACTIVATE) { + if (wParam != 0) { + _EmitEvent("focus"); + } else { + _EmitEvent("blur"); + } + + if (window_manager->title_bar_style_ == "hidden" || + window_manager->is_frameless_) + return 1; + } else if (message == WM_EXITSIZEMOVE) { + if (window_manager->is_resizing_) { + _EmitEvent("resized"); + window_manager->is_resizing_ = false; + } + if (window_manager->is_moving_) { + _EmitEvent("moved"); + window_manager->is_moving_ = false; + } + return false; + } else if (message == WM_MOVING) { + window_manager->is_moving_ = true; + _EmitEvent("move"); + return false; + } else if (message == WM_SIZING) { + window_manager->is_resizing_ = true; + _EmitEvent("resize"); + + if (window_manager->aspect_ratio_ > 0) { + RECT* rect = (LPRECT)lParam; + + double aspect_ratio = window_manager->aspect_ratio_; + + int new_width = static_cast(rect->right - rect->left); + int new_height = static_cast(rect->bottom - rect->top); + + bool is_resizing_horizontally = + wParam == WMSZ_LEFT || wParam == WMSZ_RIGHT || + wParam == WMSZ_TOPLEFT || wParam == WMSZ_BOTTOMLEFT; + + if (is_resizing_horizontally) { + new_height = static_cast(new_width / aspect_ratio); + } else { + new_width = static_cast(new_height * aspect_ratio); + } + + int left = rect->left; + int top = rect->top; + int right = rect->right; + int bottom = rect->bottom; + + switch (wParam) { + case WMSZ_RIGHT: + case WMSZ_BOTTOM: + right = new_width + left; + bottom = top + new_height; + break; + case WMSZ_TOP: + right = new_width + left; + top = bottom - new_height; + break; + case WMSZ_LEFT: + case WMSZ_TOPLEFT: + left = right - new_width; + top = bottom - new_height; + break; + case WMSZ_TOPRIGHT: + right = left + new_width; + top = bottom - new_height; + break; + case WMSZ_BOTTOMLEFT: + left = right - new_width; + bottom = top + new_height; + break; + case WMSZ_BOTTOMRIGHT: + right = left + new_width; + bottom = top + new_height; + break; + } + + rect->left = left; + rect->top = top; + rect->right = right; + rect->bottom = bottom; + } + } else if (message == WM_SIZE) { + if (window_manager->IsFullScreen() && wParam == SIZE_MAXIMIZED && + window_manager->last_state != STATE_FULLSCREEN_ENTERED) { + _EmitEvent("enter-full-screen"); + window_manager->last_state = STATE_FULLSCREEN_ENTERED; + } else if (!window_manager->IsFullScreen() && wParam == SIZE_RESTORED && + window_manager->last_state == STATE_FULLSCREEN_ENTERED) { + window_manager->ForceChildRefresh(); + _EmitEvent("leave-full-screen"); + window_manager->last_state = STATE_NORMAL; + } else if (window_manager->last_state != STATE_FULLSCREEN_ENTERED) { + if (wParam == SIZE_MAXIMIZED) { + _EmitEvent("maximize"); + window_manager->last_state = STATE_MAXIMIZED; + } else if (wParam == SIZE_MINIMIZED) { + _EmitEvent("minimize"); + window_manager->last_state = STATE_MINIMIZED; + return 0; + } else if (wParam == SIZE_RESTORED) { + if (window_manager->last_state == STATE_MAXIMIZED) { + _EmitEvent("unmaximize"); + window_manager->last_state = STATE_NORMAL; + } else if (window_manager->last_state == STATE_MINIMIZED) { + _EmitEvent("restore"); + window_manager->last_state = STATE_NORMAL; + } + } + } + } else if (message == WM_CLOSE) { + _EmitEvent("close"); + if (window_manager->IsPreventClose()) { + return -1; + } + } else if (message == WM_SHOWWINDOW) { + if (wParam == TRUE) { + _EmitEvent("show"); + } else { + _EmitEvent("hide"); + } + } else if (message == WM_WINDOWPOSCHANGED) { + if (window_manager->IsAlwaysOnBottom()) { + const flutter::EncodableMap& args = { + {flutter::EncodableValue("isAlwaysOnBottom"), + flutter::EncodableValue(true)}}; + window_manager->SetAlwaysOnBottom(args); + } + } + + return result; +} + +void WindowManagerPlugin::HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result) { + std::string method_name = method_call.method_name(); + + if (method_name.compare("ensureInitialized") == 0) { + window_manager->native_window = + ::GetAncestor(registrar->GetView()->GetNativeWindow(), GA_ROOT); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("waitUntilReadyToShow") == 0) { + window_manager->WaitUntilReadyToShow(); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("setAsFrameless") == 0) { + window_manager->SetAsFrameless(); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("destroy") == 0) { + window_manager->Destroy(); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("close") == 0) { + window_manager->Close(); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("isPreventClose") == 0) { + auto value = window_manager->IsPreventClose(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("setPreventClose") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetPreventClose(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("focus") == 0) { + window_manager->Focus(); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("blur") == 0) { + window_manager->Blur(); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("isFocused") == 0) { + bool value = window_manager->IsFocused(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("show") == 0) { + window_manager->Show(); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("hide") == 0) { + window_manager->Hide(); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("isVisible") == 0) { + bool value = window_manager->IsVisible(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("isMaximized") == 0) { + bool value = window_manager->IsMaximized(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("maximize") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->Maximize(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("unmaximize") == 0) { + window_manager->Unmaximize(); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("isMinimized") == 0) { + bool value = window_manager->IsMinimized(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("minimize") == 0) { + window_manager->Minimize(); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("restore") == 0) { + window_manager->Restore(); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("isDockable") == 0) { + bool value = window_manager->IsDockable(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("isDocked") == 0) { + int value = window_manager->IsDocked(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("dock") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->Dock(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("undock") == 0) { + bool value = window_manager->Undock(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("isFullScreen") == 0) { + bool value = window_manager->IsFullScreen(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("setFullScreen") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetFullScreen(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("setAspectRatio") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetAspectRatio(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("setBackgroundColor") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetBackgroundColor(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("getBounds") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + flutter::EncodableMap value = window_manager->GetBounds(args); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("setBounds") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetBounds(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("setMinimumSize") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetMinimumSize(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("setMaximumSize") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetMaximumSize(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("isResizable") == 0) { + bool value = window_manager->IsResizable(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("setResizable") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetResizable(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("isMinimizable") == 0) { + bool value = window_manager->IsMinimizable(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("setMinimizable") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetMinimizable(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("isMaximizable") == 0) { + bool value = window_manager->IsMaximizable(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("setMaximizable") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetMaximizable(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("isClosable") == 0) { + bool value = window_manager->IsClosable(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("setClosable") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetClosable(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("isAlwaysOnTop") == 0) { + bool value = window_manager->IsAlwaysOnTop(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("setAlwaysOnTop") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetAlwaysOnTop(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("isAlwaysOnBottom") == 0) { + bool value = window_manager->IsAlwaysOnBottom(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("setAlwaysOnBottom") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetAlwaysOnBottom(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("getTitle") == 0) { + std::string value = window_manager->GetTitle(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("setTitle") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetTitle(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("setTitleBarStyle") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetTitleBarStyle(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("getTitleBarHeight") == 0) { + int value = window_manager->GetTitleBarHeight(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("isSkipTaskbar") == 0) { + bool value = window_manager->IsSkipTaskbar(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("setSkipTaskbar") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetSkipTaskbar(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("setProgressBar") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetProgressBar(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("setIcon") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetIcon(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("hasShadow") == 0) { + bool value = window_manager->HasShadow(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("setHasShadow") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetHasShadow(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("getOpacity") == 0) { + double value = window_manager->GetOpacity(); + result->Success(flutter::EncodableValue(value)); + } else if (method_name.compare("setOpacity") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetOpacity(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("setBrightness") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetBrightness(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("setIgnoreMouseEvents") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->SetIgnoreMouseEvents(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("popUpWindowMenu") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->PopUpWindowMenu(args); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("startDragging") == 0) { + window_manager->StartDragging(); + result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("startResizing") == 0) { + const flutter::EncodableMap& args = + std::get(*method_call.arguments()); + window_manager->StartResizing(args); + result->Success(flutter::EncodableValue(true)); + } else { + result->NotImplemented(); + } +} + +} // namespace + +void WindowManagerPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + WindowManagerPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/pubspec.lock b/pubspec.lock index 7ad2eb91..910c5e41 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,54 +1,62 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + ansi_styles: + dependency: transitive + description: + name: ansi_styles + sha256: "9c656cc12b3c27b17dd982b2cc5c0cfdfbdabd7bc8f3ae5e8542d9867b47ce8a" + url: "https://pub.dev" + source: hosted + version: "0.3.2+1" args: dependency: transitive description: name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.6.0" async: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" - build_config: + version: "2.1.2" + charcode: dependency: transitive description: - name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + name: charcode + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 url: "https://pub.dev" source: hosted - version: "1.1.1" - characters: + version: "1.3.1" + cli_launcher: dependency: transitive description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + name: cli_launcher + sha256: "5e7e0282b79e8642edd6510ee468ae2976d847a0a29b3916e85f5fa1bfe24005" url: "https://pub.dev" source: hosted - version: "1.3.0" - checked_yaml: + version: "0.3.1" + cli_util: dependency: transitive description: - name: checked_yaml - sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + name: cli_util + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "0.4.1" clock: dependency: transitive description: @@ -61,44 +69,26 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.dev" - source: hosted - version: "1.18.0" - dependency_validator: - dependency: "direct dev" - description: - name: dependency_validator - sha256: f727a5627aa405965fab4aef4f468e50a9b632ba0737fd2f98c932fec6d712b9 + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "3.2.3" - fake_async: + version: "1.19.1" + conventional_commit: dependency: transitive description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + name: conventional_commit + sha256: dec15ad1118f029c618651a4359eb9135d8b88f761aa24e4016d061cd45948f2 url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "0.6.0+1" file: dependency: transitive description: name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 url: "https://pub.dev" source: hosted - version: "7.0.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" + version: "7.0.1" glob: dependency: transitive description: @@ -107,6 +97,38 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + http: + dependency: transitive + description: + name: http + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + url: "https://pub.dev" + source: hosted + version: "1.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360" + url: "https://pub.dev" + source: hosted + version: "4.1.1" + intl: + dependency: transitive + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" io: dependency: transitive description: @@ -123,94 +145,78 @@ packages: url: "https://pub.dev" source: hosted version: "4.9.0" - leak_tracker: + matcher: dependency: transitive description: - name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "10.0.5" - leak_tracker_flutter_testing: - dependency: transitive + version: "0.12.17" + melos: + dependency: "direct dev" description: - name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + name: melos + sha256: a62abfa8c7826cec927f8585572bb9adf591be152150494d879ca2c75118809d url: "https://pub.dev" source: hosted - version: "3.0.5" - leak_tracker_testing: + version: "6.2.0" + meta: dependency: transitive description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "3.0.1" - lints: + version: "1.16.0" + mustache_template: dependency: transitive description: - name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" + name: mustache_template + sha256: a46e26f91445bfb0b60519be280555b06792460b27b19e2b19ad5b9740df5d1c url: "https://pub.dev" source: hosted - version: "4.0.0" - logging: + version: "2.0.0" + path: dependency: transitive description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.2.0" - matcher: + version: "1.9.1" + platform: dependency: transitive description: - name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" url: "https://pub.dev" source: hosted - version: "0.12.16+1" - material_color_utilities: + version: "3.1.6" + pool: dependency: transitive description: - name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" url: "https://pub.dev" source: hosted - version: "0.11.1" - meta: + version: "1.5.1" + process: dependency: transitive description: - name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 - url: "https://pub.dev" - source: hosted - version: "1.15.0" - mostly_reasonable_lints: - dependency: "direct dev" - description: - name: mostly_reasonable_lints - sha256: e19fec63536866ba307b3dfbc258b4bce9b3745129f038006b56b4067c6293d8 + name: process + sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" url: "https://pub.dev" source: hosted - version: "0.1.2" - package_config: + version: "5.0.2" + prompts: dependency: transitive description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + name: prompts + sha256: "3773b845e85a849f01e793c4fc18a45d52d7783b4cb6c0569fad19f9d0a774a1" url: "https://pub.dev" source: hosted - version: "2.1.0" - path: - dependency: "direct main" - description: - name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://pub.dev" - source: hosted - version: "1.9.0" + version: "2.0.0" pub_semver: dependency: transitive description: @@ -219,27 +225,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - pubspec_parse: + pub_updater: dependency: transitive description: - name: pubspec_parse - sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 + name: pub_updater + sha256: "54e8dc865349059ebe7f163d6acce7c89eb958b8047e6d6e80ce93b13d7c9e60" url: "https://pub.dev" source: hosted - version: "1.2.3" - screen_retriever: - dependency: "direct main" + version: "0.4.0" + pubspec: + dependency: transitive description: - name: screen_retriever - sha256: "6ee02c8a1158e6dae7ca430da79436e3b1c9563c8cf02f524af997c201ac2b90" + name: pubspec + sha256: f534a50a2b4d48dc3bc0ec147c8bd7c304280fff23b153f3f11803c4d49d927e url: "https://pub.dev" source: hosted - version: "0.1.9" - sky_engine: + version: "2.3.0" + quiver: dependency: transitive - description: flutter - source: sdk - version: "0.0.99" + description: + name: quiver + sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 + url: "https://pub.dev" + source: hosted + version: "3.2.2" source_span: dependency: transitive description: @@ -252,10 +261,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.0" stream_channel: dependency: transitive description: @@ -268,10 +277,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "0bd04f5bb74fcd6ff0606a888a30e917af9bd52820b178eaa464beb11dca84b6" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.0" term_glyph: dependency: transitive description: @@ -284,26 +293,34 @@ packages: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.2" - vector_math: + version: "0.7.3" + typed_data: dependency: transitive description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "2.1.4" - vm_service: + version: "1.4.0" + uri: dependency: transitive description: - name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + name: uri + sha256: "889eea21e953187c6099802b7b4cf5219ba8f3518f604a1033064d45b1b8268a" url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "1.0.0" + web: + dependency: transitive + description: + name: web + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + url: "https://pub.dev" + source: hosted + version: "1.1.0" yaml: dependency: transitive description: @@ -312,6 +329,13 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + yaml_edit: + dependency: transitive + description: + name: yaml_edit + sha256: e9c1a3543d2da0db3e90270dbb1e4eebc985ee5e3ffe468d83224472b2194a5f + url: "https://pub.dev" + source: hosted + version: "2.2.1" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.5.0 <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 9cdb4391..7b9be4ff 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,42 +1,9 @@ -name: window_manager -description: This plugin allows Flutter desktop apps to resizing and repositioning the window. -version: 0.4.2 +name: window_manager_workspace homepage: https://github.com/leanflutter/window_manager - -platforms: - linux: - macos: - windows: - -topics: - - window - - window-resize - - window-manager - - desktop - - desktop-window +publish_to: none environment: sdk: ">=3.0.0 <4.0.0" - flutter: ">=3.3.0" - -dependencies: - flutter: - sdk: flutter - path: ^1.8.2 - screen_retriever: ^0.1.9 dev_dependencies: - dependency_validator: ^3.0.0 - flutter_test: - sdk: flutter - mostly_reasonable_lints: ^0.1.2 - -flutter: - plugin: - platforms: - linux: - pluginClass: WindowManagerPlugin - macos: - pluginClass: WindowManagerPlugin - windows: - pluginClass: WindowManagerPlugin + melos: ^6.2.0 From 92e6a90e8843100a94f7261035859dca435fff38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Sun, 27 Oct 2024 10:14:46 +0800 Subject: [PATCH 30/50] wip: Add window_manager_platform_interface package --- LICENSE | 2 +- .../.gitignore | 29 ++++++++++++++++++ .../.metadata | 30 +++++++++++++++++++ .../CHANGELOG.md | 3 ++ .../window_manager_platform_interface/LICENSE | 21 +++++++++++++ .../README.md | 16 ++++++++++ .../analysis_options.yaml | 1 + .../src/window_manager_method_channel.dart | 17 +++++++++++ .../window_manager_platform_interface.dart | 28 +++++++++++++++++ .../window_manager_platform_interface.dart | 4 +++ .../pubspec.yaml | 18 +++++++++++ .../window_manager_method_channel_test.dart | 29 ++++++++++++++++++ 12 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 packages/window_manager_platform_interface/.gitignore create mode 100644 packages/window_manager_platform_interface/.metadata create mode 100644 packages/window_manager_platform_interface/CHANGELOG.md create mode 100644 packages/window_manager_platform_interface/LICENSE create mode 100644 packages/window_manager_platform_interface/README.md create mode 100644 packages/window_manager_platform_interface/analysis_options.yaml create mode 100644 packages/window_manager_platform_interface/lib/src/window_manager_method_channel.dart create mode 100644 packages/window_manager_platform_interface/lib/src/window_manager_platform_interface.dart create mode 100644 packages/window_manager_platform_interface/lib/window_manager_platform_interface.dart create mode 100644 packages/window_manager_platform_interface/pubspec.yaml create mode 100644 packages/window_manager_platform_interface/test/src/window_manager_method_channel_test.dart diff --git a/LICENSE b/LICENSE index 46e9d56a..2fdca2eb 100644 --- a/LICENSE +++ b/LICENSE @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/packages/window_manager_platform_interface/.gitignore b/packages/window_manager_platform_interface/.gitignore new file mode 100644 index 00000000..ac5aa989 --- /dev/null +++ b/packages/window_manager_platform_interface/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/packages/window_manager_platform_interface/.metadata b/packages/window_manager_platform_interface/.metadata new file mode 100644 index 00000000..b2763f65 --- /dev/null +++ b/packages/window_manager_platform_interface/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" + channel: "stable" + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 + base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 + - platform: macos + create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 + base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 + + # 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/packages/window_manager_platform_interface/CHANGELOG.md b/packages/window_manager_platform_interface/CHANGELOG.md new file mode 100644 index 00000000..d0bd041d --- /dev/null +++ b/packages/window_manager_platform_interface/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* Initial release. diff --git a/packages/window_manager_platform_interface/LICENSE b/packages/window_manager_platform_interface/LICENSE new file mode 100644 index 00000000..2fdca2eb --- /dev/null +++ b/packages/window_manager_platform_interface/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022-present LiJianying + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/window_manager_platform_interface/README.md b/packages/window_manager_platform_interface/README.md new file mode 100644 index 00000000..4ac89a87 --- /dev/null +++ b/packages/window_manager_platform_interface/README.md @@ -0,0 +1,16 @@ +# window_manager_platform_interface + +[![pub version][pub-image]][pub-url] + +[pub-image]: https://img.shields.io/pub/v/window_manager_platform_interface.svg +[pub-url]: https://pub.dev/packages/window_manager_platform_interface + +A common platform interface for the [window_manager](https://pub.dev/packages/window_manager) plugin. + +## Usage + +To implement a new platform-specific implementation of window_manager, extend `WindowManagerPlatform` with an implementation that performs the platform-specific behavior, and when you register your plugin, set the default `WindowManagerPlatform` by calling `WindowManagerPlatform.instance = MyPlatformWindowManager()`. + +## License + +[MIT](./LICENSE) diff --git a/packages/window_manager_platform_interface/analysis_options.yaml b/packages/window_manager_platform_interface/analysis_options.yaml new file mode 100644 index 00000000..9033bb29 --- /dev/null +++ b/packages/window_manager_platform_interface/analysis_options.yaml @@ -0,0 +1 @@ +include: package:mostly_reasonable_lints/analysis_options.yaml diff --git a/packages/window_manager_platform_interface/lib/src/window_manager_method_channel.dart b/packages/window_manager_platform_interface/lib/src/window_manager_method_channel.dart new file mode 100644 index 00000000..e02d42a0 --- /dev/null +++ b/packages/window_manager_platform_interface/lib/src/window_manager_method_channel.dart @@ -0,0 +1,17 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:window_manager_platform_interface/src/window_manager_platform_interface.dart'; + +/// An implementation of [WindowManagerPlatform] that uses method channels. +class MethodChannelWindowManager extends WindowManagerPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('window_manager'); + + @override + Future getPlatformVersion() async { + final version = + await methodChannel.invokeMethod('getPlatformVersion'); + return version; + } +} diff --git a/packages/window_manager_platform_interface/lib/src/window_manager_platform_interface.dart b/packages/window_manager_platform_interface/lib/src/window_manager_platform_interface.dart new file mode 100644 index 00000000..9715a034 --- /dev/null +++ b/packages/window_manager_platform_interface/lib/src/window_manager_platform_interface.dart @@ -0,0 +1,28 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:window_manager_platform_interface/src/window_manager_method_channel.dart'; + +abstract class WindowManagerPlatform extends PlatformInterface { + /// Constructs a WindowManagerPlatform. + WindowManagerPlatform() : super(token: _token); + + static final Object _token = Object(); + + static WindowManagerPlatform _instance = MethodChannelWindowManager(); + + /// The default instance of [WindowManagerPlatform] to use. + /// + /// Defaults to [MethodChannelWindowManager]. + static WindowManagerPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [WindowManagerPlatform] when + /// they register themselves. + static set instance(WindowManagerPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + Future getPlatformVersion() { + throw UnimplementedError('platformVersion() has not been implemented.'); + } +} diff --git a/packages/window_manager_platform_interface/lib/window_manager_platform_interface.dart b/packages/window_manager_platform_interface/lib/window_manager_platform_interface.dart new file mode 100644 index 00000000..57bbf8cc --- /dev/null +++ b/packages/window_manager_platform_interface/lib/window_manager_platform_interface.dart @@ -0,0 +1,4 @@ +library window_manager_platform_interface; + +export 'src/window_manager_method_channel.dart'; +export 'src/window_manager_platform_interface.dart'; diff --git a/packages/window_manager_platform_interface/pubspec.yaml b/packages/window_manager_platform_interface/pubspec.yaml new file mode 100644 index 00000000..92b2e170 --- /dev/null +++ b/packages/window_manager_platform_interface/pubspec.yaml @@ -0,0 +1,18 @@ +name: window_manager_platform_interface +description: A common platform interface for the window_manager plugin. +version: 0.0.1 +homepage: https://github.com/leanflutter/window_manager/blob/main/packages/window_manager_platform_interface + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.3.0" + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + mostly_reasonable_lints: ^0.1.2 diff --git a/packages/window_manager_platform_interface/test/src/window_manager_method_channel_test.dart b/packages/window_manager_platform_interface/test/src/window_manager_method_channel_test.dart new file mode 100644 index 00000000..b9b023f7 --- /dev/null +++ b/packages/window_manager_platform_interface/test/src/window_manager_method_channel_test.dart @@ -0,0 +1,29 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:window_manager_platform_interface/src/window_manager_method_channel.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + MethodChannelWindowManager platform = MethodChannelWindowManager(); + const MethodChannel channel = MethodChannel('window_manager'); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler( + channel, + (MethodCall methodCall) async { + return '42'; + }, + ); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('getPlatformVersion', () async { + expect(await platform.getPlatformVersion(), '42'); + }); +} From 547dfbf315708e56915f46a27d62ee7b8b1ed855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Sun, 27 Oct 2024 10:16:17 +0800 Subject: [PATCH 31/50] wip: Update lint workflow --- .github/workflows/lint.yml | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index db14caed..ce824ca0 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,9 +2,9 @@ name: lint on: push: - branches: [main] + branches: [main, dev] pull_request: - types: [opened, reopened] + branches: [main] jobs: analyze: @@ -13,8 +13,10 @@ jobs: - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2 with: + flutter-version: "3.24.3" channel: "stable" - - run: flutter analyze --fatal-infos + - uses: bluefireteam/melos-action@v3 + - run: melos run analyze format: runs-on: ubuntu-latest @@ -22,15 +24,8 @@ jobs: - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2 with: + flutter-version: "3.24.3" channel: "stable" - - run: dart format . --fix --set-exit-if-changed - - dependency_validator: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: subosito/flutter-action@v2 - with: - channel: "stable" - - run: flutter pub get - - run: flutter pub run dependency_validator + cache: true + - uses: bluefireteam/melos-action@v3 + - run: melos run format-check \ No newline at end of file From d0993bb2783c604cb5231fbb06e7089c9475c6da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Sun, 27 Oct 2024 10:21:11 +0800 Subject: [PATCH 32/50] wip: Update build & test workflows --- .github/workflows/build.yml | 63 ++++++++++++++++++++++++------------- .github/workflows/test.yml | 43 +++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 042c7e16..4fbfdad7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,44 +2,63 @@ name: build on: push: - branches: [main] + branches: [main, dev] pull_request: - types: [opened, reopened] + branches: [main] jobs: build-linux: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2 with: - channel: stable - flutter-version: 3.22.2 + flutter-version: "3.24.3" + channel: "stable" - run: | - sudo apt-get update -y - sudo apt-get install -y ninja-build libgtk-3-dev libappindicator3-dev xvfb - - run: flutter config --enable-linux-desktop - - run: cd example && flutter build linux -v - - run: cd example && xvfb-run -a flutter test integration_test -v + sudo apt-get update + sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev + sudo apt-get install -y keybinder-3.0 + - uses: bluefireteam/melos-action@v3 + - working-directory: ./packages/window_manager/example + run: | + flutter build linux --release + build-macos: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2 with: - channel: stable - flutter-version: 3.22.2 - - run: flutter config --enable-macos-desktop - - run: cd example && flutter build macos -v - # Blocked by https://github.com/flutter/flutter/issues/118469 - # - run: cd example && flutter test integration_test -v + flutter-version: "3.24.3" + channel: "stable" + - uses: bluefireteam/melos-action@v3 + - working-directory: ./packages/window_manager/example + run: | + flutter build macos --release + + build-web: + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + with: + flutter-version: "3.24.3" + channel: "stable" + - uses: bluefireteam/melos-action@v3 + - working-directory: ./packages/window_manager/example + run: | + flutter build web --release + build-windows: runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2 with: - channel: stable - flutter-version: 3.22.2 - - run: cd example && flutter build windows -v - - run: cd example && flutter test integration_test -v + flutter-version: "3.24.3" + channel: "stable" + - uses: bluefireteam/melos-action@v3 + - working-directory: ./packages/window_manager/example + run: | + flutter build windows --release diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..b98bbead --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,43 @@ +name: build + +on: + push: + branches: [main, dev] + pull_request: + types: [opened, reopened] + +jobs: + integration_test-linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: subosito/flutter-action@v2 + with: + channel: stable + flutter-version: "3.24.3" + - run: | + sudo apt-get update -y + sudo apt-get install -y ninja-build libgtk-3-dev libappindicator3-dev xvfb + - working-directory: ./packages/window_manager/example + run: xvfb-run -a flutter test integration_test -v + integration_test-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - uses: subosito/flutter-action@v2 + with: + channel: stable + flutter-version: "3.24.3" + # Blocked by https://github.com/flutter/flutter/issues/118469 + # - working-directory: ./packages/window_manager/example + # run: flutter test integration_test -v + integration_test-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - uses: subosito/flutter-action@v2 + with: + channel: stable + flutter-version: "3.24.3" + - working-directory: ./packages/window_manager/example + run: flutter test integration_test -v From dc758d1d41d2838287789932c4746ed4242c8748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Sun, 27 Oct 2024 10:22:36 +0800 Subject: [PATCH 33/50] wip: Update test workflow --- .github/workflows/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b98bbead..2c720b77 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: build +name: test on: push: @@ -18,6 +18,7 @@ jobs: - run: | sudo apt-get update -y sudo apt-get install -y ninja-build libgtk-3-dev libappindicator3-dev xvfb + - uses: bluefireteam/melos-action@v3 - working-directory: ./packages/window_manager/example run: xvfb-run -a flutter test integration_test -v integration_test-macos: @@ -28,6 +29,7 @@ jobs: with: channel: stable flutter-version: "3.24.3" + - uses: bluefireteam/melos-action@v3 # Blocked by https://github.com/flutter/flutter/issues/118469 # - working-directory: ./packages/window_manager/example # run: flutter test integration_test -v @@ -39,5 +41,6 @@ jobs: with: channel: stable flutter-version: "3.24.3" + - uses: bluefireteam/melos-action@v3 - working-directory: ./packages/window_manager/example run: flutter test integration_test -v From 8c3acfc6dd9a07474b50aedf90106379eae01281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=81=A5=E8=8E=B9?= Date: Sun, 27 Oct 2024 10:34:17 +0800 Subject: [PATCH 34/50] wip: --- .github/workflows/build.yml | 2 +- .github/workflows/test.yml | 2 +- packages/window_manager_linux/.metadata | 27 -------- packages/window_manager_linux/CHANGELOG.md | 2 +- .../analysis_options.yaml | 5 +- .../lib/window_manager_linux.dart | 14 ---- .../window_manager_linux_method_channel.dart | 17 ----- ...ndow_manager_linux_platform_interface.dart | 29 --------- packages/window_manager_linux/pubspec.yaml | 64 ++----------------- ...dow_manager_linux_method_channel_test.dart | 27 -------- .../test/window_manager_linux_test.dart | 29 --------- packages/window_manager_macos/.metadata | 30 --------- packages/window_manager_macos/CHANGELOG.md | 2 +- .../analysis_options.yaml | 5 +- .../lib/window_manager_macos.dart | 8 --- .../window_manager_macos_method_channel.dart | 17 ----- ...ndow_manager_macos_platform_interface.dart | 29 --------- .../Classes/WindowManagerMacosPlugin.swift | 19 ------ .../macos/window_manager_macos.podspec | 23 ------- packages/window_manager_macos/pubspec.yaml | 53 ++------------- ...dow_manager_macos_method_channel_test.dart | 27 -------- .../test/window_manager_macos_test.dart | 29 --------- .../CHANGELOG.md | 2 +- .../pubspec.yaml | 2 +- packages/window_manager_windows/.metadata | 27 -------- packages/window_manager_windows/CHANGELOG.md | 2 +- .../analysis_options.yaml | 5 +- .../lib/window_manager_windows.dart | 14 ---- ...window_manager_windows_method_channel.dart | 17 ----- ...ow_manager_windows_platform_interface.dart | 29 --------- packages/window_manager_windows/pubspec.yaml | 63 ++---------------- ...w_manager_windows_method_channel_test.dart | 27 -------- .../test/window_manager_windows_test.dart | 29 --------- 33 files changed, 27 insertions(+), 650 deletions(-) delete mode 100644 packages/window_manager_linux/.metadata delete mode 100644 packages/window_manager_linux/lib/window_manager_linux.dart delete mode 100644 packages/window_manager_linux/lib/window_manager_linux_method_channel.dart delete mode 100644 packages/window_manager_linux/lib/window_manager_linux_platform_interface.dart delete mode 100644 packages/window_manager_linux/test/window_manager_linux_method_channel_test.dart delete mode 100644 packages/window_manager_linux/test/window_manager_linux_test.dart delete mode 100644 packages/window_manager_macos/.metadata delete mode 100644 packages/window_manager_macos/lib/window_manager_macos.dart delete mode 100644 packages/window_manager_macos/lib/window_manager_macos_method_channel.dart delete mode 100644 packages/window_manager_macos/lib/window_manager_macos_platform_interface.dart delete mode 100644 packages/window_manager_macos/macos/Classes/WindowManagerMacosPlugin.swift delete mode 100644 packages/window_manager_macos/macos/window_manager_macos.podspec delete mode 100644 packages/window_manager_macos/test/window_manager_macos_method_channel_test.dart delete mode 100644 packages/window_manager_macos/test/window_manager_macos_test.dart delete mode 100644 packages/window_manager_windows/.metadata delete mode 100644 packages/window_manager_windows/lib/window_manager_windows.dart delete mode 100644 packages/window_manager_windows/lib/window_manager_windows_method_channel.dart delete mode 100644 packages/window_manager_windows/lib/window_manager_windows_platform_interface.dart delete mode 100644 packages/window_manager_windows/test/window_manager_windows_method_channel_test.dart delete mode 100644 packages/window_manager_windows/test/window_manager_windows_test.dart diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4fbfdad7..e41eeb00 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: - run: | sudo apt-get update sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev - sudo apt-get install -y keybinder-3.0 + sudo apt-get install -y ayatana-appindicator3-0.1 - uses: bluefireteam/melos-action@v3 - working-directory: ./packages/window_manager/example run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2c720b77..88fb1685 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: flutter-version: "3.24.3" - run: | sudo apt-get update -y - sudo apt-get install -y ninja-build libgtk-3-dev libappindicator3-dev xvfb + sudo apt-get install -y ninja-build libgtk-3-dev ayatana-appindicator3-0.1 xvfb - uses: bluefireteam/melos-action@v3 - working-directory: ./packages/window_manager/example run: xvfb-run -a flutter test integration_test -v diff --git a/packages/window_manager_linux/.metadata b/packages/window_manager_linux/.metadata deleted file mode 100644 index 17ceedf5..00000000 --- a/packages/window_manager_linux/.metadata +++ /dev/null @@ -1,27 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" - channel: "stable" - -project_type: plugin - -# Tracks metadata for the flutter migrate command -migration: - platforms: - - platform: root - create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 - base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 - - # 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/packages/window_manager_linux/CHANGELOG.md b/packages/window_manager_linux/CHANGELOG.md index d0bd041d..7928521c 100644 --- a/packages/window_manager_linux/CHANGELOG.md +++ b/packages/window_manager_linux/CHANGELOG.md @@ -1,3 +1,3 @@ -## 0.0.1 +## 1.0.1 * Initial release. diff --git a/packages/window_manager_linux/analysis_options.yaml b/packages/window_manager_linux/analysis_options.yaml index a5744c1c..9033bb29 100644 --- a/packages/window_manager_linux/analysis_options.yaml +++ b/packages/window_manager_linux/analysis_options.yaml @@ -1,4 +1 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options +include: package:mostly_reasonable_lints/analysis_options.yaml diff --git a/packages/window_manager_linux/lib/window_manager_linux.dart b/packages/window_manager_linux/lib/window_manager_linux.dart deleted file mode 100644 index 8efb5817..00000000 --- a/packages/window_manager_linux/lib/window_manager_linux.dart +++ /dev/null @@ -1,14 +0,0 @@ -// You have generated a new plugin project without specifying the `--platforms` -// flag. A plugin project with no platform support was generated. To add a -// platform, run `flutter create -t plugin --platforms .` under the -// same directory. You can also find a detailed instruction on how to add -// platforms in the `pubspec.yaml` at -// https://flutter.dev/to/pubspec-plugin-platforms. - -import 'window_manager_linux_platform_interface.dart'; - -class WindowManagerLinux { - Future getPlatformVersion() { - return WindowManagerLinuxPlatform.instance.getPlatformVersion(); - } -} diff --git a/packages/window_manager_linux/lib/window_manager_linux_method_channel.dart b/packages/window_manager_linux/lib/window_manager_linux_method_channel.dart deleted file mode 100644 index fdc4cbd5..00000000 --- a/packages/window_manager_linux/lib/window_manager_linux_method_channel.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; - -import 'window_manager_linux_platform_interface.dart'; - -/// An implementation of [WindowManagerLinuxPlatform] that uses method channels. -class MethodChannelWindowManagerLinux extends WindowManagerLinuxPlatform { - /// The method channel used to interact with the native platform. - @visibleForTesting - final methodChannel = const MethodChannel('window_manager_linux'); - - @override - Future getPlatformVersion() async { - final version = await methodChannel.invokeMethod('getPlatformVersion'); - return version; - } -} diff --git a/packages/window_manager_linux/lib/window_manager_linux_platform_interface.dart b/packages/window_manager_linux/lib/window_manager_linux_platform_interface.dart deleted file mode 100644 index d2f89a0c..00000000 --- a/packages/window_manager_linux/lib/window_manager_linux_platform_interface.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -import 'window_manager_linux_method_channel.dart'; - -abstract class WindowManagerLinuxPlatform extends PlatformInterface { - /// Constructs a WindowManagerLinuxPlatform. - WindowManagerLinuxPlatform() : super(token: _token); - - static final Object _token = Object(); - - static WindowManagerLinuxPlatform _instance = MethodChannelWindowManagerLinux(); - - /// The default instance of [WindowManagerLinuxPlatform] to use. - /// - /// Defaults to [MethodChannelWindowManagerLinux]. - static WindowManagerLinuxPlatform get instance => _instance; - - /// Platform-specific implementations should set this with their own - /// platform-specific class that extends [WindowManagerLinuxPlatform] when - /// they register themselves. - static set instance(WindowManagerLinuxPlatform instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - Future getPlatformVersion() { - throw UnimplementedError('platformVersion() has not been implemented.'); - } -} diff --git a/packages/window_manager_linux/pubspec.yaml b/packages/window_manager_linux/pubspec.yaml index bc14f77d..a7f39989 100644 --- a/packages/window_manager_linux/pubspec.yaml +++ b/packages/window_manager_linux/pubspec.yaml @@ -1,11 +1,11 @@ name: window_manager_linux description: Linux implementation of the window_manager plugin. -version: 0.0.1 +version: 1.0.1 repository: https://github.com/leanflutter/window_manager/tree/main/packages/window_manager_linux environment: - sdk: ^3.5.3 - flutter: '>=3.3.0' + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.3.0" dependencies: flutter: @@ -15,61 +15,11 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^4.0.0 + mostly_reasonable_lints: ^0.1.2 -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. flutter: - # This section identifies this Flutter project as a plugin project. - # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) - # which should be registered in the plugin registry. This is required for - # using method channels. - # The Android 'package' specifies package in which the registered class is. - # This is required for using method channels on Android. - # The 'ffiPlugin' specifies that native code should be built and bundled. - # This is required for using `dart:ffi`. - # All these are used by the tooling to maintain consistency when - # adding or updating assets for this project. plugin: platforms: - # This plugin project was generated without specifying any - # platforms with the `--platform` argument. If you see the `some_platform` map below, remove it and - # then add platforms following the instruction here: - # https://flutter.dev/to/pubspec-plugin-platforms - # ------------------- - some_platform: - pluginClass: somePluginClass - # ------------------- - - # To add assets to your plugin package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/to/asset-from-package - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/to/resolution-aware-images - - # To add custom fonts to your plugin package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/to/font-from-package + linux: + pluginClass: WindowManagerLinuxPlugin + \ No newline at end of file diff --git a/packages/window_manager_linux/test/window_manager_linux_method_channel_test.dart b/packages/window_manager_linux/test/window_manager_linux_method_channel_test.dart deleted file mode 100644 index 8cb565d7..00000000 --- a/packages/window_manager_linux/test/window_manager_linux_method_channel_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:window_manager_linux/window_manager_linux_method_channel.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - MethodChannelWindowManagerLinux platform = MethodChannelWindowManagerLinux(); - const MethodChannel channel = MethodChannel('window_manager_linux'); - - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler( - channel, - (MethodCall methodCall) async { - return '42'; - }, - ); - }); - - tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); - }); - - test('getPlatformVersion', () async { - expect(await platform.getPlatformVersion(), '42'); - }); -} diff --git a/packages/window_manager_linux/test/window_manager_linux_test.dart b/packages/window_manager_linux/test/window_manager_linux_test.dart deleted file mode 100644 index 3fbc0c58..00000000 --- a/packages/window_manager_linux/test/window_manager_linux_test.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:window_manager_linux/window_manager_linux.dart'; -import 'package:window_manager_linux/window_manager_linux_platform_interface.dart'; -import 'package:window_manager_linux/window_manager_linux_method_channel.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -class MockWindowManagerLinuxPlatform - with MockPlatformInterfaceMixin - implements WindowManagerLinuxPlatform { - - @override - Future getPlatformVersion() => Future.value('42'); -} - -void main() { - final WindowManagerLinuxPlatform initialPlatform = WindowManagerLinuxPlatform.instance; - - test('$MethodChannelWindowManagerLinux is the default instance', () { - expect(initialPlatform, isInstanceOf()); - }); - - test('getPlatformVersion', () async { - WindowManagerLinux windowManagerLinuxPlugin = WindowManagerLinux(); - MockWindowManagerLinuxPlatform fakePlatform = MockWindowManagerLinuxPlatform(); - WindowManagerLinuxPlatform.instance = fakePlatform; - - expect(await windowManagerLinuxPlugin.getPlatformVersion(), '42'); - }); -} diff --git a/packages/window_manager_macos/.metadata b/packages/window_manager_macos/.metadata deleted file mode 100644 index b2763f65..00000000 --- a/packages/window_manager_macos/.metadata +++ /dev/null @@ -1,30 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" - channel: "stable" - -project_type: plugin - -# Tracks metadata for the flutter migrate command -migration: - platforms: - - platform: root - create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 - base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 - - platform: macos - create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 - base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 - - # 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/packages/window_manager_macos/CHANGELOG.md b/packages/window_manager_macos/CHANGELOG.md index d0bd041d..7928521c 100644 --- a/packages/window_manager_macos/CHANGELOG.md +++ b/packages/window_manager_macos/CHANGELOG.md @@ -1,3 +1,3 @@ -## 0.0.1 +## 1.0.1 * Initial release. diff --git a/packages/window_manager_macos/analysis_options.yaml b/packages/window_manager_macos/analysis_options.yaml index a5744c1c..9033bb29 100644 --- a/packages/window_manager_macos/analysis_options.yaml +++ b/packages/window_manager_macos/analysis_options.yaml @@ -1,4 +1 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options +include: package:mostly_reasonable_lints/analysis_options.yaml diff --git a/packages/window_manager_macos/lib/window_manager_macos.dart b/packages/window_manager_macos/lib/window_manager_macos.dart deleted file mode 100644 index 6fb70378..00000000 --- a/packages/window_manager_macos/lib/window_manager_macos.dart +++ /dev/null @@ -1,8 +0,0 @@ - -import 'window_manager_macos_platform_interface.dart'; - -class WindowManagerMacos { - Future getPlatformVersion() { - return WindowManagerMacosPlatform.instance.getPlatformVersion(); - } -} diff --git a/packages/window_manager_macos/lib/window_manager_macos_method_channel.dart b/packages/window_manager_macos/lib/window_manager_macos_method_channel.dart deleted file mode 100644 index 64fc778d..00000000 --- a/packages/window_manager_macos/lib/window_manager_macos_method_channel.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; - -import 'window_manager_macos_platform_interface.dart'; - -/// An implementation of [WindowManagerMacosPlatform] that uses method channels. -class MethodChannelWindowManagerMacos extends WindowManagerMacosPlatform { - /// The method channel used to interact with the native platform. - @visibleForTesting - final methodChannel = const MethodChannel('window_manager_macos'); - - @override - Future getPlatformVersion() async { - final version = await methodChannel.invokeMethod('getPlatformVersion'); - return version; - } -} diff --git a/packages/window_manager_macos/lib/window_manager_macos_platform_interface.dart b/packages/window_manager_macos/lib/window_manager_macos_platform_interface.dart deleted file mode 100644 index 0ed17eda..00000000 --- a/packages/window_manager_macos/lib/window_manager_macos_platform_interface.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -import 'window_manager_macos_method_channel.dart'; - -abstract class WindowManagerMacosPlatform extends PlatformInterface { - /// Constructs a WindowManagerMacosPlatform. - WindowManagerMacosPlatform() : super(token: _token); - - static final Object _token = Object(); - - static WindowManagerMacosPlatform _instance = MethodChannelWindowManagerMacos(); - - /// The default instance of [WindowManagerMacosPlatform] to use. - /// - /// Defaults to [MethodChannelWindowManagerMacos]. - static WindowManagerMacosPlatform get instance => _instance; - - /// Platform-specific implementations should set this with their own - /// platform-specific class that extends [WindowManagerMacosPlatform] when - /// they register themselves. - static set instance(WindowManagerMacosPlatform instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - Future getPlatformVersion() { - throw UnimplementedError('platformVersion() has not been implemented.'); - } -} diff --git a/packages/window_manager_macos/macos/Classes/WindowManagerMacosPlugin.swift b/packages/window_manager_macos/macos/Classes/WindowManagerMacosPlugin.swift deleted file mode 100644 index 1452f679..00000000 --- a/packages/window_manager_macos/macos/Classes/WindowManagerMacosPlugin.swift +++ /dev/null @@ -1,19 +0,0 @@ -import Cocoa -import FlutterMacOS - -public class WindowManagerMacosPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "window_manager_macos", binaryMessenger: registrar.messenger) - let instance = WindowManagerMacosPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - switch call.method { - case "getPlatformVersion": - result("macOS " + ProcessInfo.processInfo.operatingSystemVersionString) - default: - result(FlutterMethodNotImplemented) - } - } -} diff --git a/packages/window_manager_macos/macos/window_manager_macos.podspec b/packages/window_manager_macos/macos/window_manager_macos.podspec deleted file mode 100644 index 21585b55..00000000 --- a/packages/window_manager_macos/macos/window_manager_macos.podspec +++ /dev/null @@ -1,23 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint window_manager_macos.podspec` to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'window_manager_macos' - s.version = '0.0.1' - s.summary = 'A new Flutter plugin project.' - s.description = <<-DESC -A new Flutter plugin project. - DESC - s.homepage = 'http://example.com' - s.license = { :file => '../LICENSE' } - s.author = { 'Your Company' => 'email@example.com' } - - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.dependency 'FlutterMacOS' - - s.platform = :osx, '10.11' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } - s.swift_version = '5.0' -end diff --git a/packages/window_manager_macos/pubspec.yaml b/packages/window_manager_macos/pubspec.yaml index 31fe4c05..a50b723a 100644 --- a/packages/window_manager_macos/pubspec.yaml +++ b/packages/window_manager_macos/pubspec.yaml @@ -1,11 +1,11 @@ name: window_manager_macos description: macOS implementation of the window_manager plugin. -version: 0.0.1 +version: 1.0.1 repository: https://github.com/leanflutter/window_manager/tree/main/packages/window_manager_macos environment: - sdk: ^3.5.3 - flutter: '>=3.3.0' + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.3.0" dependencies: flutter: @@ -15,55 +15,10 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^4.0.0 + mostly_reasonable_lints: ^0.1.2 -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. flutter: - # This section identifies this Flutter project as a plugin project. - # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) - # which should be registered in the plugin registry. This is required for - # using method channels. - # The Android 'package' specifies package in which the registered class is. - # This is required for using method channels on Android. - # The 'ffiPlugin' specifies that native code should be built and bundled. - # This is required for using `dart:ffi`. - # All these are used by the tooling to maintain consistency when - # adding or updating assets for this project. plugin: platforms: macos: pluginClass: WindowManagerMacosPlugin - - # To add assets to your plugin package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/to/asset-from-package - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/to/resolution-aware-images - - # To add custom fonts to your plugin package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/to/font-from-package diff --git a/packages/window_manager_macos/test/window_manager_macos_method_channel_test.dart b/packages/window_manager_macos/test/window_manager_macos_method_channel_test.dart deleted file mode 100644 index 35a2bc23..00000000 --- a/packages/window_manager_macos/test/window_manager_macos_method_channel_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:window_manager_macos/window_manager_macos_method_channel.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - MethodChannelWindowManagerMacos platform = MethodChannelWindowManagerMacos(); - const MethodChannel channel = MethodChannel('window_manager_macos'); - - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler( - channel, - (MethodCall methodCall) async { - return '42'; - }, - ); - }); - - tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); - }); - - test('getPlatformVersion', () async { - expect(await platform.getPlatformVersion(), '42'); - }); -} diff --git a/packages/window_manager_macos/test/window_manager_macos_test.dart b/packages/window_manager_macos/test/window_manager_macos_test.dart deleted file mode 100644 index 1db03bca..00000000 --- a/packages/window_manager_macos/test/window_manager_macos_test.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:window_manager_macos/window_manager_macos.dart'; -import 'package:window_manager_macos/window_manager_macos_platform_interface.dart'; -import 'package:window_manager_macos/window_manager_macos_method_channel.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -class MockWindowManagerMacosPlatform - with MockPlatformInterfaceMixin - implements WindowManagerMacosPlatform { - - @override - Future getPlatformVersion() => Future.value('42'); -} - -void main() { - final WindowManagerMacosPlatform initialPlatform = WindowManagerMacosPlatform.instance; - - test('$MethodChannelWindowManagerMacos is the default instance', () { - expect(initialPlatform, isInstanceOf()); - }); - - test('getPlatformVersion', () async { - WindowManagerMacos windowManagerMacosPlugin = WindowManagerMacos(); - MockWindowManagerMacosPlatform fakePlatform = MockWindowManagerMacosPlatform(); - WindowManagerMacosPlatform.instance = fakePlatform; - - expect(await windowManagerMacosPlugin.getPlatformVersion(), '42'); - }); -} diff --git a/packages/window_manager_platform_interface/CHANGELOG.md b/packages/window_manager_platform_interface/CHANGELOG.md index d0bd041d..7928521c 100644 --- a/packages/window_manager_platform_interface/CHANGELOG.md +++ b/packages/window_manager_platform_interface/CHANGELOG.md @@ -1,3 +1,3 @@ -## 0.0.1 +## 1.0.1 * Initial release. diff --git a/packages/window_manager_platform_interface/pubspec.yaml b/packages/window_manager_platform_interface/pubspec.yaml index 92b2e170..17529dd1 100644 --- a/packages/window_manager_platform_interface/pubspec.yaml +++ b/packages/window_manager_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: window_manager_platform_interface description: A common platform interface for the window_manager plugin. -version: 0.0.1 +version: 1.0.1 homepage: https://github.com/leanflutter/window_manager/blob/main/packages/window_manager_platform_interface environment: diff --git a/packages/window_manager_windows/.metadata b/packages/window_manager_windows/.metadata deleted file mode 100644 index 17ceedf5..00000000 --- a/packages/window_manager_windows/.metadata +++ /dev/null @@ -1,27 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" - channel: "stable" - -project_type: plugin - -# Tracks metadata for the flutter migrate command -migration: - platforms: - - platform: root - create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 - base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 - - # 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/packages/window_manager_windows/CHANGELOG.md b/packages/window_manager_windows/CHANGELOG.md index d0bd041d..7928521c 100644 --- a/packages/window_manager_windows/CHANGELOG.md +++ b/packages/window_manager_windows/CHANGELOG.md @@ -1,3 +1,3 @@ -## 0.0.1 +## 1.0.1 * Initial release. diff --git a/packages/window_manager_windows/analysis_options.yaml b/packages/window_manager_windows/analysis_options.yaml index a5744c1c..9033bb29 100644 --- a/packages/window_manager_windows/analysis_options.yaml +++ b/packages/window_manager_windows/analysis_options.yaml @@ -1,4 +1 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options +include: package:mostly_reasonable_lints/analysis_options.yaml diff --git a/packages/window_manager_windows/lib/window_manager_windows.dart b/packages/window_manager_windows/lib/window_manager_windows.dart deleted file mode 100644 index 6ea6f783..00000000 --- a/packages/window_manager_windows/lib/window_manager_windows.dart +++ /dev/null @@ -1,14 +0,0 @@ -// You have generated a new plugin project without specifying the `--platforms` -// flag. A plugin project with no platform support was generated. To add a -// platform, run `flutter create -t plugin --platforms .` under the -// same directory. You can also find a detailed instruction on how to add -// platforms in the `pubspec.yaml` at -// https://flutter.dev/to/pubspec-plugin-platforms. - -import 'window_manager_windows_platform_interface.dart'; - -class WindowManagerWindows { - Future getPlatformVersion() { - return WindowManagerWindowsPlatform.instance.getPlatformVersion(); - } -} diff --git a/packages/window_manager_windows/lib/window_manager_windows_method_channel.dart b/packages/window_manager_windows/lib/window_manager_windows_method_channel.dart deleted file mode 100644 index 2f8143cf..00000000 --- a/packages/window_manager_windows/lib/window_manager_windows_method_channel.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; - -import 'window_manager_windows_platform_interface.dart'; - -/// An implementation of [WindowManagerWindowsPlatform] that uses method channels. -class MethodChannelWindowManagerWindows extends WindowManagerWindowsPlatform { - /// The method channel used to interact with the native platform. - @visibleForTesting - final methodChannel = const MethodChannel('window_manager_windows'); - - @override - Future getPlatformVersion() async { - final version = await methodChannel.invokeMethod('getPlatformVersion'); - return version; - } -} diff --git a/packages/window_manager_windows/lib/window_manager_windows_platform_interface.dart b/packages/window_manager_windows/lib/window_manager_windows_platform_interface.dart deleted file mode 100644 index 69ab6a31..00000000 --- a/packages/window_manager_windows/lib/window_manager_windows_platform_interface.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -import 'window_manager_windows_method_channel.dart'; - -abstract class WindowManagerWindowsPlatform extends PlatformInterface { - /// Constructs a WindowManagerWindowsPlatform. - WindowManagerWindowsPlatform() : super(token: _token); - - static final Object _token = Object(); - - static WindowManagerWindowsPlatform _instance = MethodChannelWindowManagerWindows(); - - /// The default instance of [WindowManagerWindowsPlatform] to use. - /// - /// Defaults to [MethodChannelWindowManagerWindows]. - static WindowManagerWindowsPlatform get instance => _instance; - - /// Platform-specific implementations should set this with their own - /// platform-specific class that extends [WindowManagerWindowsPlatform] when - /// they register themselves. - static set instance(WindowManagerWindowsPlatform instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - Future getPlatformVersion() { - throw UnimplementedError('platformVersion() has not been implemented.'); - } -} diff --git a/packages/window_manager_windows/pubspec.yaml b/packages/window_manager_windows/pubspec.yaml index 891dc2f8..6278e9f2 100644 --- a/packages/window_manager_windows/pubspec.yaml +++ b/packages/window_manager_windows/pubspec.yaml @@ -1,11 +1,11 @@ name: window_manager_windows description: Windows implementation of the window_manager plugin. -version: 0.0.1 +version: 1.0.1 repository: https://github.com/leanflutter/window_manager/tree/main/packages/window_manager_windows environment: - sdk: ^3.5.3 - flutter: '>=3.3.0' + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.3.0" dependencies: flutter: @@ -15,61 +15,10 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^4.0.0 + mostly_reasonable_lints: ^0.1.2 -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. flutter: - # This section identifies this Flutter project as a plugin project. - # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) - # which should be registered in the plugin registry. This is required for - # using method channels. - # The Android 'package' specifies package in which the registered class is. - # This is required for using method channels on Android. - # The 'ffiPlugin' specifies that native code should be built and bundled. - # This is required for using `dart:ffi`. - # All these are used by the tooling to maintain consistency when - # adding or updating assets for this project. plugin: platforms: - # This plugin project was generated without specifying any - # platforms with the `--platform` argument. If you see the `some_platform` map below, remove it and - # then add platforms following the instruction here: - # https://flutter.dev/to/pubspec-plugin-platforms - # ------------------- - some_platform: - pluginClass: somePluginClass - # ------------------- - - # To add assets to your plugin package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/to/asset-from-package - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/to/resolution-aware-images - - # To add custom fonts to your plugin package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/to/font-from-package + windows: + pluginClass: WindowManagerWindowsPlugin diff --git a/packages/window_manager_windows/test/window_manager_windows_method_channel_test.dart b/packages/window_manager_windows/test/window_manager_windows_method_channel_test.dart deleted file mode 100644 index a85b6f1b..00000000 --- a/packages/window_manager_windows/test/window_manager_windows_method_channel_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:window_manager_windows/window_manager_windows_method_channel.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - MethodChannelWindowManagerWindows platform = MethodChannelWindowManagerWindows(); - const MethodChannel channel = MethodChannel('window_manager_windows'); - - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler( - channel, - (MethodCall methodCall) async { - return '42'; - }, - ); - }); - - tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); - }); - - test('getPlatformVersion', () async { - expect(await platform.getPlatformVersion(), '42'); - }); -} diff --git a/packages/window_manager_windows/test/window_manager_windows_test.dart b/packages/window_manager_windows/test/window_manager_windows_test.dart deleted file mode 100644 index 6b643b0a..00000000 --- a/packages/window_manager_windows/test/window_manager_windows_test.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:window_manager_windows/window_manager_windows.dart'; -import 'package:window_manager_windows/window_manager_windows_platform_interface.dart'; -import 'package:window_manager_windows/window_manager_windows_method_channel.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -class MockWindowManagerWindowsPlatform - with MockPlatformInterfaceMixin - implements WindowManagerWindowsPlatform { - - @override - Future getPlatformVersion() => Future.value('42'); -} - -void main() { - final WindowManagerWindowsPlatform initialPlatform = WindowManagerWindowsPlatform.instance; - - test('$MethodChannelWindowManagerWindows is the default instance', () { - expect(initialPlatform, isInstanceOf()); - }); - - test('getPlatformVersion', () async { - WindowManagerWindows windowManagerWindowsPlugin = WindowManagerWindows(); - MockWindowManagerWindowsPlatform fakePlatform = MockWindowManagerWindowsPlatform(); - WindowManagerWindowsPlatform.instance = fakePlatform; - - expect(await windowManagerWindowsPlugin.getPlatformVersion(), '42'); - }); -} From a91131015a4d35cfe8d19471fd2621846dba87f7 Mon Sep 17 00:00:00 2001 From: Mohammed CHAHBOUN <69054810+M97Chahboun@users.noreply.github.com> Date: Fri, 22 Nov 2024 15:02:15 +0100 Subject: [PATCH 35/50] Fixed Eyes Care app link (Update README.md) (#509) * Update README.md * Fixed Eyes Care app link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ac2e7ea..54106dc8 100644 --- a/README.md +++ b/README.md @@ -518,7 +518,7 @@ class _HomePageState extends State with WindowListener { - [RustDesk](https://github.com/rustdesk/rustdesk) - Yet another remote desktop software, written in Rust. Works out of the box, no configuration required. - [Ubuntu Desktop Installer](https://github.com/canonical/ubuntu-desktop-installer) - This project is a modern implementation of the Ubuntu Desktop installer. - [UniControlHub](https://github.com/rohitsangwan01/uni_control_hub) - Seamlessly bridge your Desktop and Mobile devices -- [EyesCare](bixat.dev/products/EyesCare) - A light-weight application following 20 rule adherence for optimum eye health +- [EyesCare](https://bixat.dev/products/EyesCare) - A light-weight application following 20 rule adherence for optimum eye health ## API From 3678a0acdc1eda5f28dce647f96e6cf7b0e799e6 Mon Sep 17 00:00:00 2001 From: Daniel Mircea Date: Fri, 22 Nov 2024 17:31:50 +0200 Subject: [PATCH 36/50] Initialize window_hints to fix minimum size setting in release mode (#510) When compiling in release mode for linux, the window size constraints (ie. `setMinimumSize`) weren't being enforced. This happened because the `window_hints` variable was not initialized and contained garbage data (while it was zero in debug mode). --- packages/window_manager/linux/window_manager_plugin.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/window_manager/linux/window_manager_plugin.cc b/packages/window_manager/linux/window_manager_plugin.cc index b1839577..7093ca49 100644 --- a/packages/window_manager/linux/window_manager_plugin.cc +++ b/packages/window_manager/linux/window_manager_plugin.cc @@ -1092,6 +1092,7 @@ void window_manager_plugin_register_with_registrar( plugin->window_geometry.min_height = -1; plugin->window_geometry.max_width = G_MAXINT; plugin->window_geometry.max_height = G_MAXINT; + plugin->window_hints = static_cast(0); // Disconnect all delete-event handlers first in flutter 3.10.1, which causes delete_event not working. // Issues from flutter/engine: https://github.com/flutter/engine/pull/40033 From 77f82f30ea5e863dc317967d93eecffdbef43a9d Mon Sep 17 00:00:00 2001 From: LiJianying Date: Sun, 23 Mar 2025 09:45:56 +0800 Subject: [PATCH 37/50] Remove federated plugin packages --- packages/window_manager_linux/.gitignore | 29 ------------------ packages/window_manager_linux/CHANGELOG.md | 3 -- packages/window_manager_linux/LICENSE | 21 ------------- packages/window_manager_linux/README.md | 12 -------- .../analysis_options.yaml | 1 - packages/window_manager_linux/pubspec.yaml | 25 ---------------- packages/window_manager_macos/.gitignore | 29 ------------------ packages/window_manager_macos/CHANGELOG.md | 3 -- packages/window_manager_macos/LICENSE | 21 ------------- packages/window_manager_macos/README.md | 12 -------- .../analysis_options.yaml | 1 - packages/window_manager_macos/pubspec.yaml | 24 --------------- .../.gitignore | 29 ------------------ .../.metadata | 30 ------------------- .../CHANGELOG.md | 3 -- .../window_manager_platform_interface/LICENSE | 21 ------------- .../README.md | 16 ---------- .../analysis_options.yaml | 1 - .../src/window_manager_method_channel.dart | 17 ----------- .../window_manager_platform_interface.dart | 28 ----------------- .../window_manager_platform_interface.dart | 4 --- .../pubspec.yaml | 18 ----------- .../window_manager_method_channel_test.dart | 29 ------------------ packages/window_manager_windows/.gitignore | 29 ------------------ packages/window_manager_windows/CHANGELOG.md | 3 -- packages/window_manager_windows/LICENSE | 21 ------------- packages/window_manager_windows/README.md | 12 -------- .../analysis_options.yaml | 1 - packages/window_manager_windows/pubspec.yaml | 24 --------------- 29 files changed, 467 deletions(-) delete mode 100644 packages/window_manager_linux/.gitignore delete mode 100644 packages/window_manager_linux/CHANGELOG.md delete mode 100644 packages/window_manager_linux/LICENSE delete mode 100644 packages/window_manager_linux/README.md delete mode 100644 packages/window_manager_linux/analysis_options.yaml delete mode 100644 packages/window_manager_linux/pubspec.yaml delete mode 100644 packages/window_manager_macos/.gitignore delete mode 100644 packages/window_manager_macos/CHANGELOG.md delete mode 100644 packages/window_manager_macos/LICENSE delete mode 100644 packages/window_manager_macos/README.md delete mode 100644 packages/window_manager_macos/analysis_options.yaml delete mode 100644 packages/window_manager_macos/pubspec.yaml delete mode 100644 packages/window_manager_platform_interface/.gitignore delete mode 100644 packages/window_manager_platform_interface/.metadata delete mode 100644 packages/window_manager_platform_interface/CHANGELOG.md delete mode 100644 packages/window_manager_platform_interface/LICENSE delete mode 100644 packages/window_manager_platform_interface/README.md delete mode 100644 packages/window_manager_platform_interface/analysis_options.yaml delete mode 100644 packages/window_manager_platform_interface/lib/src/window_manager_method_channel.dart delete mode 100644 packages/window_manager_platform_interface/lib/src/window_manager_platform_interface.dart delete mode 100644 packages/window_manager_platform_interface/lib/window_manager_platform_interface.dart delete mode 100644 packages/window_manager_platform_interface/pubspec.yaml delete mode 100644 packages/window_manager_platform_interface/test/src/window_manager_method_channel_test.dart delete mode 100644 packages/window_manager_windows/.gitignore delete mode 100644 packages/window_manager_windows/CHANGELOG.md delete mode 100644 packages/window_manager_windows/LICENSE delete mode 100644 packages/window_manager_windows/README.md delete mode 100644 packages/window_manager_windows/analysis_options.yaml delete mode 100644 packages/window_manager_windows/pubspec.yaml diff --git a/packages/window_manager_linux/.gitignore b/packages/window_manager_linux/.gitignore deleted file mode 100644 index ac5aa989..00000000 --- a/packages/window_manager_linux/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. -/pubspec.lock -**/doc/api/ -.dart_tool/ -build/ diff --git a/packages/window_manager_linux/CHANGELOG.md b/packages/window_manager_linux/CHANGELOG.md deleted file mode 100644 index 7928521c..00000000 --- a/packages/window_manager_linux/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 1.0.1 - -* Initial release. diff --git a/packages/window_manager_linux/LICENSE b/packages/window_manager_linux/LICENSE deleted file mode 100644 index 9b1046a8..00000000 --- a/packages/window_manager_linux/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 LiJianying - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/window_manager_linux/README.md b/packages/window_manager_linux/README.md deleted file mode 100644 index 2445cc0e..00000000 --- a/packages/window_manager_linux/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# window_manager_linux - -[![pub version][pub-image]][pub-url] - -[pub-image]: https://img.shields.io/pub/v/window_manager_linux.svg -[pub-url]: https://pub.dev/packages/window_manager_linux - -The Windows implementation of [window_manager](https://pub.dev/packages/window_manager). - -## License - -[MIT](./LICENSE) diff --git a/packages/window_manager_linux/analysis_options.yaml b/packages/window_manager_linux/analysis_options.yaml deleted file mode 100644 index 9033bb29..00000000 --- a/packages/window_manager_linux/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: package:mostly_reasonable_lints/analysis_options.yaml diff --git a/packages/window_manager_linux/pubspec.yaml b/packages/window_manager_linux/pubspec.yaml deleted file mode 100644 index a7f39989..00000000 --- a/packages/window_manager_linux/pubspec.yaml +++ /dev/null @@ -1,25 +0,0 @@ -name: window_manager_linux -description: Linux implementation of the window_manager plugin. -version: 1.0.1 -repository: https://github.com/leanflutter/window_manager/tree/main/packages/window_manager_linux - -environment: - sdk: ">=3.0.0 <4.0.0" - flutter: ">=3.3.0" - -dependencies: - flutter: - sdk: flutter - plugin_platform_interface: ^2.0.2 - -dev_dependencies: - flutter_test: - sdk: flutter - mostly_reasonable_lints: ^0.1.2 - -flutter: - plugin: - platforms: - linux: - pluginClass: WindowManagerLinuxPlugin - \ No newline at end of file diff --git a/packages/window_manager_macos/.gitignore b/packages/window_manager_macos/.gitignore deleted file mode 100644 index ac5aa989..00000000 --- a/packages/window_manager_macos/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. -/pubspec.lock -**/doc/api/ -.dart_tool/ -build/ diff --git a/packages/window_manager_macos/CHANGELOG.md b/packages/window_manager_macos/CHANGELOG.md deleted file mode 100644 index 7928521c..00000000 --- a/packages/window_manager_macos/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 1.0.1 - -* Initial release. diff --git a/packages/window_manager_macos/LICENSE b/packages/window_manager_macos/LICENSE deleted file mode 100644 index 9b1046a8..00000000 --- a/packages/window_manager_macos/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 LiJianying - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/window_manager_macos/README.md b/packages/window_manager_macos/README.md deleted file mode 100644 index d4e2f8b5..00000000 --- a/packages/window_manager_macos/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# window_manager_macos - -[![pub version][pub-image]][pub-url] - -[pub-image]: https://img.shields.io/pub/v/window_manager_macos.svg -[pub-url]: https://pub.dev/packages/window_manager_macos - -The Windows implementation of [window_manager](https://pub.dev/packages/window_manager). - -## License - -[MIT](./LICENSE) diff --git a/packages/window_manager_macos/analysis_options.yaml b/packages/window_manager_macos/analysis_options.yaml deleted file mode 100644 index 9033bb29..00000000 --- a/packages/window_manager_macos/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: package:mostly_reasonable_lints/analysis_options.yaml diff --git a/packages/window_manager_macos/pubspec.yaml b/packages/window_manager_macos/pubspec.yaml deleted file mode 100644 index a50b723a..00000000 --- a/packages/window_manager_macos/pubspec.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: window_manager_macos -description: macOS implementation of the window_manager plugin. -version: 1.0.1 -repository: https://github.com/leanflutter/window_manager/tree/main/packages/window_manager_macos - -environment: - sdk: ">=3.0.0 <4.0.0" - flutter: ">=3.3.0" - -dependencies: - flutter: - sdk: flutter - plugin_platform_interface: ^2.0.2 - -dev_dependencies: - flutter_test: - sdk: flutter - mostly_reasonable_lints: ^0.1.2 - -flutter: - plugin: - platforms: - macos: - pluginClass: WindowManagerMacosPlugin diff --git a/packages/window_manager_platform_interface/.gitignore b/packages/window_manager_platform_interface/.gitignore deleted file mode 100644 index ac5aa989..00000000 --- a/packages/window_manager_platform_interface/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. -/pubspec.lock -**/doc/api/ -.dart_tool/ -build/ diff --git a/packages/window_manager_platform_interface/.metadata b/packages/window_manager_platform_interface/.metadata deleted file mode 100644 index b2763f65..00000000 --- a/packages/window_manager_platform_interface/.metadata +++ /dev/null @@ -1,30 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" - channel: "stable" - -project_type: plugin - -# Tracks metadata for the flutter migrate command -migration: - platforms: - - platform: root - create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 - base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 - - platform: macos - create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 - base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 - - # 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/packages/window_manager_platform_interface/CHANGELOG.md b/packages/window_manager_platform_interface/CHANGELOG.md deleted file mode 100644 index 7928521c..00000000 --- a/packages/window_manager_platform_interface/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 1.0.1 - -* Initial release. diff --git a/packages/window_manager_platform_interface/LICENSE b/packages/window_manager_platform_interface/LICENSE deleted file mode 100644 index 2fdca2eb..00000000 --- a/packages/window_manager_platform_interface/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022-present LiJianying - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/window_manager_platform_interface/README.md b/packages/window_manager_platform_interface/README.md deleted file mode 100644 index 4ac89a87..00000000 --- a/packages/window_manager_platform_interface/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# window_manager_platform_interface - -[![pub version][pub-image]][pub-url] - -[pub-image]: https://img.shields.io/pub/v/window_manager_platform_interface.svg -[pub-url]: https://pub.dev/packages/window_manager_platform_interface - -A common platform interface for the [window_manager](https://pub.dev/packages/window_manager) plugin. - -## Usage - -To implement a new platform-specific implementation of window_manager, extend `WindowManagerPlatform` with an implementation that performs the platform-specific behavior, and when you register your plugin, set the default `WindowManagerPlatform` by calling `WindowManagerPlatform.instance = MyPlatformWindowManager()`. - -## License - -[MIT](./LICENSE) diff --git a/packages/window_manager_platform_interface/analysis_options.yaml b/packages/window_manager_platform_interface/analysis_options.yaml deleted file mode 100644 index 9033bb29..00000000 --- a/packages/window_manager_platform_interface/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: package:mostly_reasonable_lints/analysis_options.yaml diff --git a/packages/window_manager_platform_interface/lib/src/window_manager_method_channel.dart b/packages/window_manager_platform_interface/lib/src/window_manager_method_channel.dart deleted file mode 100644 index e02d42a0..00000000 --- a/packages/window_manager_platform_interface/lib/src/window_manager_method_channel.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; -import 'package:window_manager_platform_interface/src/window_manager_platform_interface.dart'; - -/// An implementation of [WindowManagerPlatform] that uses method channels. -class MethodChannelWindowManager extends WindowManagerPlatform { - /// The method channel used to interact with the native platform. - @visibleForTesting - final methodChannel = const MethodChannel('window_manager'); - - @override - Future getPlatformVersion() async { - final version = - await methodChannel.invokeMethod('getPlatformVersion'); - return version; - } -} diff --git a/packages/window_manager_platform_interface/lib/src/window_manager_platform_interface.dart b/packages/window_manager_platform_interface/lib/src/window_manager_platform_interface.dart deleted file mode 100644 index 9715a034..00000000 --- a/packages/window_manager_platform_interface/lib/src/window_manager_platform_interface.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:window_manager_platform_interface/src/window_manager_method_channel.dart'; - -abstract class WindowManagerPlatform extends PlatformInterface { - /// Constructs a WindowManagerPlatform. - WindowManagerPlatform() : super(token: _token); - - static final Object _token = Object(); - - static WindowManagerPlatform _instance = MethodChannelWindowManager(); - - /// The default instance of [WindowManagerPlatform] to use. - /// - /// Defaults to [MethodChannelWindowManager]. - static WindowManagerPlatform get instance => _instance; - - /// Platform-specific implementations should set this with their own - /// platform-specific class that extends [WindowManagerPlatform] when - /// they register themselves. - static set instance(WindowManagerPlatform instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - Future getPlatformVersion() { - throw UnimplementedError('platformVersion() has not been implemented.'); - } -} diff --git a/packages/window_manager_platform_interface/lib/window_manager_platform_interface.dart b/packages/window_manager_platform_interface/lib/window_manager_platform_interface.dart deleted file mode 100644 index 57bbf8cc..00000000 --- a/packages/window_manager_platform_interface/lib/window_manager_platform_interface.dart +++ /dev/null @@ -1,4 +0,0 @@ -library window_manager_platform_interface; - -export 'src/window_manager_method_channel.dart'; -export 'src/window_manager_platform_interface.dart'; diff --git a/packages/window_manager_platform_interface/pubspec.yaml b/packages/window_manager_platform_interface/pubspec.yaml deleted file mode 100644 index 17529dd1..00000000 --- a/packages/window_manager_platform_interface/pubspec.yaml +++ /dev/null @@ -1,18 +0,0 @@ -name: window_manager_platform_interface -description: A common platform interface for the window_manager plugin. -version: 1.0.1 -homepage: https://github.com/leanflutter/window_manager/blob/main/packages/window_manager_platform_interface - -environment: - sdk: ">=3.0.0 <4.0.0" - flutter: ">=3.3.0" - -dependencies: - flutter: - sdk: flutter - plugin_platform_interface: ^2.0.2 - -dev_dependencies: - flutter_test: - sdk: flutter - mostly_reasonable_lints: ^0.1.2 diff --git a/packages/window_manager_platform_interface/test/src/window_manager_method_channel_test.dart b/packages/window_manager_platform_interface/test/src/window_manager_method_channel_test.dart deleted file mode 100644 index b9b023f7..00000000 --- a/packages/window_manager_platform_interface/test/src/window_manager_method_channel_test.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:window_manager_platform_interface/src/window_manager_method_channel.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - MethodChannelWindowManager platform = MethodChannelWindowManager(); - const MethodChannel channel = MethodChannel('window_manager'); - - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler( - channel, - (MethodCall methodCall) async { - return '42'; - }, - ); - }); - - tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(channel, null); - }); - - test('getPlatformVersion', () async { - expect(await platform.getPlatformVersion(), '42'); - }); -} diff --git a/packages/window_manager_windows/.gitignore b/packages/window_manager_windows/.gitignore deleted file mode 100644 index ac5aa989..00000000 --- a/packages/window_manager_windows/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ -migrate_working_dir/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. -/pubspec.lock -**/doc/api/ -.dart_tool/ -build/ diff --git a/packages/window_manager_windows/CHANGELOG.md b/packages/window_manager_windows/CHANGELOG.md deleted file mode 100644 index 7928521c..00000000 --- a/packages/window_manager_windows/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 1.0.1 - -* Initial release. diff --git a/packages/window_manager_windows/LICENSE b/packages/window_manager_windows/LICENSE deleted file mode 100644 index 9b1046a8..00000000 --- a/packages/window_manager_windows/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 LiJianying - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/window_manager_windows/README.md b/packages/window_manager_windows/README.md deleted file mode 100644 index b2c9ce25..00000000 --- a/packages/window_manager_windows/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# window_manager_windows - -[![pub version][pub-image]][pub-url] - -[pub-image]: https://img.shields.io/pub/v/window_manager_windows.svg -[pub-url]: https://pub.dev/packages/window_manager_windows - -The Windows implementation of [window_manager](https://pub.dev/packages/window_manager). - -## License - -[MIT](./LICENSE) diff --git a/packages/window_manager_windows/analysis_options.yaml b/packages/window_manager_windows/analysis_options.yaml deleted file mode 100644 index 9033bb29..00000000 --- a/packages/window_manager_windows/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: package:mostly_reasonable_lints/analysis_options.yaml diff --git a/packages/window_manager_windows/pubspec.yaml b/packages/window_manager_windows/pubspec.yaml deleted file mode 100644 index 6278e9f2..00000000 --- a/packages/window_manager_windows/pubspec.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: window_manager_windows -description: Windows implementation of the window_manager plugin. -version: 1.0.1 -repository: https://github.com/leanflutter/window_manager/tree/main/packages/window_manager_windows - -environment: - sdk: ">=3.0.0 <4.0.0" - flutter: ">=3.3.0" - -dependencies: - flutter: - sdk: flutter - plugin_platform_interface: ^2.0.2 - -dev_dependencies: - flutter_test: - sdk: flutter - mostly_reasonable_lints: ^0.1.2 - -flutter: - plugin: - platforms: - windows: - pluginClass: WindowManagerWindowsPlugin From 56d7218d27b70b1be31853181823ea574116aa05 Mon Sep 17 00:00:00 2001 From: LiJianying Date: Sun, 23 Mar 2025 11:39:49 +0800 Subject: [PATCH 38/50] Update README files to promote Fastforge for easier app distribution --- README-ZH.md | 2 ++ README.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README-ZH.md b/README-ZH.md index a2dd03bb..64b9143e 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -1,3 +1,5 @@ +> **🚀 快速发布您的应用**: 试试 [Fastforge](https://fastforge.dev) - 构建、打包和分发您的 Flutter 应用最简单的方式。 + # window_manager [![pub version][pub-image]][pub-url] [![][discord-image]][discord-url] [![All Contributors][all-contributors-image]](#contributors) diff --git a/README.md b/README.md index 54106dc8..baf4d625 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +> **🚀 Ship Your App Faster**: Try [Fastforge](https://fastforge.dev) - The simplest way to build, package and distribute your Flutter apps. + # window_manager [![pub version][pub-image]][pub-url] [![][discord-image]][discord-url] ![][visits-count-image] [![All Contributors][all-contributors-image]](#contributors) From bb56ad9c51ae1be6a6d6f099905e6f474e859860 Mon Sep 17 00:00:00 2001 From: LiJianying Date: Sun, 23 Mar 2025 14:21:49 +0800 Subject: [PATCH 39/50] Update README files and podspec to reflect new domain for links --- README-ZH.md | 4 ++-- README.md | 4 ++-- packages/window_manager/macos/window_manager.podspec | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README-ZH.md b/README-ZH.md index 64b9143e..85c5f408 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -505,8 +505,8 @@ class _HomePageState extends State with WindowListener { ## 文章 -- [关闭窗口后点击Dock图标进行恢复](https://leanflutter.org/zh/blog/click-dock-icon-to-restore-after-closing-the-window) -- [让应用成为单实例](https://leanflutter.org/zh/blog/making-the-app-single-instanced) +- [关闭窗口后点击Dock图标进行恢复](https://leanflutter.dev/zh/blog/click-dock-icon-to-restore-after-closing-the-window) +- [让应用成为单实例](https://leanflutter.dev/zh/blog/making-the-app-single-instanced) ## 谁在用使用它? diff --git a/README.md b/README.md index baf4d625..48fb0464 100644 --- a/README.md +++ b/README.md @@ -506,8 +506,8 @@ class _HomePageState extends State with WindowListener { ## Articles -- [Click the dock icon to restore after closing the window](https://leanflutter.dev/tips-and-tricks/002-click-dock-icon-to-restore-after-closing-the-window/) -- [Making the app single-instanced](https://leanflutter.dev/tips-and-tricks/001-making-the-app-single-instanced/) +- [Click the dock icon to restore after closing the window](https://leanflutter.dev/blog/click-dock-icon-to-restore-after-closing-the-window/) +- [Making the app single-instanced](https://leanflutter.dev/blog/making-the-app-single-instanced/) ## Who's using it? diff --git a/packages/window_manager/macos/window_manager.podspec b/packages/window_manager/macos/window_manager.podspec index fdaa7def..6e4ae058 100644 --- a/packages/window_manager/macos/window_manager.podspec +++ b/packages/window_manager/macos/window_manager.podspec @@ -9,7 +9,7 @@ Pod::Spec.new do |s| s.description = <<-DESC A new flutter plugin project. DESC - s.homepage = 'https://leanflutter.org' + s.homepage = 'https://leanflutter.dev' s.license = { :file => '../LICENSE' } s.author = { 'LiJianying' => 'lijy91@foxmail.com' } s.source = { :path => '.' } From 27999e2bd7752075bfd1b02d418058369359023c Mon Sep 17 00:00:00 2001 From: LiJianying Date: Sun, 23 Mar 2025 17:51:45 +0800 Subject: [PATCH 40/50] Update README files to include new features and improvements --- README-ZH.md | 835 +--------------------------------------- README.md | 837 +---------------------------------------- docs/en/index.md | 34 ++ docs/en/quick-start.md | 376 ++++++++++++++++++ docs/zh/index.md | 34 ++ docs/zh/quick-start.md | 376 ++++++++++++++++++ 6 files changed, 842 insertions(+), 1650 deletions(-) create mode 100644 docs/en/index.md create mode 100644 docs/en/quick-start.md create mode 100644 docs/zh/index.md create mode 100644 docs/zh/quick-start.md diff --git a/README-ZH.md b/README-ZH.md index 85c5f408..dcdfa554 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -12,7 +12,7 @@ [all-contributors-image]: https://img.shields.io/github/all-contributors/leanflutter/window_manager?color=ee8449&style=flat-square -这个插件允许 Flutter 桌面应用调整窗口的大小和位置。 +这个插件为 Flutter 桌面应用程序提供了全面的窗口管理功能,使开发者能够完全控制窗口大小、位置、外观、关闭行为,以及监听事件。 --- @@ -24,110 +24,12 @@ - [平台支持](#%E5%B9%B3%E5%8F%B0%E6%94%AF%E6%8C%81) +- [文档](#%E6%96%87%E6%A1%A3) - [快速开始](#%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B) - [安装](#%E5%AE%89%E8%A3%85) - [用法](#%E7%94%A8%E6%B3%95) - - [监听事件](#%E7%9B%91%E5%90%AC%E4%BA%8B%E4%BB%B6) - - [关闭时退出](#%E5%85%B3%E9%97%AD%E6%97%B6%E9%80%80%E5%87%BA) - - [macOS](#macos) - - [关闭前确认](#%E5%85%B3%E9%97%AD%E5%89%8D%E7%A1%AE%E8%AE%A4) - - [在启动时隐藏](#%E5%9C%A8%E5%90%AF%E5%8A%A8%E6%97%B6%E9%9A%90%E8%97%8F) - - [Linux](#linux) - - [macOS](#macos-1) - - [Windows](#windows) -- [文章](#%E6%96%87%E7%AB%A0) +- [相关文章](#%E7%9B%B8%E5%85%B3%E6%96%87%E7%AB%A0) - [谁在用使用它?](#%E8%B0%81%E5%9C%A8%E7%94%A8%E4%BD%BF%E7%94%A8%E5%AE%83) -- [API](#api) - - [WindowManager](#windowmanager) - - [Methods](#methods) - - [waitUntilReadyToShow](#waituntilreadytoshow) - - [destroy](#destroy) - - [close](#close) - - [isPreventClose](#ispreventclose) - - [setPreventClose](#setpreventclose) - - [focus](#focus) - - [blur `macos` `windows`](#blur--macos--windows) - - [isFocused `macos` `windows`](#isfocused--macos--windows) - - [show](#show) - - [hide](#hide) - - [isVisible](#isvisible) - - [isMaximized](#ismaximized) - - [maximize](#maximize) - - [unmaximize](#unmaximize) - - [isMinimized](#isminimized) - - [minimize](#minimize) - - [restore](#restore) - - [isFullScreen](#isfullscreen) - - [setFullScreen](#setfullscreen) - - [isDockable `windows`](#isdockable--windows) - - [isDocked `windows`](#isdocked--windows) - - [dock `windows`](#dock--windows) - - [undock `windows`](#undock--windows) - - [setAspectRatio](#setaspectratio) - - [setBackgroundColor](#setbackgroundcolor) - - [setAlignment](#setalignment) - - [center](#center) - - [getBounds](#getbounds) - - [setBounds](#setbounds) - - [getSize](#getsize) - - [setSize](#setsize) - - [getPosition](#getposition) - - [setPosition](#setposition) - - [setMinimumSize](#setminimumsize) - - [setMaximumSize](#setmaximumsize) - - [isResizable](#isresizable) - - [setResizable](#setresizable) - - [isMovable `macos`](#ismovable--macos) - - [setMovable `macos`](#setmovable--macos) - - [isMinimizable `macos` `windows`](#isminimizable--macos--windows) - - [setMinimizable `macos` `windows`](#setminimizable--macos--windows) - - [isClosable `windows`](#isclosable--windows) - - [isMaximizable `macos` `windows`](#ismaximizable--macos--windows) - - [setMaximizable](#setmaximizable) - - [setClosable `macos` `windows`](#setclosable--macos--windows) - - [isAlwaysOnTop](#isalwaysontop) - - [setAlwaysOnTop](#setalwaysontop) - - [isAlwaysOnBottom](#isalwaysonbottom) - - [setAlwaysOnBottom `linux` `windows`](#setalwaysonbottom--linux--windows) - - [getTitle](#gettitle) - - [setTitle](#settitle) - - [setTitleBarStyle](#settitlebarstyle) - - [getTitleBarHeight](#gettitlebarheight) - - [isSkipTaskbar](#isskiptaskbar) - - [setSkipTaskbar](#setskiptaskbar) - - [setProgressBar `macos` `windows`](#setprogressbar--macos--windows) - - [setIcon `windows`](#seticon--windows) - - [isVisibleOnAllWorkspaces `macos`](#isvisibleonallworkspaces--macos) - - [setVisibleOnAllWorkspaces `macos`](#setvisibleonallworkspaces--macos) - - [setBadgeLabel `macos`](#setbadgelabel--macos) - - [hasShadow `macos` `windows`](#hasshadow--macos--windows) - - [setHasShadow `macos` `windows`](#sethasshadow--macos--windows) - - [getOpacity](#getopacity) - - [setOpacity](#setopacity) - - [setBrightness](#setbrightness) - - [setIgnoreMouseEvents](#setignoremouseevents) - - [startDragging](#startdragging) - - [startResizing `linux` `windows`](#startresizing--linux--windows) - - [grabKeyboard `linux`](#grabkeyboard--linux) - - [ungrabKeyboard `linux`](#ungrabkeyboard--linux) - - [WindowListener](#windowlistener) - - [Methods](#methods-1) - - [onWindowClose](#onwindowclose) - - [onWindowFocus](#onwindowfocus) - - [onWindowBlur](#onwindowblur) - - [onWindowMaximize](#onwindowmaximize) - - [onWindowUnmaximize](#onwindowunmaximize) - - [onWindowMinimize](#onwindowminimize) - - [onWindowRestore](#onwindowrestore) - - [onWindowResize](#onwindowresize) - - [onWindowResized `macos` `windows`](#onwindowresized--macos--windows) - - [onWindowMove](#onwindowmove) - - [onWindowMoved `macos` `windows`](#onwindowmoved--macos--windows) - - [onWindowEnterFullScreen](#onwindowenterfullscreen) - - [onWindowLeaveFullScreen](#onwindowleavefullscreen) - - [onWindowDocked `windows`](#onwindowdocked--windows) - - [onWindowUndocked `windows`](#onwindowundocked--windows) - - [onWindowEvent](#onwindowevent) - [贡献者](#%E8%B4%A1%E7%8C%AE%E8%80%85) - [许可证](#%E8%AE%B8%E5%8F%AF%E8%AF%81) @@ -139,6 +41,12 @@ | :---: | :---: | :-----: | | ✔️ | ✔️ | ✔️ | +## 文档 + +- [快速开始](https://leanflutter.dev/zh/documentation/window_manager/quick-start) +- [API 参考](https://pub.dev/documentation/window_manager/latest/window_manager/) +- [更新日志](https://pub.dev/packages/window_manager/changelog) + ## 快速开始 ### 安装 @@ -150,16 +58,6 @@ dependencies: window_manager: ^0.4.2 ``` -或 - -```yaml -dependencies: - window_manager: - git: - url: https://github.com/leanflutter/window_manager.git - ref: main -``` - ### 用法 ```dart @@ -190,320 +88,7 @@ void main() async { > 请看这个插件的示例应用,以了解完整的例子。 -#### 监听事件 - -```dart -import 'package:flutter/cupertino.dart'; -import 'package:window_manager/window_manager.dart'; - -class HomePage extends StatefulWidget { - @override - _HomePageState createState() => _HomePageState(); -} - -class _HomePageState extends State with WindowListener { - @override - void initState() { - super.initState(); - windowManager.addListener(this); - } - - @override - void dispose() { - windowManager.removeListener(this); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - // ... - } - - @override - void onWindowEvent(String eventName) { - print('[WindowManager] onWindowEvent: $eventName'); - } - - @override - void onWindowClose() { - // do something - } - - @override - void onWindowFocus() { - // do something - } - - @override - void onWindowBlur() { - // do something - } - - @override - void onWindowMaximize() { - // do something - } - - @override - void onWindowUnmaximize() { - // do something - } - - @override - void onWindowMinimize() { - // do something - } - - @override - void onWindowRestore() { - // do something - } - - @override - void onWindowResize() { - // do something - } - - @override - void onWindowMove() { - // do something - } - - @override - void onWindowEnterFullScreen() { - // do something - } - - @override - void onWindowLeaveFullScreen() { - // do something - } -} -``` - -#### 关闭时退出 - -如果你需要使用 `hide` 方法,你需要禁用 `QuitOnClose`。 - -##### macOS - -更改文件 `macos/Runner/AppDelegate.swift` 如下: - -```diff -import Cocoa -import FlutterMacOS - -@NSApplicationMain -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { -- return true -+ return false - } -} -``` - -#### 关闭前确认 - -```dart -import 'package:flutter/cupertino.dart'; -import 'package:window_manager/window_manager.dart'; - -class HomePage extends StatefulWidget { - @override - _HomePageState createState() => _HomePageState(); -} - -class _HomePageState extends State with WindowListener { - @override - void initState() { - super.initState(); - windowManager.addListener(this); - _init(); - } - - @override - void dispose() { - windowManager.removeListener(this); - super.dispose(); - } - - void _init() async { - // 添加此行以覆盖默认关闭处理程序 - await windowManager.setPreventClose(true); - setState(() {}); - } - - @override - Widget build(BuildContext context) { - // ... - } - - @override - void onWindowClose() async { - bool _isPreventClose = await windowManager.isPreventClose(); - if (_isPreventClose) { - showDialog( - context: context, - builder: (_) { - return AlertDialog( - title: Text('Are you sure you want to close this window?'), - actions: [ - TextButton( - child: Text('No'), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - TextButton( - child: Text('Yes'), - onPressed: () { - Navigator.of(context).pop(); - await windowManager.destroy(); - }, - ), - ], - ); - }, - ); - } - } -} -``` - -#### 在启动时隐藏 - -##### Linux - -更改文件 `linux/my_application.cc` 如下: - -```diff - -... - -// Implements GApplication::activate. -static void my_application_activate(GApplication* application) { - - ... - - gtk_window_set_default_size(window, 1280, 720); -- gtk_widget_show(GTK_WIDGET(window)); -+ gtk_widget_realize(GTK_WIDGET(window)); - - 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); - gtk_widget_show(GTK_WIDGET(view)); - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); - - fl_register_plugins(FL_PLUGIN_REGISTRY(view)); - - gtk_widget_grab_focus(GTK_WIDGET(view)); -} - -... - -``` - -##### macOS - -更改文件 `macos/Runner/MainFlutterWindow.swift` 如下: - -```diff -import Cocoa -import FlutterMacOS -+import window_manager - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController.init() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } - -+ override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) { -+ super.order(place, relativeTo: otherWin) -+ hiddenWindowAtLaunch() -+ } -} - -``` - -##### Windows - -更改文件 `windows/runner/win32_window.cpp` 如下: - -```diff -bool Win32Window::CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size) { - ... - HWND window = CreateWindow( -- window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, -+ window_class, title.c_str(), -+ WS_OVERLAPPEDWINDOW, // do not add WS_VISIBLE since the window will be shown later - Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), - Scale(size.width, scale_factor), Scale(size.height, scale_factor), - nullptr, nullptr, GetModuleHandle(nullptr), this); -``` - -使用 flutter 3.7 创建的 Windows 项目 -更改 `windows/runner/flutter_window.cpp` 如下: - -```diff -bool FlutterWindow::OnCreate() { - ... - flutter_controller_->engine()->SetNextFrameCallback([&]() { -- this->Show(); -+ "" //删除 this->Show() - }); -``` - -确保在 `onWindowFocus` 事件中调用一次 `setState`。 - -```dart -import 'package:flutter/cupertino.dart'; -import 'package:window_manager/window_manager.dart'; - -class HomePage extends StatefulWidget { - @override - _HomePageState createState() => _HomePageState(); -} - -class _HomePageState extends State with WindowListener { - @override - void initState() { - super.initState(); - windowManager.addListener(this); - } - - @override - void dispose() { - windowManager.removeListener(this); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - // ... - } - - @override - void onWindowFocus() { - // Make sure to call once. - setState(() {}); - // do something - } -} - -``` - -## 文章 +## 相关文章 - [关闭窗口后点击Dock图标进行恢复](https://leanflutter.dev/zh/blog/click-dock-icon-to-restore-after-closing-the-window) - [让应用成为单实例](https://leanflutter.dev/zh/blog/making-the-app-single-instanced) @@ -519,406 +104,8 @@ class _HomePageState extends State with WindowListener { - [RustDesk](https://github.com/rustdesk/rustdesk) - 远程桌面软件,开箱即用,无需任何配置。您完全掌控数据,不用担心安全问题。 - [Ubuntu Desktop Installer](https://github.com/canonical/ubuntu-desktop-installer) - This project is a modern implementation of the Ubuntu Desktop installer. - [UniControlHub](https://github.com/rohitsangwan01/uni_control_hub) - Seamlessly bridge your Desktop and Mobile devices +- [EyesCare](https://bixat.dev/products/EyesCare) - A light-weight application following 20 rule adherence for optimum eye health -## API - - -### WindowManager - -#### Methods - -##### waitUntilReadyToShow - -Wait until ready to show. - -##### destroy - -Force closing the window. - -##### close - -Try to close the window. - -##### isPreventClose - -Check if is intercepting the native close signal. - -##### setPreventClose - -Set if intercept the native close signal. May useful when combine with the onclose event listener. -This will also prevent the manually triggered close event. - -##### focus - -Focuses on the window. - -##### blur `macos` `windows` - -Removes focus from the window. - - -##### isFocused `macos` `windows` - -Returns `bool` - Whether window is focused. - - -##### show - -Shows and gives focus to the window. - -##### hide - -Hides the window. - -##### isVisible - -Returns `bool` - Whether the window is visible to the user. - -##### isMaximized - -Returns `bool` - Whether the window is maximized. - -##### maximize - -Maximizes the window. `vertically` simulates aero snap, only works on Windows - -##### unmaximize - -Unmaximizes the window. - -##### isMinimized - -Returns `bool` - Whether the window is minimized. - -##### minimize - -Minimizes the window. On some platforms the minimized window will be shown in the Dock. - -##### restore - -Restores the window from minimized state to its previous state. - -##### isFullScreen - -Returns `bool` - Whether the window is in fullscreen mode. - -##### setFullScreen - -Sets whether the window should be in fullscreen mode. - -##### isDockable `windows` - -Returns `bool` - Whether the window is dockable or not. - - -##### isDocked `windows` - -Returns `bool` - Whether the window is docked. - - -##### dock `windows` - -Docks the window. only works on Windows - - -##### undock `windows` - -Undocks the window. only works on Windows - - -##### setAspectRatio - -This will make a window maintain an aspect ratio. - -##### setBackgroundColor - -Sets the background color of the window. - -##### setAlignment - -Move the window to a position aligned with the screen. - -##### center - -Moves window to the center of the screen. - -##### getBounds - -Returns `Rect` - The bounds of the window as Object. - -##### setBounds - -Resizes and moves the window to the supplied bounds. - -##### getSize - -Returns `Size` - Contains the window's width and height. - -##### setSize - -Resizes the window to `width` and `height`. - -##### getPosition - -Returns `Offset` - Contains the window's current position. - -##### setPosition - -Moves window to position. - -##### setMinimumSize - -Sets the minimum size of window to `width` and `height`. - -##### setMaximumSize - -Sets the maximum size of window to `width` and `height`. - -##### isResizable - -Returns `bool` - Whether the window can be manually resized by the user. - -##### setResizable - -Sets whether the window can be manually resized by the user. - -##### isMovable `macos` - -Returns `bool` - Whether the window can be moved by user. - - -##### setMovable `macos` - -Sets whether the window can be moved by user. - - -##### isMinimizable `macos` `windows` - -Returns `bool` - Whether the window can be manually minimized by the user. - - -##### setMinimizable `macos` `windows` - -Sets whether the window can be manually minimized by user. - - -##### isClosable `windows` - -Returns `bool` - Whether the window can be manually closed by user. - - -##### isMaximizable `macos` `windows` - -Returns `bool` - Whether the window can be manually maximized by the user. - - -##### setMaximizable - -Sets whether the window can be manually maximized by the user. - -##### setClosable `macos` `windows` - -Sets whether the window can be manually closed by user. - - -##### isAlwaysOnTop - -Returns `bool` - Whether the window is always on top of other windows. - -##### setAlwaysOnTop - -Sets whether the window should show always on top of other windows. - -##### isAlwaysOnBottom - -Returns `bool` - Whether the window is always below other windows. - -##### setAlwaysOnBottom `linux` `windows` - -Sets whether the window should show always below other windows. - - -##### getTitle - -Returns `String` - The title of the native window. - -##### setTitle - -Changes the title of native window to title. - -##### setTitleBarStyle - -Changes the title bar style of native window. - -##### getTitleBarHeight - -Returns `int` - The title bar height of the native window. - -##### isSkipTaskbar - -Returns `bool` - Whether skipping taskbar is enabled. - -##### setSkipTaskbar - -Makes the window not show in the taskbar / dock. - -##### setProgressBar `macos` `windows` - -Sets progress value in progress bar. Valid range is [0, 1.0]. - - -##### setIcon `windows` - -Sets window/taskbar icon. - - -##### isVisibleOnAllWorkspaces `macos` - -Returns `bool` - Whether the window is visible on all workspaces. - - -##### setVisibleOnAllWorkspaces `macos` - -Sets whether the window should be visible on all workspaces. - -Note: If you need to support dragging a window on top of a fullscreen -window on another screen, you need to modify MainFlutterWindow -to inherit from NSPanel - -```swift -class MainFlutterWindow: NSPanel { -// ... -} -``` - - -##### setBadgeLabel `macos` - -Set/unset label on taskbar(dock) app icon - -Note that it's required to request access at your AppDelegate.swift like this: -UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge]) - - -##### hasShadow `macos` `windows` - -Returns `bool` - Whether the window has a shadow. On Windows, always returns true unless window is frameless. - - -##### setHasShadow `macos` `windows` - -Sets whether the window should have a shadow. On Windows, doesn't do anything unless window is frameless. - - -##### getOpacity - -Returns `double` - between 0.0 (fully transparent) and 1.0 (fully opaque). - -##### setOpacity - -Sets the opacity of the window. - -##### setBrightness - -Sets the brightness of the window. - -##### setIgnoreMouseEvents - -Makes the window ignore all mouse events. - -All mouse events happened in this window will be passed to the window below this window, but if this window has focus, it will still receive keyboard events. - -##### startDragging - -Starts a window drag based on the specified mouse-down event. - -##### startResizing `linux` `windows` - -Starts a window resize based on the specified mouse-down & mouse-move event. - - -##### grabKeyboard `linux` - -Grabs the keyboard. - -##### ungrabKeyboard `linux` - -Ungrabs the keyboard. - -### WindowListener - -#### Methods - -##### onWindowClose - -Emitted when the window is going to be closed. - -##### onWindowFocus - -Emitted when the window gains focus. - -##### onWindowBlur - -Emitted when the window loses focus. - -##### onWindowMaximize - -Emitted when window is maximized. - -##### onWindowUnmaximize - -Emitted when the window exits from a maximized state. - -##### onWindowMinimize - -Emitted when the window is minimized. - -##### onWindowRestore - -Emitted when the window is restored from a minimized state. - -##### onWindowResize - -Emitted after the window has been resized. - -##### onWindowResized `macos` `windows` - -Emitted once when the window has finished being resized. - - -##### onWindowMove - -Emitted when the window is being moved to a new position. - -##### onWindowMoved `macos` `windows` - -Emitted once when the window is moved to a new position. - - -##### onWindowEnterFullScreen - -Emitted when the window enters a full-screen state. - -##### onWindowLeaveFullScreen - -Emitted when the window leaves a full-screen state. - -##### onWindowDocked `windows` - -Emitted when the window entered a docked state. - - -##### onWindowUndocked `windows` - -Emitted when the window leaves a docked state. - - -##### onWindowEvent - -Emitted all events. - - - ## 贡献者 diff --git a/README.md b/README.md index 48fb0464..ba9cb594 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # window_manager -[![pub version][pub-image]][pub-url] [![][discord-image]][discord-url] ![][visits-count-image] [![All Contributors][all-contributors-image]](#contributors) +[![pub version][pub-image]][pub-url] [![][discord-image]][discord-url] [![All Contributors][all-contributors-image]](#contributors) [pub-image]: https://img.shields.io/pub/v/window_manager.svg [pub-url]: https://pub.dev/packages/window_manager @@ -10,10 +10,9 @@ [discord-image]: https://img.shields.io/discord/884679008049037342.svg [discord-url]: https://discord.gg/zPa6EZ2jqb -[visits-count-image]: https://img.shields.io/badge/dynamic/json?label=Visits%20Count&query=value&url=https://api.countapi.xyz/hit/leanflutter.window_manager/visits [all-contributors-image]: https://img.shields.io/github/all-contributors/leanflutter/window_manager?color=ee8449&style=flat-square -This plugin allows Flutter desktop apps to resizing and repositioning the window. +This plugin provides comprehensive window management capabilities for Flutter desktop applications, enabling full control over window size, position, appearance, close behavior, and listening to events. --- @@ -25,110 +24,12 @@ English | [简体中文](./README-ZH.md) - [Platform Support](#platform-support) +- [Documentation](#documentation) - [Quick Start](#quick-start) - [Installation](#installation) - [Usage](#usage) - - [Listening events](#listening-events) - - [Quit on close](#quit-on-close) - - [macOS](#macos) - - [Confirm before closing](#confirm-before-closing) - - [Hidden at launch](#hidden-at-launch) - - [Linux](#linux) - - [macOS](#macos-1) - - [Windows](#windows) -- [Articles](#articles) +- [Related Articles](#related-articles) - [Who's using it?](#whos-using-it) -- [API](#api) - - [WindowManager](#windowmanager) - - [Methods](#methods) - - [waitUntilReadyToShow](#waituntilreadytoshow) - - [destroy](#destroy) - - [close](#close) - - [isPreventClose](#ispreventclose) - - [setPreventClose](#setpreventclose) - - [focus](#focus) - - [blur `macos` `windows`](#blur--macos--windows) - - [isFocused `macos` `windows`](#isfocused--macos--windows) - - [show](#show) - - [hide](#hide) - - [isVisible](#isvisible) - - [isMaximized](#ismaximized) - - [maximize](#maximize) - - [unmaximize](#unmaximize) - - [isMinimized](#isminimized) - - [minimize](#minimize) - - [restore](#restore) - - [isFullScreen](#isfullscreen) - - [setFullScreen](#setfullscreen) - - [isDockable `windows`](#isdockable--windows) - - [isDocked `windows`](#isdocked--windows) - - [dock `windows`](#dock--windows) - - [undock `windows`](#undock--windows) - - [setAspectRatio](#setaspectratio) - - [setBackgroundColor](#setbackgroundcolor) - - [setAlignment](#setalignment) - - [center](#center) - - [getBounds](#getbounds) - - [setBounds](#setbounds) - - [getSize](#getsize) - - [setSize](#setsize) - - [getPosition](#getposition) - - [setPosition](#setposition) - - [setMinimumSize](#setminimumsize) - - [setMaximumSize](#setmaximumsize) - - [isResizable](#isresizable) - - [setResizable](#setresizable) - - [isMovable `macos`](#ismovable--macos) - - [setMovable `macos`](#setmovable--macos) - - [isMinimizable `macos` `windows`](#isminimizable--macos--windows) - - [setMinimizable `macos` `windows`](#setminimizable--macos--windows) - - [isClosable `windows`](#isclosable--windows) - - [isMaximizable `macos` `windows`](#ismaximizable--macos--windows) - - [setMaximizable](#setmaximizable) - - [setClosable `macos` `windows`](#setclosable--macos--windows) - - [isAlwaysOnTop](#isalwaysontop) - - [setAlwaysOnTop](#setalwaysontop) - - [isAlwaysOnBottom](#isalwaysonbottom) - - [setAlwaysOnBottom `linux` `windows`](#setalwaysonbottom--linux--windows) - - [getTitle](#gettitle) - - [setTitle](#settitle) - - [setTitleBarStyle](#settitlebarstyle) - - [getTitleBarHeight](#gettitlebarheight) - - [isSkipTaskbar](#isskiptaskbar) - - [setSkipTaskbar](#setskiptaskbar) - - [setProgressBar `macos` `windows`](#setprogressbar--macos--windows) - - [setIcon `windows`](#seticon--windows) - - [isVisibleOnAllWorkspaces `macos`](#isvisibleonallworkspaces--macos) - - [setVisibleOnAllWorkspaces `macos`](#setvisibleonallworkspaces--macos) - - [setBadgeLabel `macos`](#setbadgelabel--macos) - - [hasShadow `macos` `windows`](#hasshadow--macos--windows) - - [setHasShadow `macos` `windows`](#sethasshadow--macos--windows) - - [getOpacity](#getopacity) - - [setOpacity](#setopacity) - - [setBrightness](#setbrightness) - - [setIgnoreMouseEvents](#setignoremouseevents) - - [startDragging](#startdragging) - - [startResizing `linux` `windows`](#startresizing--linux--windows) - - [grabKeyboard `linux`](#grabkeyboard--linux) - - [ungrabKeyboard `linux`](#ungrabkeyboard--linux) - - [WindowListener](#windowlistener) - - [Methods](#methods-1) - - [onWindowClose](#onwindowclose) - - [onWindowFocus](#onwindowfocus) - - [onWindowBlur](#onwindowblur) - - [onWindowMaximize](#onwindowmaximize) - - [onWindowUnmaximize](#onwindowunmaximize) - - [onWindowMinimize](#onwindowminimize) - - [onWindowRestore](#onwindowrestore) - - [onWindowResize](#onwindowresize) - - [onWindowResized `macos` `windows`](#onwindowresized--macos--windows) - - [onWindowMove](#onwindowmove) - - [onWindowMoved `macos` `windows`](#onwindowmoved--macos--windows) - - [onWindowEnterFullScreen](#onwindowenterfullscreen) - - [onWindowLeaveFullScreen](#onwindowleavefullscreen) - - [onWindowDocked `windows`](#onwindowdocked--windows) - - [onWindowUndocked `windows`](#onwindowundocked--windows) - - [onWindowEvent](#onwindowevent) - [Contributors](#contributors) - [License](#license) @@ -140,6 +41,12 @@ English | [简体中文](./README-ZH.md) | :---: | :---: | :-----: | | ✔️ | ✔️ | ✔️ | +## Documentation + +- [Quick Start](https://leanflutter.dev/documentation/window_manager/quick-start) +- [API Reference](https://pub.dev/documentation/window_manager/latest/window_manager/) +- [Changelog](https://pub.dev/packages/window_manager/changelog) + ## Quick Start ### Installation @@ -151,16 +58,6 @@ dependencies: window_manager: ^0.4.2 ``` -Or - -```yaml -dependencies: - window_manager: - git: - url: https://github.com/leanflutter/window_manager.git - ref: main -``` - ### Usage ```dart @@ -191,320 +88,7 @@ void main() async { > Please see the example app of this plugin for a full example. -#### Listening events - -```dart -import 'package:flutter/cupertino.dart'; -import 'package:window_manager/window_manager.dart'; - -class HomePage extends StatefulWidget { - @override - _HomePageState createState() => _HomePageState(); -} - -class _HomePageState extends State with WindowListener { - @override - void initState() { - super.initState(); - windowManager.addListener(this); - } - - @override - void dispose() { - windowManager.removeListener(this); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - // ... - } - - @override - void onWindowEvent(String eventName) { - print('[WindowManager] onWindowEvent: $eventName'); - } - - @override - void onWindowClose() { - // do something - } - - @override - void onWindowFocus() { - // do something - } - - @override - void onWindowBlur() { - // do something - } - - @override - void onWindowMaximize() { - // do something - } - - @override - void onWindowUnmaximize() { - // do something - } - - @override - void onWindowMinimize() { - // do something - } - - @override - void onWindowRestore() { - // do something - } - - @override - void onWindowResize() { - // do something - } - - @override - void onWindowMove() { - // do something - } - - @override - void onWindowEnterFullScreen() { - // do something - } - - @override - void onWindowLeaveFullScreen() { - // do something - } -} -``` - -#### Quit on close - -If you need to use the hide method, you need to disable `QuitOnClose`. - -##### macOS - -Change the file `macos/Runner/AppDelegate.swift` as follows: - -```diff -import Cocoa -import FlutterMacOS - -@NSApplicationMain -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { -- return true -+ return false - } -} -``` - -#### Confirm before closing - -```dart -import 'package:flutter/cupertino.dart'; -import 'package:window_manager/window_manager.dart'; - -class HomePage extends StatefulWidget { - @override - _HomePageState createState() => _HomePageState(); -} - -class _HomePageState extends State with WindowListener { - @override - void initState() { - super.initState(); - windowManager.addListener(this); - _init(); - } - - @override - void dispose() { - windowManager.removeListener(this); - super.dispose(); - } - - void _init() async { - // Add this line to override the default close handler - await windowManager.setPreventClose(true); - setState(() {}); - } - - @override - Widget build(BuildContext context) { - // ... - } - - @override - void onWindowClose() async { - bool _isPreventClose = await windowManager.isPreventClose(); - if (_isPreventClose) { - showDialog( - context: context, - builder: (_) { - return AlertDialog( - title: Text('Are you sure you want to close this window?'), - actions: [ - TextButton( - child: Text('No'), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - TextButton( - child: Text('Yes'), - onPressed: () { - Navigator.of(context).pop(); - await windowManager.destroy(); - }, - ), - ], - ); - }, - ); - } - } -} -``` - -#### Hidden at launch - -##### Linux - -Change the file `linux/my_application.cc` as follows: - -```diff - -... - -// Implements GApplication::activate. -static void my_application_activate(GApplication* application) { - - ... - - gtk_window_set_default_size(window, 1280, 720); -- gtk_widget_show(GTK_WIDGET(window)); -+ gtk_widget_realize(GTK_WIDGET(window)); - - 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); - gtk_widget_show(GTK_WIDGET(view)); - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); - - fl_register_plugins(FL_PLUGIN_REGISTRY(view)); - - gtk_widget_grab_focus(GTK_WIDGET(view)); -} - -... - -``` - -##### macOS - -Change the file `macos/Runner/MainFlutterWindow.swift` as follows: - -```diff -import Cocoa -import FlutterMacOS -+import window_manager - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController.init() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } - -+ override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) { -+ super.order(place, relativeTo: otherWin) -+ hiddenWindowAtLaunch() -+ } -} - -``` - -##### Windows - -Change the file `windows/runner/win32_window.cpp` as follows: - -```diff -bool Win32Window::CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size) { - ... - HWND window = CreateWindow( -- window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, -+ window_class, title.c_str(), -+ WS_OVERLAPPEDWINDOW, // do not add WS_VISIBLE since the window will be shown later - Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), - Scale(size.width, scale_factor), Scale(size.height, scale_factor), - nullptr, nullptr, GetModuleHandle(nullptr), this); -``` - -Since flutter 3.7 new windows project -Change the file `windows/runner/flutter_window.cpp` as follows: - -```diff -bool FlutterWindow::OnCreate() { - ... - flutter_controller_->engine()->SetNextFrameCallback([&]() { -- this->Show(); -+ "" //delete this->Show() - }); -``` - -Make sure to call `setState` once on the `onWindowFocus` event. - -```dart -import 'package:flutter/cupertino.dart'; -import 'package:window_manager/window_manager.dart'; - -class HomePage extends StatefulWidget { - @override - _HomePageState createState() => _HomePageState(); -} - -class _HomePageState extends State with WindowListener { - @override - void initState() { - super.initState(); - windowManager.addListener(this); - } - - @override - void dispose() { - windowManager.removeListener(this); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - // ... - } - - @override - void onWindowFocus() { - // Make sure to call once. - setState(() {}); - // do something - } -} - -``` - -## Articles +## Related Articles - [Click the dock icon to restore after closing the window](https://leanflutter.dev/blog/click-dock-icon-to-restore-after-closing-the-window/) - [Making the app single-instanced](https://leanflutter.dev/blog/making-the-app-single-instanced/) @@ -522,405 +106,6 @@ class _HomePageState extends State with WindowListener { - [UniControlHub](https://github.com/rohitsangwan01/uni_control_hub) - Seamlessly bridge your Desktop and Mobile devices - [EyesCare](https://bixat.dev/products/EyesCare) - A light-weight application following 20 rule adherence for optimum eye health -## API - - -### WindowManager - -#### Methods - -##### waitUntilReadyToShow - -Wait until ready to show. - -##### destroy - -Force closing the window. - -##### close - -Try to close the window. - -##### isPreventClose - -Check if is intercepting the native close signal. - -##### setPreventClose - -Set if intercept the native close signal. May useful when combine with the onclose event listener. -This will also prevent the manually triggered close event. - -##### focus - -Focuses on the window. - -##### blur `macos` `windows` - -Removes focus from the window. - - -##### isFocused `macos` `windows` - -Returns `bool` - Whether window is focused. - - -##### show - -Shows and gives focus to the window. - -##### hide - -Hides the window. - -##### isVisible - -Returns `bool` - Whether the window is visible to the user. - -##### isMaximized - -Returns `bool` - Whether the window is maximized. - -##### maximize - -Maximizes the window. `vertically` simulates aero snap, only works on Windows - -##### unmaximize - -Unmaximizes the window. - -##### isMinimized - -Returns `bool` - Whether the window is minimized. - -##### minimize - -Minimizes the window. On some platforms the minimized window will be shown in the Dock. - -##### restore - -Restores the window from minimized state to its previous state. - -##### isFullScreen - -Returns `bool` - Whether the window is in fullscreen mode. - -##### setFullScreen - -Sets whether the window should be in fullscreen mode. - -##### isDockable `windows` - -Returns `bool` - Whether the window is dockable or not. - - -##### isDocked `windows` - -Returns `bool` - Whether the window is docked. - - -##### dock `windows` - -Docks the window. only works on Windows - - -##### undock `windows` - -Undocks the window. only works on Windows - - -##### setAspectRatio - -This will make a window maintain an aspect ratio. - -##### setBackgroundColor - -Sets the background color of the window. - -##### setAlignment - -Move the window to a position aligned with the screen. - -##### center - -Moves window to the center of the screen. - -##### getBounds - -Returns `Rect` - The bounds of the window as Object. - -##### setBounds - -Resizes and moves the window to the supplied bounds. - -##### getSize - -Returns `Size` - Contains the window's width and height. - -##### setSize - -Resizes the window to `width` and `height`. - -##### getPosition - -Returns `Offset` - Contains the window's current position. - -##### setPosition - -Moves window to position. - -##### setMinimumSize - -Sets the minimum size of window to `width` and `height`. - -##### setMaximumSize - -Sets the maximum size of window to `width` and `height`. - -##### isResizable - -Returns `bool` - Whether the window can be manually resized by the user. - -##### setResizable - -Sets whether the window can be manually resized by the user. - -##### isMovable `macos` - -Returns `bool` - Whether the window can be moved by user. - - -##### setMovable `macos` - -Sets whether the window can be moved by user. - - -##### isMinimizable `macos` `windows` - -Returns `bool` - Whether the window can be manually minimized by the user. - - -##### setMinimizable `macos` `windows` - -Sets whether the window can be manually minimized by user. - - -##### isClosable `windows` - -Returns `bool` - Whether the window can be manually closed by user. - - -##### isMaximizable `macos` `windows` - -Returns `bool` - Whether the window can be manually maximized by the user. - - -##### setMaximizable - -Sets whether the window can be manually maximized by the user. - -##### setClosable `macos` `windows` - -Sets whether the window can be manually closed by user. - - -##### isAlwaysOnTop - -Returns `bool` - Whether the window is always on top of other windows. - -##### setAlwaysOnTop - -Sets whether the window should show always on top of other windows. - -##### isAlwaysOnBottom - -Returns `bool` - Whether the window is always below other windows. - -##### setAlwaysOnBottom `linux` `windows` - -Sets whether the window should show always below other windows. - - -##### getTitle - -Returns `String` - The title of the native window. - -##### setTitle - -Changes the title of native window to title. - -##### setTitleBarStyle - -Changes the title bar style of native window. - -##### getTitleBarHeight - -Returns `int` - The title bar height of the native window. - -##### isSkipTaskbar - -Returns `bool` - Whether skipping taskbar is enabled. - -##### setSkipTaskbar - -Makes the window not show in the taskbar / dock. - -##### setProgressBar `macos` `windows` - -Sets progress value in progress bar. Valid range is [0, 1.0]. - - -##### setIcon `windows` - -Sets window/taskbar icon. - - -##### isVisibleOnAllWorkspaces `macos` - -Returns `bool` - Whether the window is visible on all workspaces. - - -##### setVisibleOnAllWorkspaces `macos` - -Sets whether the window should be visible on all workspaces. - -Note: If you need to support dragging a window on top of a fullscreen -window on another screen, you need to modify MainFlutterWindow -to inherit from NSPanel - -```swift -class MainFlutterWindow: NSPanel { -// ... -} -``` - - -##### setBadgeLabel `macos` - -Set/unset label on taskbar(dock) app icon - -Note that it's required to request access at your AppDelegate.swift like this: -UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge]) - - -##### hasShadow `macos` `windows` - -Returns `bool` - Whether the window has a shadow. On Windows, always returns true unless window is frameless. - - -##### setHasShadow `macos` `windows` - -Sets whether the window should have a shadow. On Windows, doesn't do anything unless window is frameless. - - -##### getOpacity - -Returns `double` - between 0.0 (fully transparent) and 1.0 (fully opaque). - -##### setOpacity - -Sets the opacity of the window. - -##### setBrightness - -Sets the brightness of the window. - -##### setIgnoreMouseEvents - -Makes the window ignore all mouse events. - -All mouse events happened in this window will be passed to the window below this window, but if this window has focus, it will still receive keyboard events. - -##### startDragging - -Starts a window drag based on the specified mouse-down event. - -##### startResizing `linux` `windows` - -Starts a window resize based on the specified mouse-down & mouse-move event. - - -##### grabKeyboard `linux` - -Grabs the keyboard. - -##### ungrabKeyboard `linux` - -Ungrabs the keyboard. - -### WindowListener - -#### Methods - -##### onWindowClose - -Emitted when the window is going to be closed. - -##### onWindowFocus - -Emitted when the window gains focus. - -##### onWindowBlur - -Emitted when the window loses focus. - -##### onWindowMaximize - -Emitted when window is maximized. - -##### onWindowUnmaximize - -Emitted when the window exits from a maximized state. - -##### onWindowMinimize - -Emitted when the window is minimized. - -##### onWindowRestore - -Emitted when the window is restored from a minimized state. - -##### onWindowResize - -Emitted after the window has been resized. - -##### onWindowResized `macos` `windows` - -Emitted once when the window has finished being resized. - - -##### onWindowMove - -Emitted when the window is being moved to a new position. - -##### onWindowMoved `macos` `windows` - -Emitted once when the window is moved to a new position. - - -##### onWindowEnterFullScreen - -Emitted when the window enters a full-screen state. - -##### onWindowLeaveFullScreen - -Emitted when the window leaves a full-screen state. - -##### onWindowDocked `windows` - -Emitted when the window entered a docked state. - - -##### onWindowUndocked `windows` - -Emitted when the window leaves a docked state. - - -##### onWindowEvent - -Emitted all events. - - - ## Contributors diff --git a/docs/en/index.md b/docs/en/index.md new file mode 100644 index 00000000..440fe5f8 --- /dev/null +++ b/docs/en/index.md @@ -0,0 +1,34 @@ +# Introduction + +The `window_manager` plugin provides comprehensive window management capabilities for Flutter desktop applications, enabling full control over window size, position, appearance, close behavior, and listening to events. + +## Core Features + +### Window Control + +- Set size/min/max limits +- Position windows across displays +- Manage states: maximize/minimize/fullscreen +- Custom close handlers + +### Visual Customization + +- Hide title bars +- Create borderless windows +- Adjust opacity/background color +- Control window shadows + +### Event Listeners + +- Lifecycle: open/close events +- State: maximize/minimize/fullscreen +- Position: move/resize +- Focus: focus/blur + +## Platform Support + +| Platform | Support | +| -------- | :----------------- | +| Linux | ✔️ Fully supported | +| macOS | ✔️ Fully supported | +| Windows | ✔️ Fully supported | diff --git a/docs/en/quick-start.md b/docs/en/quick-start.md new file mode 100644 index 00000000..24cc6584 --- /dev/null +++ b/docs/en/quick-start.md @@ -0,0 +1,376 @@ +# Quick Start + +Follow the steps below to quickly get started with the `window_manager` plugin: + +## Installation + +Add this to your package's `pubspec.yaml` file: + +```yaml +dependencies: + window_manager: ^0.4.2 +``` + +Or + +```yaml +dependencies: + window_manager: + git: + url: https://github.com/leanflutter/window_manager.git + ref: main +``` + +## Usage + +```dart +import 'package:flutter/material.dart'; +import 'package:window_manager/window_manager.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + // Must add this line. + await windowManager.ensureInitialized(); + + WindowOptions windowOptions = WindowOptions( + size: Size(800, 600), + center: true, + backgroundColor: Colors.transparent, + skipTaskbar: false, + titleBarStyle: TitleBarStyle.hidden, + ); + windowManager.waitUntilReadyToShow(windowOptions, () async { + await windowManager.show(); + await windowManager.focus(); + }); + + runApp(MyApp()); +} + +``` + +> Please see the example app of this plugin for a full example. + +### Listening events + +```dart +import 'package:flutter/cupertino.dart'; +import 'package:window_manager/window_manager.dart'; + +class HomePage extends StatefulWidget { + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State with WindowListener { + @override + void initState() { + super.initState(); + windowManager.addListener(this); + } + + @override + void dispose() { + windowManager.removeListener(this); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + // ... + } + + @override + void onWindowEvent(String eventName) { + print('[WindowManager] onWindowEvent: $eventName'); + } + + @override + void onWindowClose() { + // do something + } + + @override + void onWindowFocus() { + // do something + } + + @override + void onWindowBlur() { + // do something + } + + @override + void onWindowMaximize() { + // do something + } + + @override + void onWindowUnmaximize() { + // do something + } + + @override + void onWindowMinimize() { + // do something + } + + @override + void onWindowRestore() { + // do something + } + + @override + void onWindowResize() { + // do something + } + + @override + void onWindowMove() { + // do something + } + + @override + void onWindowEnterFullScreen() { + // do something + } + + @override + void onWindowLeaveFullScreen() { + // do something + } +} +``` + +### Quit on close + +If you need to use the hide method, you need to disable `QuitOnClose`. + +#### macOS + +Change the file `macos/Runner/AppDelegate.swift` as follows: + +```diff +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { +- return true ++ return false + } +} +``` + +### Confirm before closing + +```dart +import 'package:flutter/cupertino.dart'; +import 'package:window_manager/window_manager.dart'; + +class HomePage extends StatefulWidget { + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State with WindowListener { + @override + void initState() { + super.initState(); + windowManager.addListener(this); + _init(); + } + + @override + void dispose() { + windowManager.removeListener(this); + super.dispose(); + } + + void _init() async { + // Add this line to override the default close handler + await windowManager.setPreventClose(true); + setState(() {}); + } + + @override + Widget build(BuildContext context) { + // ... + } + + @override + void onWindowClose() async { + bool _isPreventClose = await windowManager.isPreventClose(); + if (_isPreventClose) { + showDialog( + context: context, + builder: (_) { + return AlertDialog( + title: Text('Are you sure you want to close this window?'), + actions: [ + TextButton( + child: Text('No'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: Text('Yes'), + onPressed: () { + Navigator.of(context).pop(); + await windowManager.destroy(); + }, + ), + ], + ); + }, + ); + } + } +} +``` + +### Hidden at launch + +When launching a Flutter desktop application, there can be a brief moment where an unstyled window is visible before your custom styling is applied. This can create a jarring visual experience for users. + +To prevent this, we can hide the window initially and only show it once Flutter has fully initialized and applied all styling. This creates a smoother launch experience. + +Here's how to implement this: + +#### Linux + +Change the file `linux/my_application.cc` as follows: + +```diff + +... + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + + ... + + gtk_window_set_default_size(window, 1280, 720); +- gtk_widget_show(GTK_WIDGET(window)); ++ gtk_widget_realize(GTK_WIDGET(window)); + + 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); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +... + +``` + +#### macOS + +Change the file `macos/Runner/MainFlutterWindow.swift` as follows: + +```diff +import Cocoa +import FlutterMacOS ++import window_manager + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } + ++ override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) { ++ super.order(place, relativeTo: otherWin) ++ hiddenWindowAtLaunch() ++ } +} + +``` + +#### Windows + +Change the file `windows/runner/win32_window.cpp` as follows: + +```diff +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + ... + HWND window = CreateWindow( +- window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, ++ window_class, title.c_str(), ++ WS_OVERLAPPEDWINDOW, // do not add WS_VISIBLE since the window will be shown later + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); +``` + +Since flutter 3.7 new windows project +Change the file `windows/runner/flutter_window.cpp` as follows: + +```diff +bool FlutterWindow::OnCreate() { + ... + flutter_controller_->engine()->SetNextFrameCallback([&]() { +- this->Show(); ++ "" //delete this->Show() + }); +``` + +Make sure to call `setState` once on the `onWindowFocus` event. + +```dart +import 'package:flutter/cupertino.dart'; +import 'package:window_manager/window_manager.dart'; + +class HomePage extends StatefulWidget { + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State with WindowListener { + @override + void initState() { + super.initState(); + windowManager.addListener(this); + } + + @override + void dispose() { + windowManager.removeListener(this); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + // ... + } + + @override + void onWindowFocus() { + // Make sure to call once. + setState(() {}); + // do something + } +} + +``` + +## Related Links + +- [Click the dock icon to restore after closing the window](https://leanflutter.dev/blog/click-dock-icon-to-restore-after-closing-the-window/) +- [Making the app single-instanced](https://leanflutter.dev/blog/making-the-app-single-instanced/) diff --git a/docs/zh/index.md b/docs/zh/index.md new file mode 100644 index 00000000..2484d345 --- /dev/null +++ b/docs/zh/index.md @@ -0,0 +1,34 @@ +# 介绍 + +`window_manager` 插件为 Flutter 桌面应用程序提供了全面的窗口管理功能,使开发者能够完全控制窗口大小、位置、外观、关闭行为,以及监听事件。 + +## 核心功能 + +### 窗口控制 + +- 设置大小/最小/最大限制 +- 在多个显示器上定位窗口 +- 管理窗口状态:最大化/最小化/全屏 +- 自定义关闭处理程序 + +### 视觉定制 + +- 隐藏标题栏 +- 创建无边框窗口 +- 调整不透明度/背景颜色 +- 控制窗口阴影 + +### 事件监听器 + +- 生命周期:打开/关闭事件 +- 状态:最大化/最小化/全屏 +- 位置:移动/调整大小 +- 焦点:获取焦点/失去焦点 + +## 平台支持 + +| 平台 | 支持 | +| -------- | :----------------- | +| Linux | ✔️ 完全支持 | +| macOS | ✔️ 完全支持 | +| Windows | ✔️ 完全支持 | diff --git a/docs/zh/quick-start.md b/docs/zh/quick-start.md new file mode 100644 index 00000000..8c808032 --- /dev/null +++ b/docs/zh/quick-start.md @@ -0,0 +1,376 @@ +# 快速开始 + +按照以下步骤快速开始使用 `window_manager` 插件: + +## 安装 + +将以下内容添加到您的软件包的 `pubspec.yaml` 文件中: + +```yaml +dependencies: + window_manager: ^0.4.2 +``` + +或者 + +```yaml +dependencies: + window_manager: + git: + url: https://github.com/leanflutter/window_manager.git + ref: main +``` + +## 使用方法 + +```dart +import 'package:flutter/material.dart'; +import 'package:window_manager/window_manager.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + // 必须添加这一行 + await windowManager.ensureInitialized(); + + WindowOptions windowOptions = WindowOptions( + size: Size(800, 600), + center: true, + backgroundColor: Colors.transparent, + skipTaskbar: false, + titleBarStyle: TitleBarStyle.hidden, + ); + windowManager.waitUntilReadyToShow(windowOptions, () async { + await windowManager.show(); + await windowManager.focus(); + }); + + runApp(MyApp()); +} + +``` + +> 完整示例请参见此插件的示例应用。 + +### 监听事件 + +```dart +import 'package:flutter/cupertino.dart'; +import 'package:window_manager/window_manager.dart'; + +class HomePage extends StatefulWidget { + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State with WindowListener { + @override + void initState() { + super.initState(); + windowManager.addListener(this); + } + + @override + void dispose() { + windowManager.removeListener(this); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + // ... + } + + @override + void onWindowEvent(String eventName) { + print('[WindowManager] onWindowEvent: $eventName'); + } + + @override + void onWindowClose() { + // 做些什么 + } + + @override + void onWindowFocus() { + // 做些什么 + } + + @override + void onWindowBlur() { + // 做些什么 + } + + @override + void onWindowMaximize() { + // 做些什么 + } + + @override + void onWindowUnmaximize() { + // 做些什么 + } + + @override + void onWindowMinimize() { + // 做些什么 + } + + @override + void onWindowRestore() { + // 做些什么 + } + + @override + void onWindowResize() { + // 做些什么 + } + + @override + void onWindowMove() { + // 做些什么 + } + + @override + void onWindowEnterFullScreen() { + // 做些什么 + } + + @override + void onWindowLeaveFullScreen() { + // 做些什么 + } +} +``` + +### 关闭时退出 + +如果您需要使用隐藏方法,您需要禁用 `QuitOnClose`。 + +#### macOS + +按如下方式更改文件 `macos/Runner/AppDelegate.swift`: + +```diff +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { +- return true ++ return false + } +} +``` + +### 关闭前确认 + +```dart +import 'package:flutter/cupertino.dart'; +import 'package:window_manager/window_manager.dart'; + +class HomePage extends StatefulWidget { + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State with WindowListener { + @override + void initState() { + super.initState(); + windowManager.addListener(this); + _init(); + } + + @override + void dispose() { + windowManager.removeListener(this); + super.dispose(); + } + + void _init() async { + // 添加此行以覆盖默认的关闭处理程序 + await windowManager.setPreventClose(true); + setState(() {}); + } + + @override + Widget build(BuildContext context) { + // ... + } + + @override + void onWindowClose() async { + bool _isPreventClose = await windowManager.isPreventClose(); + if (_isPreventClose) { + showDialog( + context: context, + builder: (_) { + return AlertDialog( + title: Text('您确定要关闭此窗口吗?'), + actions: [ + TextButton( + child: Text('否'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: Text('是'), + onPressed: () { + Navigator.of(context).pop(); + await windowManager.destroy(); + }, + ), + ], + ); + }, + ); + } + } +} +``` + +### 启动时隐藏 + +在启动 Flutter 桌面应用程序时,可能会有一个短暂的时刻,其中显示未样式化的窗口,然后才应用自定义样式。这可能会为用户创造一种不连贯的视觉体验。 + +为了防止这种情况,我们可以最初隐藏窗口,只有在 Flutter 完全初始化并应用所有样式后才显示它。这创造了更平滑的启动体验。 + +以下是如何实现这一点: + +#### Linux + +按如下方式更改文件 `linux/my_application.cc`: + +```diff + +... + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + + ... + + gtk_window_set_default_size(window, 1280, 720); +- gtk_widget_show(GTK_WIDGET(window)); ++ gtk_widget_realize(GTK_WIDGET(window)); + + 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); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +... + +``` + +#### macOS + +按如下方式更改文件 `macos/Runner/MainFlutterWindow.swift`: + +```diff +import Cocoa +import FlutterMacOS ++import window_manager + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } + ++ override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) { ++ super.order(place, relativeTo: otherWin) ++ hiddenWindowAtLaunch() ++ } +} + +``` + +#### Windows + +按如下方式更改文件 `windows/runner/win32_window.cpp`: + +```diff +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + ... + HWND window = CreateWindow( +- window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, ++ window_class, title.c_str(), ++ WS_OVERLAPPEDWINDOW, // 不要添加 WS_VISIBLE,因为窗口将在稍后显示 + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); +``` + +自 flutter 3.7 新的 windows 项目以来 +按如下方式更改文件 `windows/runner/flutter_window.cpp`: + +```diff +bool FlutterWindow::OnCreate() { + ... + flutter_controller_->engine()->SetNextFrameCallback([&]() { +- this->Show(); ++ "" //删除 this->Show() + }); +``` + +确保在 `onWindowFocus` 事件上调用一次 `setState`。 + +```dart +import 'package:flutter/cupertino.dart'; +import 'package:window_manager/window_manager.dart'; + +class HomePage extends StatefulWidget { + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State with WindowListener { + @override + void initState() { + super.initState(); + windowManager.addListener(this); + } + + @override + void dispose() { + windowManager.removeListener(this); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + // ... + } + + @override + void onWindowFocus() { + // 确保只调用一次 + setState(() {}); + // 做些什么 + } +} + +``` + +## 相关链接 + +- [关闭窗口后点击dock图标恢复](https://leanflutter.dev/blog/click-dock-icon-to-restore-after-closing-the-window/) +- [使应用程序单实例化](https://leanflutter.dev/blog/making-the-app-single-instanced/) From 5a43aed33ffd9f86e1bac2fc9f4931383cc2ee2b Mon Sep 17 00:00:00 2001 From: LiJianying Date: Sat, 29 Mar 2025 11:17:25 +0800 Subject: [PATCH 41/50] Add badges to documentation for GitHub stars, Pub likes, and contributors --- docs/en/index.md | 15 +++++++++++++++ docs/zh/index.md | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/docs/en/index.md b/docs/en/index.md index 440fe5f8..a965a905 100644 --- a/docs/en/index.md +++ b/docs/en/index.md @@ -2,6 +2,21 @@ The `window_manager` plugin provides comprehensive window management capabilities for Flutter desktop applications, enabling full control over window size, position, appearance, close behavior, and listening to events. + + ## Core Features ### Window Control diff --git a/docs/zh/index.md b/docs/zh/index.md index 2484d345..ce9fa552 100644 --- a/docs/zh/index.md +++ b/docs/zh/index.md @@ -2,6 +2,21 @@ `window_manager` 插件为 Flutter 桌面应用程序提供了全面的窗口管理功能,使开发者能够完全控制窗口大小、位置、外观、关闭行为,以及监听事件。 + + ## 核心功能 ### 窗口控制 From 49998047fdc79048e3ca6800e75612c5f3fcd179 Mon Sep 17 00:00:00 2001 From: LiJianying Date: Thu, 1 May 2025 21:17:00 +0800 Subject: [PATCH 42/50] Update README files to include migration notice to nativeapi --- README-ZH.md | 2 ++ README.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README-ZH.md b/README-ZH.md index dcdfa554..d84d2758 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -14,6 +14,8 @@ 这个插件为 Flutter 桌面应用程序提供了全面的窗口管理功能,使开发者能够完全控制窗口大小、位置、外观、关闭行为,以及监听事件。 +> 注意:本插件计划迁移至 [nativeapi](https://github.com/leanflutter/nativeapi-flutter) 以提升可维护性和性能,但目前该方案仍处于实验阶段。 + --- [English](./README.md) | 简体中文 diff --git a/README.md b/README.md index ba9cb594..496e0122 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ This plugin provides comprehensive window management capabilities for Flutter desktop applications, enabling full control over window size, position, appearance, close behavior, and listening to events. +> Note: This plugin is planned to migrate to [nativeapi](https://github.com/leanflutter/nativeapi-flutter) to improve maintainability and performance, but the solution is still experimental at this stage. + --- English | [简体中文](./README-ZH.md) From ff6b0f4c77cefabb9706a7c4813115275888db28 Mon Sep 17 00:00:00 2001 From: Yui Date: Thu, 29 May 2025 09:25:20 +0800 Subject: [PATCH 43/50] [Windows] Use frameless window to implement fullscreen (#531) * use frameless window to implement fullscreen on windows * remove force refresh to avoid freezing the UI --- .../lib/src/window_manager.dart | 10 +++--- .../window_manager/windows/window_manager.cpp | 36 ++++++++++++++----- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/packages/window_manager/lib/src/window_manager.dart b/packages/window_manager/lib/src/window_manager.dart index dcfa1c8c..01f23d62 100644 --- a/packages/window_manager/lib/src/window_manager.dart +++ b/packages/window_manager/lib/src/window_manager.dart @@ -263,11 +263,11 @@ class WindowManager { await _channel.invokeMethod('setFullScreen', arguments); // (Windows) Force refresh the app so it 's back to the correct size // (see GitHub issue #311) - if (Platform.isWindows) { - final size = await getSize(); - setSize(size + const Offset(1, 1)); - setSize(size); - } + // if (Platform.isWindows) { + // final size = await getSize(); + // setSize(size + const Offset(1, 1)); + // setSize(size); + // } } /// Returns `bool` - Whether the window is dockable or not. diff --git a/packages/window_manager/windows/window_manager.cpp b/packages/window_manager/windows/window_manager.cpp index a27fc4f0..1d19490f 100644 --- a/packages/window_manager/windows/window_manager.cpp +++ b/packages/window_manager/windows/window_manager.cpp @@ -589,7 +589,7 @@ void WindowManager::SetFullScreen(const flutter::EncodableMap& args) { g_is_window_fullscreen = isFullScreen; if (isFullScreen) { // Set to fullscreen - ::SendMessage(mainWindow, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + // ::SendMessage(mainWindow, WM_SYSCOMMAND, SC_MAXIMIZE, 0); if (!is_frameless_) { auto monitor = MONITORINFO{}; auto placement = WINDOWPLACEMENT{}; @@ -598,19 +598,26 @@ void WindowManager::SetFullScreen(const flutter::EncodableMap& args) { ::GetWindowPlacement(mainWindow, &placement); ::GetMonitorInfo( ::MonitorFromWindow(mainWindow, MONITOR_DEFAULTTONEAREST), &monitor); - ::SetWindowLongPtr(mainWindow, GWL_STYLE, - g_style_before_fullscreen & ~WS_OVERLAPPEDWINDOW); + if (!g_maximized_before_fullscreen) { + SetAsFrameless(); + } + ::SetWindowLongPtr( + mainWindow, GWL_STYLE, + g_style_before_fullscreen & ~(WS_THICKFRAME | WS_MAXIMIZEBOX)); ::SetWindowPos(mainWindow, HWND_TOP, monitor.rcMonitor.left, - monitor.rcMonitor.top, + monitor.rcMonitor.top, 0, 0, + SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + ::SetWindowPos(mainWindow, HWND_TOP, 0, 0, monitor.rcMonitor.right - monitor.rcMonitor.left, monitor.rcMonitor.bottom - monitor.rcMonitor.top, - SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); } } else { // Restore from fullscreen - if (!g_maximized_before_fullscreen) - Restore(); - ::SetWindowLongPtr(mainWindow, GWL_STYLE, - g_style_before_fullscreen | WS_OVERLAPPEDWINDOW); + // if (!g_maximized_before_fullscreen) + // Restore(); + ::SetWindowLongPtr( + mainWindow, GWL_STYLE, + g_style_before_fullscreen | (WS_THICKFRAME | WS_MAXIMIZEBOX)); if (::IsZoomed(mainWindow)) { // Refresh the parent mainWindow. ::SetWindowPos(mainWindow, nullptr, 0, 0, 0, 0, @@ -632,6 +639,17 @@ void WindowManager::SetFullScreen(const flutter::EncodableMap& args) { g_frame_before_fullscreen.right - g_frame_before_fullscreen.left, g_frame_before_fullscreen.bottom - g_frame_before_fullscreen.top, SWP_NOACTIVATE | SWP_NOZORDER); + + // restore titlebar style + title_bar_style_ = g_title_bar_style_before_fullscreen; + is_frameless_ = false; + MARGINS margins = {0, 0, 0, 0}; + RECT rect1; + GetWindowRect(mainWindow, &rect1); + DwmExtendFrameIntoClientArea(mainWindow, &margins); + SetWindowPos(mainWindow, nullptr, rect1.left, rect1.top, 0, 0, + SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | + SWP_FRAMECHANGED); } } } From db19195a6df166ec996f7618136c03f27df63262 Mon Sep 17 00:00:00 2001 From: Joyer Huang <110209+uppet@users.noreply.github.com> Date: Thu, 29 May 2025 09:28:32 +0800 Subject: [PATCH 44/50] Fix crash when using window_manager by multi engine on windows platform. channel should not be gloabal variable. (#546) --- .../windows/window_manager_plugin.cpp | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/window_manager/windows/window_manager_plugin.cpp b/packages/window_manager/windows/window_manager_plugin.cpp index 36e767ab..489af340 100644 --- a/packages/window_manager/windows/window_manager_plugin.cpp +++ b/packages/window_manager/windows/window_manager_plugin.cpp @@ -31,11 +31,6 @@ bool IsWindows11OrGreater() { return dwBuild < 22000; } -std::unique_ptr< - flutter::MethodChannel, - std::default_delete>> - channel = nullptr; - class WindowManagerPlugin : public flutter::Plugin { public: static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); @@ -45,6 +40,12 @@ class WindowManagerPlugin : public flutter::Plugin { virtual ~WindowManagerPlugin(); private: + std::unique_ptr< + flutter::MethodChannel, + std::default_delete>> + channel = nullptr; + + WindowManager* window_manager; flutter::PluginRegistrarWindows* registrar; @@ -95,17 +96,8 @@ class WindowManagerPlugin : public flutter::Plugin { // static void WindowManagerPlugin::RegisterWithRegistrar( flutter::PluginRegistrarWindows* registrar) { - channel = std::make_unique>( - registrar->messenger(), "window_manager", - &flutter::StandardMethodCodec::GetInstance()); - auto plugin = std::make_unique(registrar); - channel->SetMethodCallHandler( - [plugin_pointer = plugin.get()](const auto& call, auto result) { - plugin_pointer->HandleMethodCall(call, std::move(result)); - }); - registrar->AddPlugin(std::move(plugin)); } @@ -117,6 +109,14 @@ WindowManagerPlugin::WindowManagerPlugin( [this](HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { return HandleWindowProc(hWnd, message, wParam, lParam); }); + channel = std::make_unique>( + registrar->messenger(), "window_manager", + &flutter::StandardMethodCodec::GetInstance()); + + channel->SetMethodCallHandler( + [this](const auto& call, auto result) { + HandleMethodCall(call, std::move(result)); + }); } WindowManagerPlugin::~WindowManagerPlugin() { From bc893bf657d5a49491ac8c85235e04e653715181 Mon Sep 17 00:00:00 2001 From: copywith9 Date: Thu, 29 May 2025 03:39:09 +0200 Subject: [PATCH 45/50] Add getWindowHandle (#548) Co-authored-by: copywith9 --- packages/window_manager/example/lib/pages/home.dart | 8 ++++++++ packages/window_manager/lib/src/window_manager.dart | 4 ++++ packages/window_manager/windows/window_manager_plugin.cpp | 6 +++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/window_manager/example/lib/pages/home.dart b/packages/window_manager/example/lib/pages/home.dart index d9149ec7..42ba6eb1 100644 --- a/packages/window_manager/example/lib/pages/home.dart +++ b/packages/window_manager/example/lib/pages/home.dart @@ -180,6 +180,14 @@ class _HomePageState extends State with TrayListener, WindowListener { print('isFocused: ${await windowManager.isFocused()}'); }, ), + if(Platform.isWindows) + PreferenceListItem( + title: const Text('getWindowHandle'), + onTap: () async { + final result = await windowManager.getWindowHandle(); + print('HWND:$result'); + }, + ), PreferenceListItem( title: const Text('show / hide'), onTap: () async { diff --git a/packages/window_manager/lib/src/window_manager.dart b/packages/window_manager/lib/src/window_manager.dart index 01f23d62..2ef3e790 100644 --- a/packages/window_manager/lib/src/window_manager.dart +++ b/packages/window_manager/lib/src/window_manager.dart @@ -104,6 +104,10 @@ class WindowManager { await _channel.invokeMethod('ensureInitialized'); } + Future getWindowHandle()async{ + return await _channel.invokeMethod('getWindowHandle') as int; + } + /// You can call this to remove the window frame (title bar, outline border, etc), which is basically everything except the Flutter view, also can call setTitleBarStyle(TitleBarStyle.normal) or setTitleBarStyle(TitleBarStyle.hidden) to restore it. Future setAsFrameless() async { await _channel.invokeMethod('setAsFrameless'); diff --git a/packages/window_manager/windows/window_manager_plugin.cpp b/packages/window_manager/windows/window_manager_plugin.cpp index 489af340..8a289b93 100644 --- a/packages/window_manager/windows/window_manager_plugin.cpp +++ b/packages/window_manager/windows/window_manager_plugin.cpp @@ -350,7 +350,11 @@ void WindowManagerPlugin::HandleMethodCall( window_manager->native_window = ::GetAncestor(registrar->GetView()->GetNativeWindow(), GA_ROOT); result->Success(flutter::EncodableValue(true)); - } else if (method_name.compare("waitUntilReadyToShow") == 0) { + } + else if (method_name.compare("getWindowHandle") == 0) { + result->Success(flutter::EncodableValue(reinterpret_cast<__int64>(window_manager->GetMainWindow()))); + } + else if (method_name.compare("waitUntilReadyToShow") == 0) { window_manager->WaitUntilReadyToShow(); result->Success(flutter::EncodableValue(true)); } else if (method_name.compare("setAsFrameless") == 0) { From f94b6186aeadd05064c065883cc1885733dd9e13 Mon Sep 17 00:00:00 2001 From: LiJianying Date: Thu, 29 May 2025 20:05:55 +0800 Subject: [PATCH 46/50] Add Swift Package Manager support --- .github/workflows/build.yml | 8 +- .github/workflows/lint.yml | 4 +- .github/workflows/test.yml | 6 +- README-ZH.md | 17 +- README.md | 13 +- packages/window_manager/.gitignore | 5 +- packages/window_manager/example/.gitignore | 7 +- .../window_manager/example/macos/.gitignore | 1 + packages/window_manager/example/macos/Podfile | 3 + .../window_manager/example/macos/Podfile.lock | 14 +- .../macos/Runner.xcodeproj/project.pbxproj | 251 +++++++++++++++--- .../xcshareddata/xcschemes/Runner.xcscheme | 30 +++ .../example/macos/Runner/AppDelegate.swift | 4 + .../AppIcon.appiconset/app_icon_1024.png | Bin 46993 -> 102994 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 3276 -> 5680 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 1429 -> 520 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 5933 -> 14142 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 1243 -> 1066 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 14800 -> 36406 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 1874 -> 2218 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 4 + .../macos/Runner/Configs/AppInfo.xcconfig | 4 +- .../macos/Runner/MainFlutterWindow.swift | 7 +- .../macos/RunnerTests/RunnerTests.swift | 30 +++ .../macos/window_manager.podspec | 16 +- .../macos/window_manager/Package.swift | 32 +++ .../window_manager/PrivacyInfo.xcprivacy | 12 + .../window_manager}/WindowManager.swift | 0 .../window_manager}/WindowManagerPlugin.swift | 2 - packages/window_manager/pubspec.yaml | 10 +- 30 files changed, 394 insertions(+), 86 deletions(-) create mode 100644 packages/window_manager/example/macos/RunnerTests/RunnerTests.swift create mode 100644 packages/window_manager/macos/window_manager/Package.swift create mode 100644 packages/window_manager/macos/window_manager/Sources/window_manager/PrivacyInfo.xcprivacy rename packages/window_manager/macos/{Classes => window_manager/Sources/window_manager}/WindowManager.swift (100%) rename packages/window_manager/macos/{Classes => window_manager/Sources/window_manager}/WindowManagerPlugin.swift (98%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e41eeb00..967f5ec3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2 with: - flutter-version: "3.24.3" + flutter-version: "3.32.0" channel: "stable" - run: | sudo apt-get update @@ -30,7 +30,7 @@ jobs: - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2 with: - flutter-version: "3.24.3" + flutter-version: "3.32.0" channel: "stable" - uses: bluefireteam/melos-action@v3 - working-directory: ./packages/window_manager/example @@ -43,7 +43,7 @@ jobs: - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2 with: - flutter-version: "3.24.3" + flutter-version: "3.32.0" channel: "stable" - uses: bluefireteam/melos-action@v3 - working-directory: ./packages/window_manager/example @@ -56,7 +56,7 @@ jobs: - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2 with: - flutter-version: "3.24.3" + flutter-version: "3.32.0" channel: "stable" - uses: bluefireteam/melos-action@v3 - working-directory: ./packages/window_manager/example diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ce824ca0..a1ddda09 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2 with: - flutter-version: "3.24.3" + flutter-version: "3.32.0" channel: "stable" - uses: bluefireteam/melos-action@v3 - run: melos run analyze @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2 with: - flutter-version: "3.24.3" + flutter-version: "3.32.0" channel: "stable" cache: true - uses: bluefireteam/melos-action@v3 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 88fb1685..ad6b197e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: - uses: subosito/flutter-action@v2 with: channel: stable - flutter-version: "3.24.3" + flutter-version: "3.32.0" - run: | sudo apt-get update -y sudo apt-get install -y ninja-build libgtk-3-dev ayatana-appindicator3-0.1 xvfb @@ -28,7 +28,7 @@ jobs: - uses: subosito/flutter-action@v2 with: channel: stable - flutter-version: "3.24.3" + flutter-version: "3.32.0" - uses: bluefireteam/melos-action@v3 # Blocked by https://github.com/flutter/flutter/issues/118469 # - working-directory: ./packages/window_manager/example @@ -40,7 +40,7 @@ jobs: - uses: subosito/flutter-action@v2 with: channel: stable - flutter-version: "3.24.3" + flutter-version: "3.32.0" - uses: bluefireteam/melos-action@v3 - working-directory: ./packages/window_manager/example run: flutter test integration_test -v diff --git a/README-ZH.md b/README-ZH.md index d84d2758..2e9551d8 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -2,14 +2,14 @@ # window_manager -[![pub version][pub-image]][pub-url] [![][discord-image]][discord-url] [![All Contributors][all-contributors-image]](#contributors) +[![pub version][pub-image]][pub-url] [![Pub Monthly Downloads][pub-dm-image]][pub-dm-url] [![][discord-image]][discord-url] [![All Contributors][all-contributors-image]](#contributors) [pub-image]: https://img.shields.io/pub/v/window_manager.svg [pub-url]: https://pub.dev/packages/window_manager - +[pub-dm-image]: https://img.shields.io/pub/dm/window_manager.svg +[pub-dm-url]: https://pub.dev/packages/window_manager/score [discord-image]: https://img.shields.io/discord/884679008049037342.svg [discord-url]: https://discord.gg/zPa6EZ2jqb - [all-contributors-image]: https://img.shields.io/github/all-contributors/leanflutter/window_manager?color=ee8449&style=flat-square 这个插件为 Flutter 桌面应用程序提供了全面的窗口管理功能,使开发者能够完全控制窗口大小、位置、外观、关闭行为,以及监听事件。 @@ -41,7 +41,7 @@ | Linux | macOS | Windows | | :---: | :---: | :-----: | -| ✔️ | ✔️ | ✔️ | +| ✔️ | ✔️ | ✔️ | ## 文档 @@ -57,7 +57,7 @@ ```yaml dependencies: - window_manager: ^0.4.2 + window_manager: ^0.5.0 ``` ### 用法 @@ -92,13 +92,13 @@ void main() async { ## 相关文章 -- [关闭窗口后点击Dock图标进行恢复](https://leanflutter.dev/zh/blog/click-dock-icon-to-restore-after-closing-the-window) +- [关闭窗口后点击 Dock 图标进行恢复](https://leanflutter.dev/zh/blog/click-dock-icon-to-restore-after-closing-the-window) - [让应用成为单实例](https://leanflutter.dev/zh/blog/making-the-app-single-instanced) ## 谁在用使用它? -- [Airclap](https://airclap.app/) - 任何文件,任意设备,随意发送。简单好用的跨平台高速文件传输APP。 -- [AuthPass](https://authpass.app/) - 基于Flutter的密码管理器,适用于所有平台。兼容Keepass 2.x(kdbx 3.x)。 +- [Airclap](https://airclap.app/) - 任何文件,任意设备,随意发送。简单好用的跨平台高速文件传输 APP。 +- [AuthPass](https://authpass.app/) - 基于 Flutter 的密码管理器,适用于所有平台。兼容 Keepass 2.x(kdbx 3.x)。 - [Biyi (比译)](https://biyidev.com/) - 一个便捷的翻译和词典应用程序。 - [BlueBubbles](https://github.com/BlueBubblesApp/bluebubbles-app) - BlueBubbles is an ecosystem of apps bringing iMessage to Android, Windows, and Linux - [LunaSea](https://github.com/CometTools/LunaSea) - A self-hosted controller for mobile and macOS built using the Flutter framework. @@ -108,7 +108,6 @@ void main() async { - [UniControlHub](https://github.com/rohitsangwan01/uni_control_hub) - Seamlessly bridge your Desktop and Mobile devices - [EyesCare](https://bixat.dev/products/EyesCare) - A light-weight application following 20 rule adherence for optimum eye health - ## 贡献者 diff --git a/README.md b/README.md index 496e0122..05dfbcba 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,14 @@ # window_manager -[![pub version][pub-image]][pub-url] [![][discord-image]][discord-url] [![All Contributors][all-contributors-image]](#contributors) +[![pub version][pub-image]][pub-url] [![Pub Monthly Downloads][pub-dm-image]][pub-dm-url] [![][discord-image]][discord-url] [![All Contributors][all-contributors-image]](#contributors) [pub-image]: https://img.shields.io/pub/v/window_manager.svg [pub-url]: https://pub.dev/packages/window_manager - +[pub-dm-image]: https://img.shields.io/pub/dm/window_manager.svg +[pub-dm-url]: https://pub.dev/packages/window_manager/score [discord-image]: https://img.shields.io/discord/884679008049037342.svg [discord-url]: https://discord.gg/zPa6EZ2jqb - [all-contributors-image]: https://img.shields.io/github/all-contributors/leanflutter/window_manager?color=ee8449&style=flat-square This plugin provides comprehensive window management capabilities for Flutter desktop applications, enabling full control over window size, position, appearance, close behavior, and listening to events. @@ -41,7 +41,7 @@ English | [简体中文](./README-ZH.md) | Linux | macOS | Windows | | :---: | :---: | :-----: | -| ✔️ | ✔️ | ✔️ | +| ✔️ | ✔️ | ✔️ | ## Documentation @@ -57,7 +57,7 @@ Add this to your package's `pubspec.yaml` file: ```yaml dependencies: - window_manager: ^0.4.2 + window_manager: ^0.5.0 ``` ### Usage @@ -103,12 +103,11 @@ void main() async { - [BlueBubbles](https://github.com/BlueBubblesApp/bluebubbles-app) - BlueBubbles is an ecosystem of apps bringing iMessage to Android, Windows, and Linux - [LunaSea](https://github.com/CometTools/LunaSea) - A self-hosted controller for mobile and macOS built using the Flutter framework. - [Linwood Butterfly](https://github.com/LinwoodCloud/Butterfly) - Open source note taking app written in Flutter -- [RustDesk](https://github.com/rustdesk/rustdesk) - Yet another remote desktop software, written in Rust. Works out of the box, no configuration required. +- [RustDesk](https://github.com/rustdesk/rustdesk) - Yet another remote desktop software, written in Rust. Works out of the box, no configuration required. - [Ubuntu Desktop Installer](https://github.com/canonical/ubuntu-desktop-installer) - This project is a modern implementation of the Ubuntu Desktop installer. - [UniControlHub](https://github.com/rohitsangwan01/uni_control_hub) - Seamlessly bridge your Desktop and Mobile devices - [EyesCare](https://bixat.dev/products/EyesCare) - A light-weight application following 20 rule adherence for optimum eye health - ## Contributors diff --git a/packages/window_manager/.gitignore b/packages/window_manager/.gitignore index 96486fd9..e7d347d9 100644 --- a/packages/window_manager/.gitignore +++ b/packages/window_manager/.gitignore @@ -5,9 +5,11 @@ *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ migrate_working_dir/ # IntelliJ related @@ -26,5 +28,6 @@ migrate_working_dir/ /pubspec.lock **/doc/api/ .dart_tool/ -.packages +.flutter-plugins +.flutter-plugins-dependencies build/ diff --git a/packages/window_manager/example/.gitignore b/packages/window_manager/example/.gitignore index 0fa6b675..79c113f9 100644 --- a/packages/window_manager/example/.gitignore +++ b/packages/window_manager/example/.gitignore @@ -5,9 +5,12 @@ *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ +migrate_working_dir/ # IntelliJ related *.iml @@ -26,14 +29,10 @@ .dart_tool/ .flutter-plugins .flutter-plugins-dependencies -.packages .pub-cache/ .pub/ /build/ -# Web related -lib/generated_plugin_registrant.dart - # Symbolication related app.*.symbols diff --git a/packages/window_manager/example/macos/.gitignore b/packages/window_manager/example/macos/.gitignore index d2fd3772..746adbb6 100644 --- a/packages/window_manager/example/macos/.gitignore +++ b/packages/window_manager/example/macos/.gitignore @@ -3,4 +3,5 @@ **/Pods/ # Xcode-related +**/dgph **/xcuserdata/ diff --git a/packages/window_manager/example/macos/Podfile b/packages/window_manager/example/macos/Podfile index 049abe29..c795730d 100644 --- a/packages/window_manager/example/macos/Podfile +++ b/packages/window_manager/example/macos/Podfile @@ -31,6 +31,9 @@ target 'Runner' do use_modular_headers! flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end end post_install do |installer| diff --git a/packages/window_manager/example/macos/Podfile.lock b/packages/window_manager/example/macos/Podfile.lock index f7e4c3ee..a3151148 100644 --- a/packages/window_manager/example/macos/Podfile.lock +++ b/packages/window_manager/example/macos/Podfile.lock @@ -4,14 +4,11 @@ PODS: - FlutterMacOS - tray_manager (0.0.1): - FlutterMacOS - - window_manager (0.2.0): - - FlutterMacOS DEPENDENCIES: - FlutterMacOS (from `Flutter/ephemeral`) - screen_retriever_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos`) - tray_manager (from `Flutter/ephemeral/.symlinks/plugins/tray_manager/macos`) - - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`) EXTERNAL SOURCES: FlutterMacOS: @@ -20,15 +17,12 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos tray_manager: :path: Flutter/ephemeral/.symlinks/plugins/tray_manager/macos - window_manager: - :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - screen_retriever_macos: 776e0fa5d42c6163d2bf772d22478df4b302b161 - tray_manager: 9064e219c56d75c476e46b9a21182087930baf90 - window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 + screen_retriever_macos: 452e51764a9e1cdb74b3c541238795849f21557f + tray_manager: a104b5c81b578d83f3c3d0f40a997c8b10810166 -PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 +PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 -COCOAPODS: 1.14.3 +COCOAPODS: 1.16.2 diff --git a/packages/window_manager/example/macos/Runner.xcodeproj/project.pbxproj b/packages/window_manager/example/macos/Runner.xcodeproj/project.pbxproj index 62683b9b..60b9e0db 100644 --- a/packages/window_manager/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/window_manager/example/macos/Runner.xcodeproj/project.pbxproj @@ -21,15 +21,25 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - 2CA19803D6C6204BCC35ADD4 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C6A4F049A9F3B091DF4DA50D /* Pods_Runner.framework */; }; + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 6536BEDA2B22F2A766B3EB03 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15DA7BF5F85ED7E8B52636D9 /* Pods_RunnerTests.framework */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; + EDDBFC46D2CB2BDA58BBC6AF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EBB821182A9EF48FEE298842 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 33CC10E52044A3C60003C045 /* Project object */; @@ -53,6 +63,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 15DA7BF5F85ED7E8B52636D9 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 1CC1C1C094F37109A9FDE8BA /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; 33CC10ED2044A3C60003C045 /* window_manager_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = window_manager_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -67,34 +81,43 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 6D4C81DEA1832566C94F300A /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 388625FFBE0E8EB617E6F221 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 48DCA1209BB675EC43B2A1CE /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 4BF95543CB2FF2EED539A03F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 59E9B0F3408BE11C0ECA1A14 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 95E16F7EB53810DBAB67EF71 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - B8321EC19EEAF1FD4ADB5061 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - C6A4F049A9F3B091DF4DA50D /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A0ADE3A8AFF023D5231C41D7 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + EBB821182A9EF48FEE298842 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6536BEDA2B22F2A766B3EB03 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 33CC10EA2044A3C60003C045 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 2CA19803D6C6204BCC35ADD4 /* Pods_Runner.framework in Frameworks */, + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, + EDDBFC46D2CB2BDA58BBC6AF /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 1E5E01E4CEEC4BF3F861E259 /* Pods */ = { + 331C80D6294CF71000263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( - 95E16F7EB53810DBAB67EF71 /* Pods-Runner.debug.xcconfig */, - 6D4C81DEA1832566C94F300A /* Pods-Runner.release.xcconfig */, - B8321EC19EEAF1FD4ADB5061 /* Pods-Runner.profile.xcconfig */, + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, ); - path = Pods; + path = RunnerTests; sourceTree = ""; }; 33BA886A226E78AF003329D5 /* Configs */ = { @@ -113,9 +136,10 @@ children = ( 33FAB671232836740065AC1E /* Runner */, 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, - 1E5E01E4CEEC4BF3F861E259 /* Pods */, + BBCF47834DBAE561B251FC5A /* Pods */, ); sourceTree = ""; }; @@ -123,6 +147,7 @@ isa = PBXGroup; children = ( 33CC10ED2044A3C60003C045 /* window_manager_example.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -162,10 +187,25 @@ path = Runner; sourceTree = ""; }; + BBCF47834DBAE561B251FC5A /* Pods */ = { + isa = PBXGroup; + children = ( + 4BF95543CB2FF2EED539A03F /* Pods-Runner.debug.xcconfig */, + 59E9B0F3408BE11C0ECA1A14 /* Pods-Runner.release.xcconfig */, + 48DCA1209BB675EC43B2A1CE /* Pods-Runner.profile.xcconfig */, + A0ADE3A8AFF023D5231C41D7 /* Pods-RunnerTests.debug.xcconfig */, + 1CC1C1C094F37109A9FDE8BA /* Pods-RunnerTests.release.xcconfig */, + 388625FFBE0E8EB617E6F221 /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( - C6A4F049A9F3B091DF4DA50D /* Pods_Runner.framework */, + EBB821182A9EF48FEE298842 /* Pods_Runner.framework */, + 15DA7BF5F85ED7E8B52636D9 /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -173,17 +213,36 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 6BEBEA9A0F0115A74C3D155D /* [CP] Check Pods Manifest.lock */, + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 33CC10EC2044A3C60003C045 /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 63C747B0BDCA287A9CCED455 /* [CP] Check Pods Manifest.lock */, + 489D83C58F4EE230860FCF54 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 50308F624EF58D06702C30A4 /* [CP] Embed Pods Frameworks */, + FF90E40E6807600E3F3607C3 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -191,6 +250,9 @@ 33CC11202044C79F0003C045 /* PBXTargetDependency */, ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 33CC10ED2044A3C60003C045 /* window_manager_example.app */; productType = "com.apple.product-type.application"; @@ -201,13 +263,19 @@ 33CC10E52044A3C60003C045 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 0920; LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; 33CC10EC2044A3C60003C045 = { CreatedOnToolsVersion = 9.2; LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.Sandbox = { enabled = 1; @@ -229,17 +297,28 @@ Base, ); mainGroup = 33CC10E42044A3C60003C045; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, 33CC111A2044C6BA0003C045 /* Flutter Assemble */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 33CC10EB2044A3C60003C045 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -290,24 +369,29 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - 50308F624EF58D06702C30A4 /* [CP] Embed Pods Frameworks */ = { + 489D83C58F4EE230860FCF54 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Embed Pods Frameworks"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 63C747B0BDCA287A9CCED455 /* [CP] Check Pods Manifest.lock */ = { + 6BEBEA9A0F0115A74C3D155D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -322,16 +406,41 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + FF90E40E6807600E3F3607C3 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 33CC10E92044A3C60003C045 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -345,6 +454,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; @@ -365,11 +479,57 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A0ADE3A8AFF023D5231C41D7 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.windowManagerExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/window_manager_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/window_manager_example"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 1CC1C1C094F37109A9FDE8BA /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.windowManagerExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/window_manager_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/window_manager_example"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 388625FFBE0E8EB617E6F221 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.windowManagerExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/window_manager_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/window_manager_example"; + }; + name = Profile; + }; 338D0CE9231458BD00FA5F75 /* Profile */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -393,9 +553,11 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -403,7 +565,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -418,9 +580,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -444,6 +605,7 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -467,9 +629,11 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -483,7 +647,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -497,6 +661,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -520,9 +685,11 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -530,7 +697,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -545,9 +712,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -566,9 +732,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -598,6 +763,16 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -629,6 +804,20 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } diff --git a/packages/window_manager/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/window_manager/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index aee170f1..a5771437 100644 --- a/packages/window_manager/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/window_manager/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + + + + + diff --git a/packages/window_manager/example/macos/Runner/AppDelegate.swift b/packages/window_manager/example/macos/Runner/AppDelegate.swift index 2aa1f82a..0b1cef5f 100644 --- a/packages/window_manager/example/macos/Runner/AppDelegate.swift +++ b/packages/window_manager/example/macos/Runner/AppDelegate.swift @@ -19,4 +19,8 @@ class AppDelegate: FlutterAppDelegate { } return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png index 3c4935a7ca84f0976aca34b7f2895d65fb94d1ea..82b6f9d9a33e198f5747104729e1fcef999772a5 100644 GIT binary patch literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY literal 46993 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 diff --git a/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png index ed4cc16421680a50164ba74381b4b35ceaa0ccfc..13b35eba55c6dabc3aac36f33d859266c18fa0d0 100644 GIT binary patch literal 5680 zcmaiYXH?Tqu=Xz`p-L#B_gI#0we$cm_HcmYFP$?wjD#BaCN4mzC5#`>w9y6=ThxrYZc0WPXprg zYjB`UsV}0=eUtY$(P6YW}npdd;%9pi?zS3k-nqCob zSX_AQEf|=wYT3r?f!*Yt)ar^;l3Sro{z(7deUBPd2~(SzZ-s@0r&~Km2S?8r##9-< z)2UOSVaHqq6}%sA9Ww;V2LG=PnNAh6mA2iWOuV7T_lRDR z&N8-eN=U)-T|;wo^Wv=34wtV0g}sAAe}`Ph@~!|<;z7*K8(qkX0}o=!(+N*UWrkEja*$_H6mhK1u{P!AC39} z|3+Z(mAOq#XRYS)TLoHv<)d%$$I@+x+2)V{@o~~J-!YUI-Q9%!Ldi4Op&Lw&B>jj* zwAgC#Y>gbIqv!d|J5f!$dbCXoq(l3GR(S>(rtZ~Z*agXMMKN!@mWT_vmCbSd3dUUm z4M&+gz?@^#RRGal%G3dDvj7C5QTb@9+!MG+>0dcjtZEB45c+qx*c?)d<%htn1o!#1 zpIGonh>P1LHu3s)fGFF-qS}AXjW|M*2Xjkh7(~r(lN=o#mBD9?jt74=Rz85I4Nfx_ z7Z)q?!};>IUjMNM6ee2Thq7))a>My?iWFxQ&}WvsFP5LP+iGz+QiYek+K1`bZiTV- zHHYng?ct@Uw5!gquJ(tEv1wTrRR7cemI>aSzLI^$PxW`wL_zt@RSfZ1M3c2sbebM* ze0=;sy^!90gL~YKISz*x;*^~hcCoO&CRD)zjT(A2b_uRue=QXFe5|!cf0z1m!iwv5GUnLw9Dr*Ux z)3Lc!J@Ei;&&yxGpf2kn@2wJ2?t6~obUg;?tBiD#uo$SkFIasu+^~h33W~`r82rSa ztyE;ehFjC2hjpJ-e__EH&z?!~>UBb=&%DS>NT)1O3Isn-!SElBV2!~m6v0$vx^a<@ISutdTk1@?;i z<8w#b-%|a#?e5(n@7>M|v<<0Kpg?BiHYMRe!3Z{wYc2hN{2`6(;q`9BtXIhVq6t~KMH~J0~XtUuT06hL8c1BYZWhN zk4F2I;|za*R{ToHH2L?MfRAm5(i1Ijw;f+0&J}pZ=A0;A4M`|10ZskA!a4VibFKn^ zdVH4OlsFV{R}vFlD~aA4xxSCTTMW@Gws4bFWI@xume%smAnuJ0b91QIF?ZV!%VSRJ zO7FmG!swKO{xuH{DYZ^##gGrXsUwYfD0dxXX3>QmD&`mSi;k)YvEQX?UyfIjQeIm! z0ME3gmQ`qRZ;{qYOWt}$-mW*>D~SPZKOgP)T-Sg%d;cw^#$>3A9I(%#vsTRQe%moT zU`geRJ16l>FV^HKX1GG7fR9AT((jaVb~E|0(c-WYQscVl(z?W!rJp`etF$dBXP|EG z=WXbcZ8mI)WBN>3<@%4eD597FD5nlZajwh8(c$lum>yP)F}=(D5g1-WVZRc)(!E3} z-6jy(x$OZOwE=~{EQS(Tp`yV2&t;KBpG*XWX!yG+>tc4aoxbXi7u@O*8WWFOxUjcq z^uV_|*818$+@_{|d~VOP{NcNi+FpJ9)aA2So<7sB%j`$Prje&auIiTBb{oD7q~3g0 z>QNIwcz(V-y{Ona?L&=JaV5`o71nIsWUMA~HOdCs10H+Irew#Kr(2cn>orG2J!jvP zqcVX0OiF}c<)+5&p}a>_Uuv)L_j}nqnJ5a?RPBNi8k$R~zpZ33AA4=xJ@Z($s3pG9 zkURJY5ZI=cZGRt_;`hs$kE@B0FrRx(6K{`i1^*TY;Vn?|IAv9|NrN*KnJqO|8$e1& zb?OgMV&q5|w7PNlHLHF) zB+AK#?EtCgCvwvZ6*u|TDhJcCO+%I^@Td8CR}+nz;OZ*4Dn?mSi97m*CXXc=};!P`B?}X`F-B5v-%ACa8fo0W++j&ztmqK z;&A)cT4ob9&MxpQU41agyMU8jFq~RzXOAsy>}hBQdFVL%aTn~M>5t9go2j$i9=(rZ zADmVj;Qntcr3NIPPTggpUxL_z#5~C!Gk2Rk^3jSiDqsbpOXf^f&|h^jT4|l2ehPat zb$<*B+x^qO8Po2+DAmrQ$Zqc`1%?gp*mDk>ERf6I|42^tjR6>}4`F_Mo^N(~Spjcg z_uY$}zui*PuDJjrpP0Pd+x^5ds3TG#f?57dFL{auS_W8|G*o}gcnsKYjS6*t8VI<) zcjqTzW(Hk*t-Qhq`Xe+x%}sxXRerScbPGv8hlJ;CnU-!Nl=# zR=iTFf9`EItr9iAlAGi}i&~nJ-&+)Y| zMZigh{LXe)uR+4D_Yb+1?I93mHQ5{pId2Fq%DBr7`?ipi;CT!Q&|EO3gH~7g?8>~l zT@%*5BbetH)~%TrAF1!-!=)`FIS{^EVA4WlXYtEy^|@y@yr!C~gX+cp2;|O4x1_Ol z4fPOE^nj(}KPQasY#U{m)}TZt1C5O}vz`A|1J!-D)bR%^+=J-yJsQXDzFiqb+PT0! zIaDWWU(AfOKlSBMS};3xBN*1F2j1-_=%o($ETm8@oR_NvtMDVIv_k zlnNBiHU&h8425{MCa=`vb2YP5KM7**!{1O>5Khzu+5OVGY;V=Vl+24fOE;tMfujoF z0M``}MNnTg3f%Uy6hZi$#g%PUA_-W>uVCYpE*1j>U8cYP6m(>KAVCmbsDf39Lqv0^ zt}V6FWjOU@AbruB7MH2XqtnwiXS2scgjVMH&aF~AIduh#^aT1>*V>-st8%=Kk*{bL zzbQcK(l2~)*A8gvfX=RPsNnjfkRZ@3DZ*ff5rmx{@iYJV+a@&++}ZW+za2fU>&(4y`6wgMpQGG5Ah(9oGcJ^P(H< zvYn5JE$2B`Z7F6ihy>_49!6}(-)oZ(zryIXt=*a$bpIw^k?>RJ2 zQYr>-D#T`2ZWDU$pM89Cl+C<;J!EzHwn(NNnWpYFqDDZ_*FZ{9KQRcSrl5T>dj+eA zi|okW;6)6LR5zebZJtZ%6Gx8^=2d9>_670!8Qm$wd+?zc4RAfV!ZZ$jV0qrv(D`db zm_T*KGCh3CJGb(*X6nXzh!h9@BZ-NO8py|wG8Qv^N*g?kouH4%QkPU~Vizh-D3<@% zGomx%q42B7B}?MVdv1DFb!axQ73AUxqr!yTyFlp%Z1IAgG49usqaEbI_RnbweR;Xs zpJq7GKL_iqi8Md?f>cR?^0CA+Uk(#mTlGdZbuC*$PrdB$+EGiW**=$A3X&^lM^K2s zzwc3LtEs5|ho z2>U(-GL`}eNgL-nv3h7E<*<>C%O^=mmmX0`jQb6$mP7jUKaY4je&dCG{x$`0=_s$+ zSpgn!8f~ya&U@c%{HyrmiW2&Wzc#Sw@+14sCpTWReYpF9EQ|7vF*g|sqG3hx67g}9 zwUj5QP2Q-(KxovRtL|-62_QsHLD4Mu&qS|iDp%!rs(~ah8FcrGb?Uv^Qub5ZT_kn%I^U2rxo1DDpmN@8uejxik`DK2~IDi1d?%~pR7i#KTS zA78XRx<(RYO0_uKnw~vBKi9zX8VnjZEi?vD?YAw}y+)wIjIVg&5(=%rjx3xQ_vGCy z*&$A+bT#9%ZjI;0w(k$|*x{I1c!ECMus|TEA#QE%#&LxfGvijl7Ih!B2 z6((F_gwkV;+oSKrtr&pX&fKo3s3`TG@ye+k3Ov)<#J|p8?vKh@<$YE@YIU1~@7{f+ zydTna#zv?)6&s=1gqH<-piG>E6XW8ZI7&b@-+Yk0Oan_CW!~Q2R{QvMm8_W1IV8<+ zQTyy=(Wf*qcQubRK)$B;QF}Y>V6d_NM#=-ydM?%EPo$Q+jkf}*UrzR?Nsf?~pzIj$ z<$wN;7c!WDZ(G_7N@YgZ``l;_eAd3+;omNjlpfn;0(B7L)^;;1SsI6Le+c^ULe;O@ zl+Z@OOAr4$a;=I~R0w4jO`*PKBp?3K+uJ+Tu8^%i<_~bU!p%so z^sjol^slR`W@jiqn!M~eClIIl+`A5%lGT{z^mRbpv}~AyO%R*jmG_Wrng{B9TwIuS z0!@fsM~!57K1l0%{yy(#no}roy#r!?0wm~HT!vLDfEBs9x#`9yCKgufm0MjVRfZ=f z4*ZRc2Lgr(P+j2zQE_JzYmP0*;trl7{*N341Cq}%^M^VC3gKG-hY zmPT>ECyrhIoFhnMB^qpdbiuI}pk{qPbK^}0?Rf7^{98+95zNq6!RuV_zAe&nDk0;f zez~oXlE5%ve^TmBEt*x_X#fs(-En$jXr-R4sb$b~`nS=iOy|OVrph(U&cVS!IhmZ~ zKIRA9X%Wp1J=vTvHZ~SDe_JXOe9*fa zgEPf;gD^|qE=dl>Qkx3(80#SE7oxXQ(n4qQ#by{uppSKoDbaq`U+fRqk0BwI>IXV3 zD#K%ASkzd7u>@|pA=)Z>rQr@dLH}*r7r0ng zxa^eME+l*s7{5TNu!+bD{Pp@2)v%g6^>yj{XP&mShhg9GszNu4ITW=XCIUp2Xro&1 zg_D=J3r)6hp$8+94?D$Yn2@Kp-3LDsci)<-H!wCeQt$e9Jk)K86hvV^*Nj-Ea*o;G zsuhRw$H{$o>8qByz1V!(yV{p_0X?Kmy%g#1oSmlHsw;FQ%j9S#}ha zm0Nx09@jmOtP8Q+onN^BAgd8QI^(y!n;-APUpo5WVdmp8!`yKTlF>cqn>ag`4;o>i zl!M0G-(S*fm6VjYy}J}0nX7nJ$h`|b&KuW4d&W5IhbR;-)*9Y0(Jj|@j`$xoPQ=Cl literal 3276 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 N;OXk;vd$@?2>?>Ex^Vyi diff --git a/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png index bcbf36df2f2aaaa0a63c7dabc94e600184229d0d..bdb57226d5f2bd20f11934f4903f16459cf52379 100644 GIT binary patch literal 14142 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 diff --git a/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png index e71a726136a47ed24125c7efc79d68a4a01961b4..326c0e72c9d820600887813b3b98d0dd69c5d4e8 100644 GIT binary patch literal 36406 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 literal 14800 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 vNcLuCM3Rj?iBw@67X_p?_CF#jIyB-s + + + + diff --git a/packages/window_manager/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/window_manager/example/macos/Runner/Configs/AppInfo.xcconfig index c001f030..b4fe57b1 100644 --- a/packages/window_manager/example/macos/Runner/Configs/AppInfo.xcconfig +++ b/packages/window_manager/example/macos/Runner/Configs/AppInfo.xcconfig @@ -8,7 +8,7 @@ PRODUCT_NAME = window_manager_example // The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = org.leanflutter.plugins.windowManagerExample +PRODUCT_BUNDLE_IDENTIFIER = dev.leanflutter.examples.windowmanagerexample // The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2021 org.leanflutter.plugins. All rights reserved. +PRODUCT_COPYRIGHT = Copyright © 2022-present LiJianying. All rights reserved. diff --git a/packages/window_manager/example/macos/Runner/MainFlutterWindow.swift b/packages/window_manager/example/macos/Runner/MainFlutterWindow.swift index 872ba9cf..223c3bbe 100644 --- a/packages/window_manager/example/macos/Runner/MainFlutterWindow.swift +++ b/packages/window_manager/example/macos/Runner/MainFlutterWindow.swift @@ -1,18 +1,15 @@ import Cocoa import FlutterMacOS -import window_manager -class MainFlutterWindow: NSPanel { +class MainFlutterWindow: NSWindow { override func awakeFromNib() { - let flutterViewController = FlutterViewController.init() + let flutterViewController = FlutterViewController() let windowFrame = self.frame self.contentViewController = flutterViewController self.setFrame(windowFrame, display: true) RegisterGeneratedPlugins(registry: flutterViewController) - WindowManagerPlugin.RegisterGeneratedPlugins = RegisterGeneratedPlugins - super.awakeFromNib() } diff --git a/packages/window_manager/example/macos/RunnerTests/RunnerTests.swift b/packages/window_manager/example/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..631c3839 --- /dev/null +++ b/packages/window_manager/example/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,30 @@ +import Cocoa +import FlutterMacOS +import XCTest + +// If your plugin has been explicitly set to "type: .dynamic" in the Package.swift, +// you will need to add your plugin as a dependency of RunnerTests within Xcode. + +@testable import window_manager + +// This demonstrates a simple unit test of the Swift portion of this plugin's implementation. +// +// See https://developer.apple.com/documentation/xctest for more information about using XCTest. + +class RunnerTests: XCTestCase { + + func testGetPlatformVersion() { + let plugin = WindowManagerPlugin() + + let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: []) + + let resultExpectation = expectation(description: "result block must be called.") + plugin.handle(call) { result in + XCTAssertEqual(result as! String, + "macOS " + ProcessInfo.processInfo.operatingSystemVersionString) + resultExpectation.fulfill() + } + waitForExpectations(timeout: 1) + } + +} diff --git a/packages/window_manager/macos/window_manager.podspec b/packages/window_manager/macos/window_manager.podspec index 6e4ae058..faf6e103 100644 --- a/packages/window_manager/macos/window_manager.podspec +++ b/packages/window_manager/macos/window_manager.podspec @@ -4,16 +4,24 @@ # Pod::Spec.new do |s| s.name = 'window_manager' - s.version = '0.2.0' - s.summary = 'A new flutter plugin project.' + s.version = '0.5.0' + s.summary = 'A new Flutter plugin project.' s.description = <<-DESC -A new flutter plugin project. +A new Flutter plugin project. DESC s.homepage = 'https://leanflutter.dev' s.license = { :file => '../LICENSE' } s.author = { 'LiJianying' => 'lijy91@foxmail.com' } + s.source = { :path => '.' } - s.source_files = 'Classes/**/*' + s.source_files = 'window_manager/Sources/window_manager/**/*' + + # If your plugin requires a privacy manifest, for example if it collects user + # data, update the PrivacyInfo.xcprivacy file to describe your plugin's + # privacy impact, and then uncomment this line. For more information, + # see https://developer.apple.com/documentation/bundleresources/privacy_manifest_files + # s.resource_bundles = {'window_manager_privacy' => ['window_manager/Sources/window_manager/PrivacyInfo.xcprivacy']} + s.dependency 'FlutterMacOS' s.platform = :osx, '10.11' diff --git a/packages/window_manager/macos/window_manager/Package.swift b/packages/window_manager/macos/window_manager/Package.swift new file mode 100644 index 00000000..8d5f04d6 --- /dev/null +++ b/packages/window_manager/macos/window_manager/Package.swift @@ -0,0 +1,32 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "window_manager", + platforms: [ + .macOS("10.15") + ], + products: [ + .library(name: "window-manager", targets: ["window_manager"]) + ], + dependencies: [], + targets: [ + .target( + name: "window_manager", + dependencies: [], + resources: [ + // If your plugin requires a privacy manifest, for example if it collects user + // data, update the PrivacyInfo.xcprivacy file to describe your plugin's + // privacy impact, and then uncomment these lines. For more information, see + // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files + // .process("PrivacyInfo.xcprivacy"), + + // If you have other resources that need to be bundled with your plugin, refer to + // the following instructions to add them: + // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package + ] + ) + ] +) diff --git a/packages/window_manager/macos/window_manager/Sources/window_manager/PrivacyInfo.xcprivacy b/packages/window_manager/macos/window_manager/Sources/window_manager/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..918d80be --- /dev/null +++ b/packages/window_manager/macos/window_manager/Sources/window_manager/PrivacyInfo.xcprivacy @@ -0,0 +1,12 @@ + + + + + NSPrivacyTrackingDomains + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + + diff --git a/packages/window_manager/macos/Classes/WindowManager.swift b/packages/window_manager/macos/window_manager/Sources/window_manager/WindowManager.swift similarity index 100% rename from packages/window_manager/macos/Classes/WindowManager.swift rename to packages/window_manager/macos/window_manager/Sources/window_manager/WindowManager.swift diff --git a/packages/window_manager/macos/Classes/WindowManagerPlugin.swift b/packages/window_manager/macos/window_manager/Sources/window_manager/WindowManagerPlugin.swift similarity index 98% rename from packages/window_manager/macos/Classes/WindowManagerPlugin.swift rename to packages/window_manager/macos/window_manager/Sources/window_manager/WindowManagerPlugin.swift index da75f5d8..6257cb11 100644 --- a/packages/window_manager/macos/Classes/WindowManagerPlugin.swift +++ b/packages/window_manager/macos/window_manager/Sources/window_manager/WindowManagerPlugin.swift @@ -2,8 +2,6 @@ import Cocoa import FlutterMacOS public class WindowManagerPlugin: NSObject, FlutterPlugin { - public static var RegisterGeneratedPlugins:((FlutterPluginRegistry) -> Void)? - public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: "window_manager", binaryMessenger: registrar.messenger) let instance = WindowManagerPlugin(registrar, channel) diff --git a/packages/window_manager/pubspec.yaml b/packages/window_manager/pubspec.yaml index a881ece3..a9072149 100644 --- a/packages/window_manager/pubspec.yaml +++ b/packages/window_manager/pubspec.yaml @@ -1,7 +1,13 @@ name: window_manager description: This plugin allows Flutter desktop apps to resizing and repositioning the window. -version: 0.4.3 -homepage: https://github.com/leanflutter/window_manager +version: 0.5.0 +homepage: https://leanflutter.dev +repository: https://github.com/leanflutter/window_manager +issue_tracker: https://github.com/leanflutter/window_manager/issues +documentation: https://leanflutter.dev/documentation/window_manager/ +funding: + - https://github.com/lijy91 + - https://liberapay.com/lijy91 platforms: linux: From 56ae6e9ed008ee39558318827c4c7e2e3bf83cb2 Mon Sep 17 00:00:00 2001 From: LiJianying Date: Thu, 29 May 2025 20:13:02 +0800 Subject: [PATCH 47/50] Add getId method for retrieving window ID on macOS and Windows --- .../example/lib/pages/home.dart | 16 ++++++------ .../lib/src/window_manager.dart | 10 ++++++-- .../window_manager/WindowManager.swift | 4 +++ .../window_manager/WindowManagerPlugin.swift | 3 +++ .../windows/window_manager_plugin.cpp | 25 ++++++++----------- 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/packages/window_manager/example/lib/pages/home.dart b/packages/window_manager/example/lib/pages/home.dart index 42ba6eb1..b30a0c0c 100644 --- a/packages/window_manager/example/lib/pages/home.dart +++ b/packages/window_manager/example/lib/pages/home.dart @@ -141,6 +141,14 @@ class _HomePageState extends State with TrayListener, WindowListener { PreferenceListSection( title: const Text('METHODS'), children: [ + if (Platform.isWindows || Platform.isMacOS) + PreferenceListItem( + title: const Text('getId'), + onTap: () async { + final result = await windowManager.getId(); + BotToast.showText(text: 'Window ID:$result'); + }, + ), PreferenceListItem( title: const Text('setAsFrameless'), onTap: () async { @@ -180,14 +188,6 @@ class _HomePageState extends State with TrayListener, WindowListener { print('isFocused: ${await windowManager.isFocused()}'); }, ), - if(Platform.isWindows) - PreferenceListItem( - title: const Text('getWindowHandle'), - onTap: () async { - final result = await windowManager.getWindowHandle(); - print('HWND:$result'); - }, - ), PreferenceListItem( title: const Text('show / hide'), onTap: () async { diff --git a/packages/window_manager/lib/src/window_manager.dart b/packages/window_manager/lib/src/window_manager.dart index 2ef3e790..6d09ba65 100644 --- a/packages/window_manager/lib/src/window_manager.dart +++ b/packages/window_manager/lib/src/window_manager.dart @@ -104,8 +104,14 @@ class WindowManager { await _channel.invokeMethod('ensureInitialized'); } - Future getWindowHandle()async{ - return await _channel.invokeMethod('getWindowHandle') as int; + /// Returns `int` - The ID of the window. + /// + /// For macOS, the ID is the window number. + /// For Windows, the ID is the window handle. + /// + /// @platforms macos,windows + Future getId() async { + return await _channel.invokeMethod('getId') as int; } /// You can call this to remove the window frame (title bar, outline border, etc), which is basically everything except the Flutter view, also can call setTitleBarStyle(TitleBarStyle.normal) or setTitleBarStyle(TitleBarStyle.hidden) to restore it. diff --git a/packages/window_manager/macos/window_manager/Sources/window_manager/WindowManager.swift b/packages/window_manager/macos/window_manager/Sources/window_manager/WindowManager.swift index 50f6e39b..65fc4ce1 100644 --- a/packages/window_manager/macos/window_manager/Sources/window_manager/WindowManager.swift +++ b/packages/window_manager/macos/window_manager/Sources/window_manager/WindowManager.swift @@ -76,6 +76,10 @@ public class WindowManager: NSObject, NSWindowDelegate { public func waitUntilReadyToShow() { // nothing } + + public func getId() -> Int { + return mainWindow.windowNumber + } public func setAsFrameless() { mainWindow.styleMask.insert(.fullSizeContentView) diff --git a/packages/window_manager/macos/window_manager/Sources/window_manager/WindowManagerPlugin.swift b/packages/window_manager/macos/window_manager/Sources/window_manager/WindowManagerPlugin.swift index 6257cb11..4119861c 100644 --- a/packages/window_manager/macos/window_manager/Sources/window_manager/WindowManagerPlugin.swift +++ b/packages/window_manager/macos/window_manager/Sources/window_manager/WindowManagerPlugin.swift @@ -50,6 +50,9 @@ public class WindowManagerPlugin: NSObject, FlutterPlugin { windowManager.waitUntilReadyToShow() result(true) break + case "getId": + result(windowManager.getId()) + break case "setAsFrameless": windowManager.setAsFrameless() result(true) diff --git a/packages/window_manager/windows/window_manager_plugin.cpp b/packages/window_manager/windows/window_manager_plugin.cpp index 8a289b93..ba7720ae 100644 --- a/packages/window_manager/windows/window_manager_plugin.cpp +++ b/packages/window_manager/windows/window_manager_plugin.cpp @@ -41,11 +41,10 @@ class WindowManagerPlugin : public flutter::Plugin { private: std::unique_ptr< - flutter::MethodChannel, - std::default_delete>> + flutter::MethodChannel, + std::default_delete>> channel = nullptr; - WindowManager* window_manager; flutter::PluginRegistrarWindows* registrar; @@ -69,8 +68,8 @@ class WindowManagerPlugin : public flutter::Plugin { // HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); // Don't use `MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST)` above. - // Because if the window is restored from minimized state, the window is not in the correct monitor. - // The monitor is always the left-most monitor. + // Because if the window is restored from minimized state, the window is not + // in the correct monitor. The monitor is always the left-most monitor. // https://github.com/leanflutter/window_manager/issues/489 HMONITOR monitor = MonitorFromRect(&sz->rgrc[0], MONITOR_DEFAULTTONEAREST); if (monitor != NULL) { @@ -113,10 +112,9 @@ WindowManagerPlugin::WindowManagerPlugin( registrar->messenger(), "window_manager", &flutter::StandardMethodCodec::GetInstance()); - channel->SetMethodCallHandler( - [this](const auto& call, auto result) { - HandleMethodCall(call, std::move(result)); - }); + channel->SetMethodCallHandler([this](const auto& call, auto result) { + HandleMethodCall(call, std::move(result)); + }); } WindowManagerPlugin::~WindowManagerPlugin() { @@ -350,13 +348,12 @@ void WindowManagerPlugin::HandleMethodCall( window_manager->native_window = ::GetAncestor(registrar->GetView()->GetNativeWindow(), GA_ROOT); result->Success(flutter::EncodableValue(true)); - } - else if (method_name.compare("getWindowHandle") == 0) { - result->Success(flutter::EncodableValue(reinterpret_cast<__int64>(window_manager->GetMainWindow()))); - } - else if (method_name.compare("waitUntilReadyToShow") == 0) { + } else if (method_name.compare("waitUntilReadyToShow") == 0) { window_manager->WaitUntilReadyToShow(); result->Success(flutter::EncodableValue(true)); + } else if (method_name.compare("getId") == 0) { + result->Success(flutter::EncodableValue( + reinterpret_cast<__int64>(window_manager->GetMainWindow()))); } else if (method_name.compare("setAsFrameless") == 0) { window_manager->SetAsFrameless(); result->Success(flutter::EncodableValue(true)); From 3cf0c12ad4e58247074ce3e4ec1d171aeacd5887 Mon Sep 17 00:00:00 2001 From: LiJianying Date: Thu, 29 May 2025 20:34:26 +0800 Subject: [PATCH 48/50] v0.5.0 --- .github/workflows/build.yml | 26 +++++++------- .github/workflows/test.yml | 2 +- docs/en/quick-start.md | 2 +- docs/zh/quick-start.md | 2 +- melos.yaml | 4 +-- packages/window_manager/CHANGELOG.md | 9 +++++ .../example/lib/pages/home.dart | 10 +++--- .../lib/src/widgets/virtual_window_frame.dart | 2 +- .../lib/src/widgets/window_caption.dart | 2 +- .../src/widgets/window_caption_button.dart | 34 +++++++++---------- .../lib/src/window_manager.dart | 8 ++--- 11 files changed, 55 insertions(+), 46 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 967f5ec3..65a67ae1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: - run: | sudo apt-get update sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev - sudo apt-get install -y ayatana-appindicator3-0.1 + sudo apt-get install -y libayatana-appindicator3-dev - uses: bluefireteam/melos-action@v3 - working-directory: ./packages/window_manager/example run: | @@ -37,18 +37,18 @@ jobs: run: | flutter build macos --release - build-web: - runs-on: macos-latest - steps: - - uses: actions/checkout@v3 - - uses: subosito/flutter-action@v2 - with: - flutter-version: "3.32.0" - channel: "stable" - - uses: bluefireteam/melos-action@v3 - - working-directory: ./packages/window_manager/example - run: | - flutter build web --release + # build-web: + # runs-on: macos-latest + # steps: + # - uses: actions/checkout@v3 + # - uses: subosito/flutter-action@v2 + # with: + # flutter-version: "3.32.0" + # channel: "stable" + # - uses: bluefireteam/melos-action@v3 + # - working-directory: ./packages/window_manager/example + # run: | + # flutter build web --release build-windows: runs-on: windows-latest diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ad6b197e..ed9c7032 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: flutter-version: "3.32.0" - run: | sudo apt-get update -y - sudo apt-get install -y ninja-build libgtk-3-dev ayatana-appindicator3-0.1 xvfb + sudo apt-get install -y ninja-build libgtk-3-dev libayatana-appindicator3-dev xvfb - uses: bluefireteam/melos-action@v3 - working-directory: ./packages/window_manager/example run: xvfb-run -a flutter test integration_test -v diff --git a/docs/en/quick-start.md b/docs/en/quick-start.md index 24cc6584..10d380d1 100644 --- a/docs/en/quick-start.md +++ b/docs/en/quick-start.md @@ -8,7 +8,7 @@ Add this to your package's `pubspec.yaml` file: ```yaml dependencies: - window_manager: ^0.4.2 + window_manager: ^0.5.0 ``` Or diff --git a/docs/zh/quick-start.md b/docs/zh/quick-start.md index 8c808032..907ce24a 100644 --- a/docs/zh/quick-start.md +++ b/docs/zh/quick-start.md @@ -8,7 +8,7 @@ ```yaml dependencies: - window_manager: ^0.4.2 + window_manager: ^0.5.0 ``` 或者 diff --git a/melos.yaml b/melos.yaml index 0c8036c9..a03260d9 100644 --- a/melos.yaml +++ b/melos.yaml @@ -23,11 +23,11 @@ scripts: - test format: - exec: dart format . --fix + exec: dart format . description: Run `dart format` for all packages. format-check: - exec: dart format . --fix --set-exit-if-changed + exec: dart format . --set-exit-if-changed description: Run `dart format` checks for all packages. fix: diff --git a/packages/window_manager/CHANGELOG.md b/packages/window_manager/CHANGELOG.md index a101a0e8..27085ee4 100644 --- a/packages/window_manager/CHANGELOG.md +++ b/packages/window_manager/CHANGELOG.md @@ -1,3 +1,12 @@ +### 0.5.0 + +* feat: Add `getId` method for retrieving window ID on macOS and Windows +* feat: Add `getWindowHandle` method (#548) +* feat: Add Swift Package Manager support +* fix: Crash when using window_manager by multi engine on Windows platform (#546) +* fix: [Windows] Use frameless window to implement fullscreen (#531) +* fix: Initialize window_hints to fix minimum size setting in release mode (#510) + ### 0.4.3 * [windows] fix: scale ratio on dpi change (#496) diff --git a/packages/window_manager/example/lib/pages/home.dart b/packages/window_manager/example/lib/pages/home.dart index b30a0c0c..4ceb1452 100644 --- a/packages/window_manager/example/lib/pages/home.dart +++ b/packages/window_manager/example/lib/pages/home.dart @@ -928,10 +928,10 @@ class _HomePageState extends State with TrayListener, WindowListener { margin: const EdgeInsets.all(0), decoration: const BoxDecoration( color: Colors.white, - // border: Border.all(color: Colors.grey.withOpacity(0.4), width: 1), + // border: Border.all(color: Colors.grey.withValues(alpha: 0.4), width: 1), // boxShadow: [ // BoxShadow( - // color: Colors.black.withOpacity(0.2), + // color: Colors.black.withValues(alpha: 0.2), // offset: Offset(1.0, 1.0), // blurRadius: 6.0, // ), @@ -966,7 +966,7 @@ class _HomePageState extends State with TrayListener, WindowListener { margin: const EdgeInsets.all(0), width: double.infinity, height: 54, - color: Colors.grey.withOpacity(0.3), + color: Colors.grey.withValues(alpha: 0.3), child: const Center( child: Text('DragToMoveArea'), ), @@ -978,11 +978,11 @@ class _HomePageState extends State with TrayListener, WindowListener { margin: const EdgeInsets.all(20), child: DragToResizeArea( resizeEdgeSize: 6, - resizeEdgeColor: Colors.red.withOpacity(0.2), + resizeEdgeColor: Colors.red.withValues(alpha: 0.2), child: Container( width: double.infinity, height: double.infinity, - color: Colors.grey.withOpacity(0.3), + color: Colors.grey.withValues(alpha: 0.3), child: Center( child: GestureDetector( child: const Text('DragToResizeArea'), diff --git a/packages/window_manager/lib/src/widgets/virtual_window_frame.dart b/packages/window_manager/lib/src/widgets/virtual_window_frame.dart index 2cac2f7e..98a2c235 100644 --- a/packages/window_manager/lib/src/widgets/virtual_window_frame.dart +++ b/packages/window_manager/lib/src/widgets/virtual_window_frame.dart @@ -55,7 +55,7 @@ class _VirtualWindowFrameState extends State boxShadow: [ if (!_isMaximized && !_isFullScreen) BoxShadow( - color: Colors.black.withOpacity(0.1), + color: Colors.black.withValues(alpha: 0.1), offset: Offset(0.0, _isFocused ? 4 : 2), blurRadius: 6, ), diff --git a/packages/window_manager/lib/src/widgets/window_caption.dart b/packages/window_manager/lib/src/widgets/window_caption.dart index 101d4476..a14779c7 100644 --- a/packages/window_manager/lib/src/widgets/window_caption.dart +++ b/packages/window_manager/lib/src/widgets/window_caption.dart @@ -74,7 +74,7 @@ class _WindowCaptionState extends State with WindowListener { child: DefaultTextStyle( style: TextStyle( color: widget.brightness == Brightness.light - ? Colors.black.withOpacity(0.8956) + ? Colors.black.withValues(alpha: 0.8956) : Colors.white, fontSize: 14, ), diff --git a/packages/window_manager/lib/src/widgets/window_caption_button.dart b/packages/window_manager/lib/src/widgets/window_caption_button.dart index 7ce99e5c..86930cc1 100644 --- a/packages/window_manager/lib/src/widgets/window_caption_button.dart +++ b/packages/window_manager/lib/src/widgets/window_caption_button.dart @@ -314,24 +314,24 @@ class WindowCaptionButton extends StatefulWidget { _lightButtonBgColorScheme = _ButtonBgColorScheme( normal: Colors.transparent, hovered: const Color(0xffC42B1C), - pressed: const Color(0xffC42B1C).withOpacity(0.9), + pressed: const Color(0xffC42B1C).withValues(alpha: 0.9), ), _lightButtonIconColorScheme = _ButtonIconColorScheme( - normal: Colors.black.withOpacity(0.8956), + normal: Colors.black.withValues(alpha: 0.8956), hovered: Colors.white, - pressed: Colors.white.withOpacity(0.7), - disabled: Colors.black.withOpacity(0.3614), + pressed: Colors.white.withValues(alpha: 0.7), + disabled: Colors.black.withValues(alpha: 0.3614), ), _darkButtonBgColorScheme = _ButtonBgColorScheme( normal: Colors.transparent, hovered: const Color(0xffC42B1C), - pressed: const Color(0xffC42B1C).withOpacity(0.9), + pressed: const Color(0xffC42B1C).withValues(alpha: 0.9), ), _darkButtonIconColorScheme = _ButtonIconColorScheme( normal: Colors.white, hovered: Colors.white, - pressed: Colors.white.withOpacity(0.786), - disabled: Colors.black.withOpacity(0.3628), + pressed: Colors.white.withValues(alpha: 0.786), + disabled: Colors.black.withValues(alpha: 0.3628), ); final Brightness? brightness; @@ -341,25 +341,25 @@ class WindowCaptionButton extends StatefulWidget { _ButtonBgColorScheme _lightButtonBgColorScheme = _ButtonBgColorScheme( normal: Colors.transparent, - hovered: Colors.black.withOpacity(0.0373), - pressed: Colors.black.withOpacity(0.0241), + hovered: Colors.black.withValues(alpha: 0.0373), + pressed: Colors.black.withValues(alpha: 0.0241), ); _ButtonIconColorScheme _lightButtonIconColorScheme = _ButtonIconColorScheme( - normal: Colors.black.withOpacity(0.8956), - hovered: Colors.black.withOpacity(0.8956), - pressed: Colors.black.withOpacity(0.6063), - disabled: Colors.black.withOpacity(0.3614), + normal: Colors.black.withValues(alpha: 0.8956), + hovered: Colors.black.withValues(alpha: 0.8956), + pressed: Colors.black.withValues(alpha: 0.6063), + disabled: Colors.black.withValues(alpha: 0.3614), ); _ButtonBgColorScheme _darkButtonBgColorScheme = _ButtonBgColorScheme( normal: Colors.transparent, - hovered: Colors.white.withOpacity(0.0605), - pressed: Colors.white.withOpacity(0.0419), + hovered: Colors.white.withValues(alpha: 0.0605), + pressed: Colors.white.withValues(alpha: 0.0419), ); _ButtonIconColorScheme _darkButtonIconColorScheme = _ButtonIconColorScheme( normal: Colors.white, hovered: Colors.white, - pressed: Colors.white.withOpacity(0.786), - disabled: Colors.black.withOpacity(0.3628), + pressed: Colors.white.withValues(alpha: 0.786), + disabled: Colors.black.withValues(alpha: 0.3628), ); _ButtonBgColorScheme get buttonBgColorScheme => brightness != Brightness.dark diff --git a/packages/window_manager/lib/src/window_manager.dart b/packages/window_manager/lib/src/window_manager.dart index 6d09ba65..5f66c914 100644 --- a/packages/window_manager/lib/src/window_manager.dart +++ b/packages/window_manager/lib/src/window_manager.dart @@ -328,10 +328,10 @@ class WindowManager { /// Sets the background color of the window. Future setBackgroundColor(Color backgroundColor) async { final Map arguments = { - 'backgroundColorA': backgroundColor.alpha, - 'backgroundColorR': backgroundColor.red, - 'backgroundColorG': backgroundColor.green, - 'backgroundColorB': backgroundColor.blue, + 'backgroundColorA': (backgroundColor.a * 255).round() & 0xff, + 'backgroundColorR': (backgroundColor.r * 255).round() & 0xff, + 'backgroundColorG': (backgroundColor.g * 255).round() & 0xff, + 'backgroundColorB': (backgroundColor.b * 255).round() & 0xff, }; await _channel.invokeMethod('setBackgroundColor', arguments); } From 625d44d2746bfbf1e843321b066d8e9f77951e05 Mon Sep 17 00:00:00 2001 From: LiJianying Date: Sat, 12 Jul 2025 15:12:14 +0800 Subject: [PATCH 49/50] fix: Fix PrivacyInfo.xcprivacy warning for macOS Desktop on Mac M1 macOS 15 (Sequoia) (#550) --- README-ZH.md | 2 +- README.md | 2 +- packages/window_manager/CHANGELOG.md | 4 ++++ .../Sources/window_manager/PrivacyInfo.xcprivacy | 12 ------------ packages/window_manager/pubspec.yaml | 2 +- 5 files changed, 7 insertions(+), 15 deletions(-) delete mode 100644 packages/window_manager/macos/window_manager/Sources/window_manager/PrivacyInfo.xcprivacy diff --git a/README-ZH.md b/README-ZH.md index 2e9551d8..b7118a87 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -57,7 +57,7 @@ ```yaml dependencies: - window_manager: ^0.5.0 + window_manager: ^0.5.1 ``` ### 用法 diff --git a/README.md b/README.md index 05dfbcba..e8efc4d7 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Add this to your package's `pubspec.yaml` file: ```yaml dependencies: - window_manager: ^0.5.0 + window_manager: ^0.5.1 ``` ### Usage diff --git a/packages/window_manager/CHANGELOG.md b/packages/window_manager/CHANGELOG.md index 27085ee4..cd375fe5 100644 --- a/packages/window_manager/CHANGELOG.md +++ b/packages/window_manager/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.1 + +* fix: Fix PrivacyInfo.xcprivacy warning for macOS Desktop on Mac M1 macOS 15 (Sequoia) (#550) + ### 0.5.0 * feat: Add `getId` method for retrieving window ID on macOS and Windows diff --git a/packages/window_manager/macos/window_manager/Sources/window_manager/PrivacyInfo.xcprivacy b/packages/window_manager/macos/window_manager/Sources/window_manager/PrivacyInfo.xcprivacy deleted file mode 100644 index 918d80be..00000000 --- a/packages/window_manager/macos/window_manager/Sources/window_manager/PrivacyInfo.xcprivacy +++ /dev/null @@ -1,12 +0,0 @@ - - - - - NSPrivacyTrackingDomains - - NSPrivacyCollectedDataTypes - - NSPrivacyTracking - - - diff --git a/packages/window_manager/pubspec.yaml b/packages/window_manager/pubspec.yaml index a9072149..740665e3 100644 --- a/packages/window_manager/pubspec.yaml +++ b/packages/window_manager/pubspec.yaml @@ -1,6 +1,6 @@ name: window_manager description: This plugin allows Flutter desktop apps to resizing and repositioning the window. -version: 0.5.0 +version: 0.5.1 homepage: https://leanflutter.dev repository: https://github.com/leanflutter/window_manager issue_tracker: https://github.com/leanflutter/window_manager/issues From 0d0364417a1b1915af1be037fc692fe93a47497c Mon Sep 17 00:00:00 2001 From: LiJianying Date: Sat, 1 Nov 2025 21:52:12 +0800 Subject: [PATCH 50/50] Update migration notice in README files Revised both English and Chinese README files to announce migration to libnativeapi/nativeapi-flutter, referencing the new unified C++ core library for improved cross-platform native API support. Removed outdated migration notes and updated links accordingly. --- README-ZH.md | 6 +++--- README.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README-ZH.md b/README-ZH.md index b7118a87..57458d77 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -1,4 +1,6 @@ -> **🚀 快速发布您的应用**: 试试 [Fastforge](https://fastforge.dev) - 构建、打包和分发您的 Flutter 应用最简单的方式。 +> **⚠️ 迁移通知**: 本插件正在迁移到 [libnativeapi/nativeapi-flutter](https://github.com/libnativeapi/nativeapi-flutter) +> +> 新版本基于统一的 C++ 核心库([libnativeapi/nativeapi](https://github.com/libnativeapi/nativeapi)),提供更完整、一致的跨平台原生 API 支持。 # window_manager @@ -14,8 +16,6 @@ 这个插件为 Flutter 桌面应用程序提供了全面的窗口管理功能,使开发者能够完全控制窗口大小、位置、外观、关闭行为,以及监听事件。 -> 注意:本插件计划迁移至 [nativeapi](https://github.com/leanflutter/nativeapi-flutter) 以提升可维护性和性能,但目前该方案仍处于实验阶段。 - --- [English](./README.md) | 简体中文 diff --git a/README.md b/README.md index e8efc4d7..43044d57 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -> **🚀 Ship Your App Faster**: Try [Fastforge](https://fastforge.dev) - The simplest way to build, package and distribute your Flutter apps. +> **⚠️ Migration Notice**: This plugin is being migrated to [libnativeapi/nativeapi-flutter](https://github.com/libnativeapi/nativeapi-flutter) +> +> The new version is based on a unified C++ core library ([libnativeapi/nativeapi](https://github.com/libnativeapi/nativeapi)), providing more complete and consistent cross-platform native API support. # window_manager @@ -14,8 +16,6 @@ This plugin provides comprehensive window management capabilities for Flutter desktop applications, enabling full control over window size, position, appearance, close behavior, and listening to events. -> Note: This plugin is planned to migrate to [nativeapi](https://github.com/leanflutter/nativeapi-flutter) to improve maintainability and performance, but the solution is still experimental at this stage. - --- English | [简体中文](./README-ZH.md)