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 024228dd..65a67ae1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,35 +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: + flutter-version: "3.32.0" + 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 libayatana-appindicator3-dev + - 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 - - 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 + with: + flutter-version: "3.32.0" + 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.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 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2 - - run: cd example && flutter build windows -v - - run: cd example && flutter test integration_test -v + with: + flutter-version: "3.32.0" + channel: "stable" + - uses: bluefireteam/melos-action@v3 + - working-directory: ./packages/window_manager/example + run: | + flutter build windows --release diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index db14caed..a1ddda09 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.32.0" 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.32.0" 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 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..ed9c7032 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,46 @@ +name: test + +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.32.0" + - run: | + sudo apt-get update -y + 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 + integration_test-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - uses: subosito/flutter-action@v2 + with: + channel: stable + 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 + # 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.32.0" + - uses: bluefireteam/melos-action@v3 + - working-directory: ./packages/window_manager/example + run: flutter test integration_test -v 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..2fdca2eb 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 @@ -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/README-ZH.md b/README-ZH.md index 5ef721c3..57458d77 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -1,16 +1,20 @@ +> **⚠️ 迁移通知**: 本插件正在迁移到 [libnativeapi/nativeapi-flutter](https://github.com/libnativeapi/nativeapi-flutter) +> +> 新版本基于统一的 C++ 核心库([libnativeapi/nativeapi](https://github.com/libnativeapi/nativeapi)),提供更完整、一致的跨平台原生 API 支持。 + # 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 桌面应用调整窗口的大小和位置。 +这个插件为 Flutter 桌面应用程序提供了全面的窗口管理功能,使开发者能够完全控制窗口大小、位置、外观、关闭行为,以及监听事件。 --- @@ -22,110 +26,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) @@ -135,7 +41,13 @@ | Linux | macOS | Windows | | :---: | :---: | :-----: | -| ✔️ | ✔️ | ✔️ | +| ✔️ | ✔️ | ✔️ | + +## 文档 + +- [快速开始](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) ## 快速开始 @@ -145,17 +57,7 @@ ```yaml dependencies: - window_manager: ^0.3.7 -``` - -或 - -```yaml -dependencies: - window_manager: - git: - url: https://github.com/leanflutter/window_manager.git - ref: main + window_manager: ^0.5.1 ``` ### 用法 @@ -188,733 +90,23 @@ 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.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) ## 谁在用使用它? -- [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. - [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. - -## 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. - - - +- [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 e2a4a4f0..43044d57 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,20 @@ +> **⚠️ 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 -[![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] [![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 - -[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. --- @@ -23,110 +26,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) @@ -136,7 +41,13 @@ English | [简体中文](./README-ZH.md) | Linux | macOS | Windows | | :---: | :---: | :-----: | -| ✔️ | ✔️ | ✔️ | +| ✔️ | ✔️ | ✔️ | + +## 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 @@ -146,17 +57,7 @@ Add this to your package's `pubspec.yaml` file: ```yaml dependencies: - window_manager: ^0.3.7 -``` - -Or - -```yaml -dependencies: - window_manager: - git: - url: https://github.com/leanflutter/window_manager.git - ref: main + window_manager: ^0.5.1 ``` ### Usage @@ -189,733 +90,23 @@ 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.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/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? +- [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 - [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. - -## 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. - - - +- [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/analysis_options.yaml b/analysis_options.yaml deleted file mode 100644 index ef8a58c1..00000000 --- a/analysis_options.yaml +++ /dev/null @@ -1,22 +0,0 @@ -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 diff --git a/docs/en/index.md b/docs/en/index.md new file mode 100644 index 00000000..a965a905 --- /dev/null +++ b/docs/en/index.md @@ -0,0 +1,49 @@ +# 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..10d380d1 --- /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.5.0 +``` + +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..ce9fa552 --- /dev/null +++ b/docs/zh/index.md @@ -0,0 +1,49 @@ +# 介绍 + +`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..907ce24a --- /dev/null +++ b/docs/zh/quick-start.md @@ -0,0 +1,376 @@ +# 快速开始 + +按照以下步骤快速开始使用 `window_manager` 插件: + +## 安装 + +将以下内容添加到您的软件包的 `pubspec.yaml` 文件中: + +```yaml +dependencies: + window_manager: ^0.5.0 +``` + +或者 + +```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/) diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml deleted file mode 100644 index 61b6c4de..00000000 --- a/example/analysis_options.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# 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 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/macos/Podfile.lock b/example/macos/Podfile.lock deleted file mode 100644 index 853b7ed8..00000000 --- a/example/macos/Podfile.lock +++ /dev/null @@ -1,34 +0,0 @@ -PODS: - - FlutterMacOS (1.0.0) - - screen_retriever (0.0.1): - - FlutterMacOS - - tray_manager (0.0.1): - - FlutterMacOS - - window_manager (0.2.0): - - FlutterMacOS - -DEPENDENCIES: - - FlutterMacOS (from `Flutter/ephemeral`) - - screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/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 - 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: 59634572a57080243dd1bf715e55b6c54f241a38 - tray_manager: 9064e219c56d75c476e46b9a21182087930baf90 - window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 - -PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 - -COCOAPODS: 1.11.3 diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index 3c4935a7..00000000 Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index ed4cc164..00000000 Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png deleted file mode 100644 index 483be613..00000000 Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png deleted file mode 100644 index bcbf36df..00000000 Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png deleted file mode 100644 index 9c0a6528..00000000 Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png deleted file mode 100644 index e71a7261..00000000 Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png deleted file mode 100644 index 8a31fe2d..00000000 Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png and /dev/null differ diff --git a/example/pubspec.lock b/example/pubspec.lock deleted file mode 100644 index 0bae7635..00000000 --- a/example/pubspec.lock +++ /dev/null @@ -1,307 +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: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" - url: "https://pub.dev" - source: hosted - version: "1.17.1" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d - url: "https://pub.dev" - source: hosted - version: "1.0.6" - 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: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" - url: "https://pub.dev" - source: hosted - version: "6.1.4" - 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_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 - 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" - 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" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - matcher: - dependency: transitive - description: - name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" - url: "https://pub.dev" - source: hosted - version: "0.12.15" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 - url: "https://pub.dev" - source: hosted - version: "0.2.0" - 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: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - path: - dependency: transitive - description: - name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" - url: "https://pub.dev" - source: hosted - version: "1.8.3" - platform: - dependency: transitive - description: - name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" - url: "https://pub.dev" - source: hosted - version: "3.1.0" - preference_list: - dependency: "direct main" - description: - name: preference_list - sha256: d3419b2ec57a6ad2156bd682a46cf3194bf162b36bb013790c7b29e6438f107a - url: "https://pub.dev" - source: hosted - version: "0.0.1" - process: - dependency: transitive - description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" - url: "https://pub.dev" - source: hosted - version: "4.2.4" - 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: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 - url: "https://pub.dev" - source: hosted - version: "1.9.1" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 - url: "https://pub.dev" - source: hosted - version: "1.11.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - 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: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb - url: "https://pub.dev" - source: hosted - version: "0.5.1" - tray_manager: - dependency: "direct main" - description: - name: tray_manager - sha256: b1975a05e0c6999e983cf9a58a6a098318c896040ccebac5398a3cc9e43b9c69 - url: "https://pub.dev" - source: hosted - version: "0.2.0" - 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: f6deed8ed625c52864792459709183da231ebf66ff0cf09e69b573227c377efe - url: "https://pub.dev" - source: hosted - version: "11.3.0" - webdriver: - dependency: transitive - description: - name: webdriver - sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - window_manager: - dependency: "direct main" - description: - path: ".." - relative: true - source: path - version: "0.3.7" -sdks: - dart: ">=3.0.0 <4.0.0" - flutter: ">=3.3.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml deleted file mode 100644 index df4ab78d..00000000 --- a/example/pubspec.yaml +++ /dev/null @@ -1,83 +0,0 @@ -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 - -environment: - sdk: ">=2.18.0 <4.0.0" - -dependencies: - 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 - -dev_dependencies: - flutter_test: - sdk: flutter - integration_test: - sdk: flutter - - # 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/images/ic_chrome_close.png b/images/ic_chrome_close.png deleted file mode 100644 index 1949491f..00000000 Binary files a/images/ic_chrome_close.png and /dev/null differ diff --git a/images/ic_chrome_maximize.png b/images/ic_chrome_maximize.png deleted file mode 100644 index 672e9e49..00000000 Binary files a/images/ic_chrome_maximize.png and /dev/null differ diff --git a/images/ic_chrome_minimize.png b/images/ic_chrome_minimize.png deleted file mode 100644 index 05534b43..00000000 Binary files a/images/ic_chrome_minimize.png and /dev/null differ diff --git a/images/ic_chrome_unmaximize.png b/images/ic_chrome_unmaximize.png deleted file mode 100644 index 52b8f4ed..00000000 Binary files a/images/ic_chrome_unmaximize.png and /dev/null differ diff --git a/lib/src/widgets/window_caption_button.dart b/lib/src/widgets/window_caption_button.dart deleted file mode 100644 index 82165f62..00000000 --- a/lib/src/widgets/window_caption_button.dart +++ /dev/null @@ -1,207 +0,0 @@ -// ignore_for_file: library_private_types_in_public_api - -import 'package:flutter/material.dart'; - -class WindowCaptionButtonIcon extends StatelessWidget { - const WindowCaptionButtonIcon({ - Key? key, - required this.name, - this.color, - this.package = 'window_manager', - }) : super(key: key); - - final String name; - final Color? color; - final String package; - - @override - Widget build(BuildContext context) { - return Image.asset( - name, - package: package, - width: 15, - color: color, - filterQuality: FilterQuality.high, - ); - } -} - -// ignore: must_be_immutable -class WindowCaptionButton extends StatefulWidget { - WindowCaptionButton({ - Key? key, - this.brightness, - this.icon, - this.iconName, - required this.onPressed, - }) : super(key: key); - WindowCaptionButton.close({ - Key? key, - this.brightness, - this.icon, - this.onPressed, - }) : iconName = 'images/ic_chrome_close.png', - _lightButtonBgColorScheme = _ButtonBgColorScheme( - normal: Colors.transparent, - hovered: const Color(0xffC42B1C), - pressed: const Color(0xffC42B1C).withOpacity(0.9), - ), - _lightButtonIconColorScheme = _ButtonIconColorScheme( - normal: Colors.black.withOpacity(0.8956), - hovered: Colors.white, - pressed: Colors.white.withOpacity(0.7), - disabled: Colors.black.withOpacity(0.3614), - ), - _darkButtonBgColorScheme = _ButtonBgColorScheme( - normal: Colors.transparent, - hovered: const Color(0xffC42B1C), - pressed: const Color(0xffC42B1C).withOpacity(0.9), - ), - _darkButtonIconColorScheme = _ButtonIconColorScheme( - normal: Colors.white, - hovered: Colors.white, - pressed: Colors.white.withOpacity(0.786), - disabled: Colors.black.withOpacity(0.3628), - ), - super(key: key); - - WindowCaptionButton.unmaximize({ - Key? key, - this.brightness, - this.icon, - this.onPressed, - }) : iconName = 'images/ic_chrome_unmaximize.png', - super(key: key); - - WindowCaptionButton.maximize({ - Key? key, - this.brightness, - this.icon, - this.onPressed, - }) : iconName = 'images/ic_chrome_maximize.png', - super(key: key); - - WindowCaptionButton.minimize({ - Key? key, - this.brightness, - this.icon, - this.onPressed, - }) : iconName = 'images/ic_chrome_minimize.png', - super(key: key); - - final Brightness? brightness; - final Widget? icon; - final String? iconName; - final VoidCallback? onPressed; - - _ButtonBgColorScheme _lightButtonBgColorScheme = _ButtonBgColorScheme( - normal: Colors.transparent, - hovered: Colors.black.withOpacity(0.0373), - pressed: Colors.black.withOpacity(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), - ); - _ButtonBgColorScheme _darkButtonBgColorScheme = _ButtonBgColorScheme( - normal: Colors.transparent, - hovered: Colors.white.withOpacity(0.0605), - pressed: Colors.white.withOpacity(0.0419), - ); - _ButtonIconColorScheme _darkButtonIconColorScheme = _ButtonIconColorScheme( - normal: Colors.white, - hovered: Colors.white, - pressed: Colors.white.withOpacity(0.786), - disabled: Colors.black.withOpacity(0.3628), - ); - - _ButtonBgColorScheme get buttonBgColorScheme => brightness != Brightness.dark - ? _lightButtonBgColorScheme - : _darkButtonBgColorScheme; - - _ButtonIconColorScheme get buttonIconColorScheme => - brightness != Brightness.dark - ? _lightButtonIconColorScheme - : _darkButtonIconColorScheme; - - @override - State createState() => _WindowCaptionButtonState(); -} - -class _WindowCaptionButtonState extends State { - bool _isHovering = false; - bool _isPressed = false; - - void _onEntered({required bool hovered}) { - setState(() => _isHovering = hovered); - } - - void _onActive({required bool pressed}) { - setState(() => _isPressed = pressed); - } - - @override - Widget build(BuildContext context) { - Color bgColor = widget.buttonBgColorScheme.normal; - Color iconColor = widget.buttonIconColorScheme.normal; - - if (_isHovering) { - bgColor = widget.buttonBgColorScheme.hovered; - iconColor = widget.buttonIconColorScheme.hovered; - } - if (_isPressed) { - bgColor = widget.buttonBgColorScheme.pressed; - iconColor = widget.buttonIconColorScheme.pressed; - } - - return MouseRegion( - onExit: (value) => _onEntered(hovered: false), - onHover: (value) => _onEntered(hovered: true), - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTapDown: (_) => _onActive(pressed: true), - onTapCancel: () => _onActive(pressed: false), - onTapUp: (_) => _onActive(pressed: false), - onTap: widget.onPressed, - child: Container( - constraints: const BoxConstraints(minWidth: 46, minHeight: 32), - decoration: BoxDecoration( - color: bgColor, - ), - child: Center( - child: WindowCaptionButtonIcon( - name: widget.iconName!, - color: iconColor, - ), - ), - ), - ), - ); - } -} - -class _ButtonBgColorScheme { - _ButtonBgColorScheme({ - required this.normal, - required this.hovered, - required this.pressed, - }); - final Color normal; - final Color hovered; - final Color pressed; -} - -class _ButtonIconColorScheme { - _ButtonIconColorScheme({ - required this.normal, - required this.hovered, - required this.pressed, - required this.disabled, - }); - final Color normal; - final Color hovered; - final Color pressed; - final Color disabled; -} diff --git a/macos/window_manager.podspec b/macos/window_manager.podspec deleted file mode 100644 index fdaa7def..00000000 --- a/macos/window_manager.podspec +++ /dev/null @@ -1,22 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint window_manager.podspec` to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'window_manager' - s.version = '0.2.0' - s.summary = 'A new flutter plugin project.' - s.description = <<-DESC -A new flutter plugin project. - DESC - s.homepage = 'https://leanflutter.org' - s.license = { :file => '../LICENSE' } - s.author = { 'LiJianying' => 'lijy91@foxmail.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/melos.yaml b/melos.yaml new file mode 100644 index 00000000..a03260d9 --- /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 . + description: Run `dart format` for all packages. + + format-check: + exec: dart format . --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..e7d347d9 --- /dev/null +++ b/packages/window_manager/.gitignore @@ -0,0 +1,33 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +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/ +.flutter-plugins +.flutter-plugins-dependencies +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 84% rename from CHANGELOG.md rename to packages/window_manager/CHANGELOG.md index a3f63bcf..cd375fe5 100644 --- a/CHANGELOG.md +++ b/packages/window_manager/CHANGELOG.md @@ -1,3 +1,44 @@ +## 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 +* 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) +* [windows] fix: get window monitor from minimized state (#495) + +### 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) +* 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 +* 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. +* [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/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/packages/window_manager/analysis_options.yaml b/packages/window_manager/analysis_options.yaml new file mode 100644 index 00000000..9033bb29 --- /dev/null +++ b/packages/window_manager/analysis_options.yaml @@ -0,0 +1 @@ +include: package:mostly_reasonable_lints/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 91% rename from example/.gitignore rename to packages/window_manager/example/.gitignore index 0fa6b675..79c113f9 100644 --- a/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/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/packages/window_manager/example/analysis_options.yaml b/packages/window_manager/example/analysis_options.yaml new file mode 100644 index 00000000..9033bb29 --- /dev/null +++ b/packages/window_manager/example/analysis_options.yaml @@ -0,0 +1 @@ +include: package:mostly_reasonable_lints/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 70% rename from example/integration_test/window_manager_test.dart rename to packages/window_manager/example/integration_test/window_manager_test.dart index 0dbade0e..5901027e 100644 --- a/example/integration_test/window_manager_test.dart +++ b/packages/window_manager/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/packages/window_manager/example/lib/main.dart similarity index 89% rename from example/lib/main.dart rename to packages/window_manager/example/lib/main.dart index 1f18072b..a19c042f 100644 --- a/example/lib/main.dart +++ b/packages/window_manager/example/lib/main.dart @@ -1,10 +1,8 @@ -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/utils/config.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -27,7 +25,7 @@ void main() async { } class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); + const MyApp({super.key}); @override State createState() => _MyAppState(); @@ -60,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/packages/window_manager/example/lib/pages/home.dart similarity index 97% rename from example/lib/pages/home.dart rename to packages/window_manager/example/lib/pages/home.dart index 75c3c1e8..4ceb1452 100644 --- a/example/lib/pages/home.dart +++ b/packages/window_manager/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/utils/config.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'; @@ -134,6 +133,7 @@ class _HomePageState extends State with TrayListener, WindowListener { ? Brightness.light : Brightness.dark, ); + setState(() {}); }, ), ], @@ -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 { @@ -920,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, // ), @@ -958,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'), ), @@ -970,17 +978,18 @@ 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'), onTap: () { BotToast.showText( - text: 'DragToResizeArea example'); + text: 'DragToResizeArea example', + ); }, ), ), @@ -1026,7 +1035,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/packages/window_manager/example/lib/utils/config.dart similarity index 88% rename from example/lib/utilities/config.dart rename to packages/window_manager/example/lib/utils/config.dart index b7014219..632f01b9 100644 --- a/example/lib/utilities/config.dart +++ b/packages/window_manager/example/lib/utils/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/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 91% rename from example/macos/.gitignore rename to packages/window_manager/example/macos/.gitignore index d2fd3772..746adbb6 100644 --- a/example/macos/.gitignore +++ b/packages/window_manager/example/macos/.gitignore @@ -3,4 +3,5 @@ **/Pods/ # Xcode-related +**/dgph **/xcuserdata/ 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 95% rename from example/macos/Podfile rename to packages/window_manager/example/macos/Podfile index 049abe29..c795730d 100644 --- a/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 new file mode 100644 index 00000000..a3151148 --- /dev/null +++ b/packages/window_manager/example/macos/Podfile.lock @@ -0,0 +1,28 @@ +PODS: + - FlutterMacOS (1.0.0) + - screen_retriever_macos (0.0.1): + - FlutterMacOS + - tray_manager (0.0.1): + - 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`) + +EXTERNAL SOURCES: + FlutterMacOS: + :path: Flutter/ephemeral + screen_retriever_macos: + :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos + tray_manager: + :path: Flutter/ephemeral/.symlinks/plugins/tray_manager/macos + +SPEC CHECKSUMS: + FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + screen_retriever_macos: 452e51764a9e1cdb74b3c541238795849f21557f + tray_manager: a104b5c81b578d83f3c3d0f40a997c8b10810166 + +PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 + +COCOAPODS: 1.16.2 diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/packages/window_manager/example/macos/Runner.xcodeproj/project.pbxproj similarity index 69% rename from example/macos/Runner.xcodeproj/project.pbxproj rename to packages/window_manager/example/macos/Runner.xcodeproj/project.pbxproj index 02283c15..60b9e0db 100644 --- a/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 = 1300; + 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/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 69% rename from example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to packages/window_manager/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index fe5b724f..a5771437 100644 --- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/window_manager/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + + + + + 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 83% rename from example/macos/Runner/AppDelegate.swift rename to packages/window_manager/example/macos/Runner/AppDelegate.swift index 814924b8..0b1cef5f 100644 --- a/example/macos/Runner/AppDelegate.swift +++ b/packages/window_manager/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 @@ -19,4 +19,8 @@ class AppDelegate: FlutterAppDelegate { } return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } 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/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 new file mode 100644 index 00000000..82b6f9d9 Binary files /dev/null and b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ 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 new file mode 100644 index 00000000..13b35eba Binary files /dev/null and b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/packages/window_manager/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 new file mode 100644 index 00000000..0a3f5fa4 Binary files /dev/null and b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ 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 new file mode 100644 index 00000000..bdb57226 Binary files /dev/null and b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/packages/window_manager/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 new file mode 100644 index 00000000..f083318e Binary files /dev/null and b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ 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 new file mode 100644 index 00000000..326c0e72 Binary files /dev/null and b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/packages/window_manager/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 new file mode 100644 index 00000000..2f1632cf Binary files /dev/null and b/packages/window_manager/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/example/macos/Runner/Base.lproj/MainMenu.xib b/packages/window_manager/example/macos/Runner/Base.lproj/MainMenu.xib similarity index 98% rename from example/macos/Runner/Base.lproj/MainMenu.xib rename to packages/window_manager/example/macos/Runner/Base.lproj/MainMenu.xib index 537341ab..80e867a4 100644 --- a/example/macos/Runner/Base.lproj/MainMenu.xib +++ b/packages/window_manager/example/macos/Runner/Base.lproj/MainMenu.xib @@ -323,6 +323,10 @@ + + + + diff --git a/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/window_manager/example/macos/Runner/Configs/AppInfo.xcconfig similarity index 75% rename from example/macos/Runner/Configs/AppInfo.xcconfig rename to packages/window_manager/example/macos/Runner/Configs/AppInfo.xcconfig index c001f030..b4fe57b1 100644 --- a/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/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 71% rename from example/macos/Runner/MainFlutterWindow.swift rename to packages/window_manager/example/macos/Runner/MainFlutterWindow.swift index 872ba9cf..223c3bbe 100644 --- a/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/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/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/example/pubspec.yaml b/packages/window_manager/example/pubspec.yaml new file mode 100644 index 00000000..c1feaa7d --- /dev/null +++ b/packages/window_manager/example/pubspec.yaml @@ -0,0 +1,28 @@ +name: window_manager_example +description: Demonstrates how to use the window_manager plugin. +publish_to: "none" + +environment: + sdk: ">=3.0.0 <4.0.0" + +dependencies: + bot_toast: ^4.1.3 + cupertino_icons: ^1.0.2 + flutter: + sdk: flutter + preference_list: ^0.0.2 + tray_manager: ^0.2.3 + window_manager: + path: ../ + +dev_dependencies: + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + mostly_reasonable_lints: ^0.1.2 + +flutter: + uses-material-design: true + assets: + - images/ 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 94% rename from example/windows/flutter/CMakeLists.txt rename to packages/window_manager/example/windows/flutter/CMakeLists.txt index d4b57706..4f2af69b 100644 --- a/example/windows/flutter/CMakeLists.txt +++ b/packages/window_manager/example/windows/flutter/CMakeLists.txt @@ -1,103 +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") - -# === 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" - windows-x64 $ - 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 89% rename from lib/src/utils/calc_window_position.dart rename to packages/window_manager/lib/src/utils/calc_window_position.dart index 1a3ca7af..4e9e3431 100644 --- a/lib/src/utils/calc_window_position.dart +++ b/packages/window_manager/lib/src/utils/calc_window_position.dart @@ -79,6 +79,15 @@ 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; } 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 96% rename from lib/src/widgets/drag_to_move_area.dart rename to packages/window_manager/lib/src/widgets/drag_to_move_area.dart index e63b8f0d..9c49b205 100644 --- a/lib/src/widgets/drag_to_move_area.dart +++ b/packages/window_manager/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/packages/window_manager/lib/src/widgets/drag_to_resize_area.dart similarity index 99% rename from lib/src/widgets/drag_to_resize_area.dart rename to packages/window_manager/lib/src/widgets/drag_to_resize_area.dart index 0714d5f3..15237e14 100644 --- a/lib/src/widgets/drag_to_resize_area.dart +++ b/packages/window_manager/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/packages/window_manager/lib/src/widgets/virtual_window_frame.dart similarity index 96% rename from lib/src/widgets/virtual_window_frame.dart rename to packages/window_manager/lib/src/widgets/virtual_window_frame.dart index 38a9b768..98a2c235 100644 --- a/lib/src/widgets/virtual_window_frame.dart +++ b/packages/window_manager/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( @@ -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/lib/src/widgets/window_caption.dart b/packages/window_manager/lib/src/widgets/window_caption.dart similarity index 96% rename from lib/src/widgets/window_caption.dart rename to packages/window_manager/lib/src/widgets/window_caption.dart index 2068dff9..a14779c7 100644 --- a/lib/src/widgets/window_caption.dart +++ b/packages/window_manager/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 @@ -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 new file mode 100644 index 00000000..86930cc1 --- /dev/null +++ b/packages/window_manager/lib/src/widgets/window_caption_button.dart @@ -0,0 +1,465 @@ +// ignore_for_file: library_private_types_in_public_api + +import 'package:flutter/material.dart'; + +const _kIconChromeClose = 'icon_chrome_close'; +const _kIconChromeMaximize = 'icon_chrome_maximize'; +const _kIconChromeMinimize = 'icon_chrome_minimize'; +const _kIconChromeUnmaximize = 'icon_chrome_unmaximize'; + +class _IconChromeMinimizePainter extends CustomPainter { + _IconChromeMinimizePainter(this.color); + + final Color color; + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = color + ..style = PaintingStyle.fill; + + final path = Path() + ..moveTo(3.49805, 8) + ..cubicTo(3.42969, 8, 3.36458, 7.98698, 3.30273, 7.96094) + ..cubicTo(3.24414, 7.9349, 3.19206, 7.89909, 3.14648, 7.85352) + ..cubicTo(3.10091, 7.80794, 3.0651, 7.75586, 3.03906, 7.69727) + ..cubicTo(3.01302, 7.63542, 3, 7.57031, 3, 7.50195) + ..cubicTo(3, 7.43359, 3.01302, 7.37012, 3.03906, 7.31152) + ..cubicTo(3.0651, 7.24967, 3.10091, 7.19596, 3.14648, 7.15039) + ..cubicTo(3.19206, 7.10156, 3.24414, 7.06413, 3.30273, 7.03809) + ..cubicTo(3.36458, 7.01204, 3.42969, 6.99902, 3.49805, 6.99902) + ..lineTo(12.502, 6.99902) + ..cubicTo(12.5703, 6.99902, 12.6338, 7.01204, 12.6924, 7.03809) + ..cubicTo(12.7542, 7.06413, 12.8079, 7.10156, 12.8535, 7.15039) + ..cubicTo(12.8991, 7.19596, 12.9349, 7.24967, 12.9609, 7.31152) + ..cubicTo(12.987, 7.37012, 13, 7.43359, 13, 7.50195) + ..cubicTo(13, 7.57031, 12.987, 7.63542, 12.9609, 7.69727) + ..cubicTo(12.9349, 7.75586, 12.8991, 7.80794, 12.8535, 7.85352) + ..cubicTo(12.8079, 7.89909, 12.7542, 7.9349, 12.6924, 7.96094) + ..cubicTo(12.6338, 7.98698, 12.5703, 8, 12.502, 8) + ..close(); + + canvas.drawPath(path, paint); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return false; + } +} + +class _IconChromeMaximizePainter extends CustomPainter { + _IconChromeMaximizePainter(this.color); + + final Color color; + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = color + ..style = PaintingStyle.fill; + + Path path = Path() + ..moveTo(4.47461, 13) + ..cubicTo(4.2793, 13, 4.09212, 12.9609, 3.91309, 12.8828) + ..cubicTo(3.73405, 12.8014, 3.57617, 12.694, 3.43945, 12.5605) + ..cubicTo(3.30599, 12.4238, 3.19857, 12.266, 3.11719, 12.0869) + ..cubicTo(3.03906, 11.9079, 3, 11.7207, 3, 11.5254) + ..lineTo(3, 4.47461) + ..cubicTo(3, 4.2793, 3.03906, 4.09212, 3.11719, 3.91309) + ..cubicTo(3.19857, 3.73405, 3.30599, 3.5778, 3.43945, 3.44434) + ..cubicTo(3.57617, 3.30762, 3.73405, 3.2002, 3.91309, 3.12207) + ..cubicTo(4.09212, 3.04069, 4.2793, 3, 4.47461, 3) + ..lineTo(11.5254, 3) + ..cubicTo(11.7207, 3, 11.9079, 3.04069, 12.0869, 3.12207) + ..cubicTo(12.266, 3.2002, 12.4222, 3.30762, 12.5557, 3.44434) + ..cubicTo(12.6924, 3.5778, 12.7998, 3.73405, 12.8779, 3.91309) + ..cubicTo(12.9593, 4.09212, 13, 4.2793, 13, 4.47461) + ..lineTo(13, 11.5254) + ..cubicTo(13, 11.7207, 12.9593, 11.9079, 12.8779, 12.0869) + ..cubicTo(12.7998, 12.266, 12.6924, 12.4238, 12.5557, 12.5605) + ..cubicTo(12.4222, 12.694, 12.266, 12.8014, 12.0869, 12.8828) + ..cubicTo(11.9079, 12.9609, 11.7207, 13, 11.5254, 13) + ..lineTo(4.47461, 13) + ..moveTo(11.501, 11.999) + ..cubicTo(11.5693, 11.999, 11.6328, 11.986, 11.6914, 11.96) + ..cubicTo(11.7533, 11.9339, 11.807, 11.8981, 11.8525, 11.8525) + ..cubicTo(11.8981, 11.807, 11.9339, 11.7549, 11.96, 11.6963) + ..cubicTo(11.986, 11.6344, 11.999, 11.5693, 11.999, 11.501) + ..lineTo(11.999, 4.49902) + ..cubicTo(11.999, 4.43066, 11.986, 4.36719, 11.96, 4.30859) + ..cubicTo(11.9339, 4.24674, 11.8981, 4.19303, 11.8525, 4.14746) + ..cubicTo(11.807, 4.10189, 11.7533, 4.06608, 11.6914, 4.04004) + ..cubicTo(11.6328, 4.014, 11.5693, 4.00098, 11.501, 4.00098) + ..lineTo(4.49902, 4.00098) + ..cubicTo(4.43066, 4.00098, 4.36556, 4.014, 4.30371, 4.04004) + ..cubicTo(4.24512, 4.06608, 4.19303, 4.10189, 4.14746, 4.14746) + ..cubicTo(4.10189, 4.19303, 4.06608, 4.24674, 4.04004, 4.30859) + ..cubicTo(4.014, 4.36719, 4.00098, 4.43066, 4.00098, 4.49902) + ..lineTo(4.00098, 11.501) + ..cubicTo(4.00098, 11.5693, 4.014, 11.6344, 4.04004, 11.6963) + ..cubicTo(4.06608, 11.7549, 4.10189, 11.807, 4.14746, 11.8525) + ..cubicTo(4.19303, 11.8981, 4.24512, 11.9339, 4.30371, 11.96) + ..cubicTo(4.36556, 11.986, 4.43066, 11.999, 4.49902, 11.999) + ..lineTo(11.501, 11.999) + ..close(); + + canvas.drawPath(path, paint); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return false; + } +} + +class _IconChromeUnmaximizePainter extends CustomPainter { + _IconChromeUnmaximizePainter(this.color); + + final Color color; + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = color + ..style = PaintingStyle.fill; + + final path = Path() + ..moveTo(11.999, 5.96387) + ..cubicTo(11.999, 5.69368, 11.9453, 5.43978, 11.8379, 5.20215) + ..cubicTo(11.7305, 4.96126, 11.584, 4.75293, 11.3984, 4.57715) + ..cubicTo(11.2161, 4.39811, 11.0029, 4.25814, 10.7588, 4.15723) + ..cubicTo(10.5179, 4.05306, 10.264, 4.00098, 9.99707, 4.00098) + ..lineTo(5.08496, 4.00098) + ..cubicTo(5.13704, 3.85124, 5.21029, 3.71452, 5.30469, 3.59082) + ..cubicTo(5.39909, 3.46712, 5.50814, 3.36133, 5.63184, 3.27344) + ..cubicTo(5.75553, 3.18555, 5.89062, 3.11882, 6.03711, 3.07324) + ..cubicTo(6.18685, 3.02441, 6.34147, 3, 6.50098, 3) + ..lineTo(9.99707, 3) + ..cubicTo(10.4105, 3, 10.7995, 3.07975, 11.1641, 3.23926) + ..cubicTo(11.5286, 3.39551, 11.846, 3.60872, 12.1162, 3.87891) + ..cubicTo(12.3896, 4.14909, 12.6045, 4.46647, 12.7607, 4.83105) + ..cubicTo(12.9202, 5.19564, 13, 5.58464, 13, 5.99805) + ..lineTo(13, 9.49902) + ..cubicTo(13, 9.65853, 12.9756, 9.81315, 12.9268, 9.96289) + ..cubicTo(12.8812, 10.1094, 12.8145, 10.2445, 12.7266, 10.3682) + ..cubicTo(12.6387, 10.4919, 12.5329, 10.6009, 12.4092, 10.6953) + ..cubicTo(12.2855, 10.7897, 12.1488, 10.863, 11.999, 10.915) + ..lineTo(11.999, 5.96387) + ..close() + ..moveTo(4.47461, 13) + ..cubicTo(4.2793, 13, 4.09212, 12.9609, 3.91309, 12.8828) + ..cubicTo(3.73405, 12.8014, 3.57617, 12.694, 3.43945, 12.5605) + ..cubicTo(3.30599, 12.4238, 3.19857, 12.266, 3.11719, 12.0869) + ..cubicTo(3.03906, 11.9079, 3, 11.7207, 3, 11.5254) + ..lineTo(3, 6.47656) + ..cubicTo(3, 6.27799, 3.03906, 6.09082, 3.11719, 5.91504) + ..cubicTo(3.19857, 5.736, 3.30599, 5.57975, 3.43945, 5.44629) + ..cubicTo(3.57617, 5.30957, 3.73242, 5.20215, 3.9082, 5.12402) + ..cubicTo(4.08724, 5.04264, 4.27604, 5.00195, 4.47461, 5.00195) + ..lineTo(9.52344, 5.00195) + ..cubicTo(9.72201, 5.00195, 9.91081, 5.04264, 10.0898, 5.12402) + ..cubicTo(10.2689, 5.20215, 10.4251, 5.30794, 10.5586, 5.44141) + ..cubicTo(10.6921, 5.57487, 10.7979, 5.73112, 10.876, 5.91016) + ..cubicTo(10.9574, 6.08919, 10.998, 6.27799, 10.998, 6.47656) + ..lineTo(10.998, 11.5254) + ..cubicTo(10.998, 11.724, 10.9574, 11.9128, 10.876, 12.0918) + ..cubicTo(10.7979, 12.2676, 10.6904, 12.4238, 10.5537, 12.5605) + ..cubicTo(10.4202, 12.694, 10.264, 12.8014, 10.085, 12.8828) + ..cubicTo(9.90918, 12.9609, 9.72201, 13, 9.52344, 13) + ..lineTo(4.47461, 13) + ..close() + ..moveTo(9.49902, 11.999) + ..cubicTo(9.56738, 11.999, 9.63086, 11.986, 9.68945, 11.96) + ..cubicTo(9.7513, 11.9339, 9.80501, 11.8981, 9.85059, 11.8525) + ..cubicTo(9.89941, 11.807, 9.93685, 11.7549, 9.96289, 11.6963) + ..cubicTo(9.98893, 11.6344, 10.002, 11.5693, 10.002, 11.501) + ..lineTo(10.002, 6.50098) + ..cubicTo(10.002, 6.43262, 9.98893, 6.36751, 9.96289, 6.30566) + ..cubicTo(9.93685, 6.24382, 9.90104, 6.1901, 9.85547, 6.14453) + ..cubicTo(9.8099, 6.09896, 9.75618, 6.06315, 9.69434, 6.03711) + ..cubicTo(9.63249, 6.01107, 9.56738, 5.99805, 9.49902, 5.99805) + ..lineTo(4.49902, 5.99805) + ..cubicTo(4.43066, 5.99805, 4.36556, 6.01107, 4.30371, 6.03711) + ..cubicTo(4.24512, 6.06315, 4.19303, 6.10059, 4.14746, 6.14941) + ..cubicTo(4.10189, 6.19499, 4.06608, 6.2487, 4.04004, 6.31055) + ..cubicTo(4.014, 6.36914, 4.00098, 6.43262, 4.00098, 6.50098) + ..lineTo(4.00098, 11.501) + ..cubicTo(4.00098, 11.5693, 4.014, 11.6344, 4.04004, 11.6963) + ..cubicTo(4.06608, 11.7549, 4.10189, 11.807, 4.14746, 11.8525) + ..cubicTo(4.19303, 11.8981, 4.24512, 11.9339, 4.30371, 11.96) + ..cubicTo(4.36556, 11.986, 4.43066, 11.999, 4.49902, 11.999) + ..lineTo(9.49902, 11.999) + ..close(); + + canvas.drawPath(path, paint); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return false; + } +} + +class _IconChromeClosePainter extends CustomPainter { + _IconChromeClosePainter(this.color); + + final Color color; + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = color + ..style = PaintingStyle.fill; + + final path = Path() + ..moveTo(8, 8.70801) + ..lineTo(3.85449, 12.8535) + ..cubicTo(3.75684, 12.9512, 3.63965, 13, 3.50293, 13) + ..cubicTo(3.3597, 13, 3.23926, 12.9528, 3.1416, 12.8584) + ..cubicTo(3.0472, 12.7607, 3, 12.6403, 3, 12.4971) + ..cubicTo(3, 12.3604, 3.04883, 12.2432, 3.14648, 12.1455) + ..lineTo(7.29199, 8) + ..lineTo(3.14648, 3.85449) + ..cubicTo(3.04883, 3.75684, 3, 3.63802, 3, 3.49805) + ..cubicTo(3, 3.42969, 3.01302, 3.36458, 3.03906, 3.30273) + ..cubicTo(3.0651, 3.24089, 3.10091, 3.1888, 3.14648, 3.14648) + ..cubicTo(3.19206, 3.10091, 3.24577, 3.0651, 3.30762, 3.03906) + ..cubicTo(3.36947, 3.01302, 3.43457, 3, 3.50293, 3) + ..cubicTo(3.63965, 3, 3.75684, 3.04883, 3.85449, 3.14648) + ..lineTo(8, 7.29199) + ..lineTo(12.1455, 3.14648) + ..cubicTo(12.2432, 3.04883, 12.362, 3, 12.502, 3) + ..cubicTo(12.5703, 3, 12.6338, 3.01302, 12.6924, 3.03906) + ..cubicTo(12.7542, 3.0651, 12.8079, 3.10091, 12.8535, 3.14648) + ..cubicTo(12.8991, 3.19206, 12.9349, 3.24577, 12.9609, 3.30762) + ..cubicTo(12.987, 3.36621, 13, 3.42969, 13, 3.49805) + ..cubicTo(13, 3.63802, 12.9512, 3.75684, 12.8535, 3.85449) + ..lineTo(8.70801, 8) + ..lineTo(12.8535, 12.1455) + ..cubicTo(12.9512, 12.2432, 13, 12.3604, 13, 12.4971) + ..cubicTo(13, 12.5654, 12.987, 12.6305, 12.9609, 12.6924) + ..cubicTo(12.9349, 12.7542, 12.8991, 12.8079, 12.8535, 12.8535) + ..cubicTo(12.8112, 12.8991, 12.7591, 12.9349, 12.6973, 12.9609) + ..cubicTo(12.6354, 12.987, 12.5703, 13, 12.502, 13) + ..cubicTo(12.362, 13, 12.2432, 12.9512, 12.1455, 12.8535) + ..lineTo(8, 8.70801) + ..close(); + + canvas.drawPath(path, paint); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return false; + } +} + +class WindowCaptionButtonIcon extends StatelessWidget { + const WindowCaptionButtonIcon({ + super.key, + this.color, + required this.createPainter, + }); + + final Color? color; + final CustomPainter Function(Color? color) createPainter; + + @override + Widget build(BuildContext context) { + return CustomPaint( + painter: createPainter(color), + size: const Size(16, 16), + ); + } +} + +// ignore: must_be_immutable +class WindowCaptionButton extends StatefulWidget { + WindowCaptionButton({ + super.key, + this.brightness, + this.icon, + this.iconName, + required this.onPressed, + }); + + WindowCaptionButton.minimize({ + super.key, + this.brightness, + this.icon, + this.onPressed, + }) : iconName = _kIconChromeMinimize; + + WindowCaptionButton.maximize({ + super.key, + this.brightness, + this.icon, + this.onPressed, + }) : iconName = _kIconChromeMaximize; + + WindowCaptionButton.unmaximize({ + super.key, + this.brightness, + this.icon, + this.onPressed, + }) : iconName = _kIconChromeUnmaximize; + + WindowCaptionButton.close({ + super.key, + this.brightness, + this.icon, + this.onPressed, + }) : iconName = _kIconChromeClose, + _lightButtonBgColorScheme = _ButtonBgColorScheme( + normal: Colors.transparent, + hovered: const Color(0xffC42B1C), + pressed: const Color(0xffC42B1C).withValues(alpha: 0.9), + ), + _lightButtonIconColorScheme = _ButtonIconColorScheme( + normal: Colors.black.withValues(alpha: 0.8956), + hovered: Colors.white, + 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).withValues(alpha: 0.9), + ), + _darkButtonIconColorScheme = _ButtonIconColorScheme( + normal: Colors.white, + hovered: Colors.white, + pressed: Colors.white.withValues(alpha: 0.786), + disabled: Colors.black.withValues(alpha: 0.3628), + ); + + final Brightness? brightness; + final Widget? icon; + final String? iconName; + final VoidCallback? onPressed; + + _ButtonBgColorScheme _lightButtonBgColorScheme = _ButtonBgColorScheme( + normal: Colors.transparent, + hovered: Colors.black.withValues(alpha: 0.0373), + pressed: Colors.black.withValues(alpha: 0.0241), + ); + _ButtonIconColorScheme _lightButtonIconColorScheme = _ButtonIconColorScheme( + 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.withValues(alpha: 0.0605), + pressed: Colors.white.withValues(alpha: 0.0419), + ); + _ButtonIconColorScheme _darkButtonIconColorScheme = _ButtonIconColorScheme( + normal: Colors.white, + hovered: Colors.white, + pressed: Colors.white.withValues(alpha: 0.786), + disabled: Colors.black.withValues(alpha: 0.3628), + ); + + _ButtonBgColorScheme get buttonBgColorScheme => brightness != Brightness.dark + ? _lightButtonBgColorScheme + : _darkButtonBgColorScheme; + + _ButtonIconColorScheme get buttonIconColorScheme => + brightness != Brightness.dark + ? _lightButtonIconColorScheme + : _darkButtonIconColorScheme; + + @override + State createState() => _WindowCaptionButtonState(); +} + +class _WindowCaptionButtonState extends State { + bool _isHovering = false; + bool _isPressed = false; + + void _onEntered({required bool hovered}) { + setState(() => _isHovering = hovered); + } + + void _onActive({required bool pressed}) { + setState(() => _isPressed = pressed); + } + + @override + Widget build(BuildContext context) { + Color bgColor = widget.buttonBgColorScheme.normal; + Color iconColor = widget.buttonIconColorScheme.normal; + + if (_isHovering) { + bgColor = widget.buttonBgColorScheme.hovered; + iconColor = widget.buttonIconColorScheme.hovered; + } + if (_isPressed) { + bgColor = widget.buttonBgColorScheme.pressed; + iconColor = widget.buttonIconColorScheme.pressed; + } + + return MouseRegion( + onExit: (value) => _onEntered(hovered: false), + onHover: (value) => _onEntered(hovered: true), + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTapDown: (_) => _onActive(pressed: true), + onTapCancel: () => _onActive(pressed: false), + onTapUp: (_) => _onActive(pressed: false), + onTap: widget.onPressed, + child: Container( + constraints: const BoxConstraints(minWidth: 46, minHeight: 32), + decoration: BoxDecoration( + color: bgColor, + ), + child: Center( + child: WindowCaptionButtonIcon( + 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!); + } + }, + ), + ), + ), + ), + ); + } +} + +class _ButtonBgColorScheme { + _ButtonBgColorScheme({ + required this.normal, + required this.hovered, + required this.pressed, + }); + final Color normal; + final Color hovered; + final Color pressed; +} + +class _ButtonIconColorScheme { + _ButtonIconColorScheme({ + required this.normal, + required this.hovered, + required this.pressed, + required this.disabled, + }); + final Color normal; + final Color hovered; + final Color pressed; + final Color disabled; +} diff --git a/lib/src/window_listener.dart b/packages/window_manager/lib/src/window_listener.dart similarity index 97% rename from lib/src/window_listener.dart rename to packages/window_manager/lib/src/window_listener.dart index 6b221800..54a18722 100644 --- a/lib/src/window_listener.dart +++ b/packages/window_manager/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/packages/window_manager/lib/src/window_manager.dart similarity index 96% rename from lib/src/window_manager.dart rename to packages/window_manager/lib/src/window_manager.dart index 9736a832..5f66c914 100644 --- a/lib/src/window_manager.dart +++ b/packages/window_manager/lib/src/window_manager.dart @@ -104,6 +104,16 @@ class WindowManager { await _channel.invokeMethod('ensureInitialized'); } + /// 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. Future setAsFrameless() async { await _channel.invokeMethod('setAsFrameless'); @@ -263,11 +273,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. @@ -318,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); } @@ -557,7 +567,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 +693,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 +730,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/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 99% rename from linux/window_manager_plugin.cc rename to packages/window_manager/linux/window_manager_plugin.cc index b1839577..7093ca49 100644 --- a/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 diff --git a/packages/window_manager/macos/window_manager.podspec b/packages/window_manager/macos/window_manager.podspec new file mode 100644 index 00000000..faf6e103 --- /dev/null +++ b/packages/window_manager/macos/window_manager.podspec @@ -0,0 +1,30 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint window_manager.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'window_manager' + s.version = '0.5.0' + s.summary = 'A new Flutter plugin project.' + s.description = <<-DESC +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 = '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' + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + s.swift_version = '5.0' +end 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/macos/Classes/WindowManager.swift b/packages/window_manager/macos/window_manager/Sources/window_manager/WindowManager.swift similarity index 99% rename from macos/Classes/WindowManager.swift rename to packages/window_manager/macos/window_manager/Sources/window_manager/WindowManager.swift index 5c69abcb..65fc4ce1 100644 --- a/macos/Classes/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) @@ -433,7 +437,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 diff --git a/macos/Classes/WindowManagerPlugin.swift b/packages/window_manager/macos/window_manager/Sources/window_manager/WindowManagerPlugin.swift similarity index 98% rename from macos/Classes/WindowManagerPlugin.swift rename to packages/window_manager/macos/window_manager/Sources/window_manager/WindowManagerPlugin.swift index da75f5d8..4119861c 100644 --- a/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) @@ -52,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/pubspec.yaml b/packages/window_manager/pubspec.yaml new file mode 100644 index 00000000..740665e3 --- /dev/null +++ b/packages/window_manager/pubspec.yaml @@ -0,0 +1,48 @@ +name: window_manager +description: This plugin allows Flutter desktop apps to resizing and repositioning the window. +version: 0.5.1 +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: + 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 88% rename from windows/window_manager.cpp rename to packages/window_manager/windows/window_manager.cpp index 0c2b06bf..1d19490f 100644 --- a/windows/window_manager.cpp +++ b/packages/window_manager/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; @@ -251,7 +270,7 @@ void WindowManager::Blur() { } bool WindowManager::IsFocused() { - return GetMainWindow() == GetActiveWindow(); + return GetMainWindow() == GetForegroundWindow(); } void WindowManager::Show() { @@ -323,6 +342,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 +375,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 +385,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 +399,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 +492,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 +573,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 +586,38 @@ void WindowManager::SetFullScreen(const flutter::EncodableMap& args) { g_title_bar_style_before_fullscreen = title_bar_style_; } - if (isFullScreen) { - ::SendMessage(mainWindow, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + 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); + 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, 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_NOMOVE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); } - } else { - if (!g_maximized_before_fullscreen) - Restore(); - ::SetWindowLongPtr(mainWindow, GWL_STYLE, g_style_before_fullscreen | WS_OVERLAPPEDWINDOW); + } else { // Restore from fullscreen + // 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, @@ -590,8 +625,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); @@ -604,10 +639,19 @@ 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); } } - - g_is_window_fullscreen = isFullScreen; } void WindowManager::SetAspectRatio(const flutter::EncodableMap& args) { @@ -838,22 +882,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() { @@ -1000,14 +1038,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) { diff --git a/windows/window_manager_plugin.cpp b/packages/window_manager/windows/window_manager_plugin.cpp similarity index 83% rename from windows/window_manager_plugin.cpp rename to packages/window_manager/windows/window_manager_plugin.cpp index acb2aa43..ba7720ae 100644 --- a/windows/window_manager_plugin.cpp +++ b/packages/window_manager/windows/window_manager_plugin.cpp @@ -1,573 +1,597 @@ -#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); -}; - -// 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); -} - -void WindowManagerPlugin::_EmitEvent(std::string eventName) { - 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; - } - - if (wParam && message == WM_NCCALCSIZE) { - 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; - } - 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; - } - 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; - } else { - // 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 - // 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 == TRUE) { - _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) { - LONG_PTR gwlStyle = - GetWindowLongPtr(window_manager->GetMainWindow(), GWL_STYLE); - if ((gwlStyle & (WS_CAPTION | WS_THICKFRAME)) == 0 && - wParam == SIZE_MAXIMIZED) { - _EmitEvent("enter-full-screen"); - window_manager->last_state = STATE_FULLSCREEN_ENTERED; - } else if (window_manager->last_state == STATE_FULLSCREEN_ENTERED && - wParam == SIZE_RESTORED) { - 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 (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; +} + +class WindowManagerPlugin : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); + + WindowManagerPlugin(flutter::PluginRegistrarWindows* registrar); + + virtual ~WindowManagerPlugin(); + + private: + std::unique_ptr< + flutter::MethodChannel, + std::default_delete>> + channel = nullptr; + + 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) { + auto plugin = std::make_unique(registrar); + + 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); + }); + 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() { + 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("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)); + } 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 ac8de0a1..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: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 url: "https://pub.dev" source: hosted - version: "2.4.2" + 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,52 +69,26 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.17.1" - dependency_validator: - dependency: "direct dev" - description: - name: dependency_validator - sha256: f727a5627aa405965fab4aef4f468e50a9b632ba0737fd2f98c932fec6d712b9 - 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_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 - source: sdk - version: "0.0.0" + version: "7.0.1" glob: dependency: transitive description: @@ -115,86 +97,126 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" - io: + graphs: dependency: transitive description: - name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" url: "https://pub.dev" source: hosted - version: "1.0.4" - js: + version: "2.3.2" + http: dependency: transitive description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + name: http + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 url: "https://pub.dev" source: hosted - version: "0.6.7" - json_annotation: + version: "1.2.2" + http_parser: dependency: transitive description: - name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + name: http_parser + sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360" url: "https://pub.dev" source: hosted - version: "4.8.1" - lints: + 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: - name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" url: "https://pub.dev" source: hosted - version: "2.1.1" - logging: + version: "1.0.4" + json_annotation: dependency: transitive description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "4.9.0" matcher: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.15" - material_color_utilities: - dependency: transitive + version: "0.12.17" + melos: + dependency: "direct dev" description: - name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + name: melos + sha256: a62abfa8c7826cec927f8585572bb9adf591be152150494d879ca2c75118809d url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "6.2.0" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.9.1" - package_config: + version: "1.16.0" + mustache_template: dependency: transitive description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + name: mustache_template + sha256: a46e26f91445bfb0b60519be280555b06792460b27b19e2b19ad5b9740df5d1c url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.0.0" path: - dependency: "direct main" + dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.1" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + process: + dependency: transitive + description: + name: process + sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" + url: "https://pub.dev" + source: hosted + version: "5.0.2" + prompts: + dependency: transitive + description: + name: prompts + sha256: "3773b845e85a849f01e793c4fc18a45d52d7783b4cb6c0569fad19f9d0a774a1" + url: "https://pub.dev" + source: hosted + version: "2.0.0" pub_semver: dependency: transitive description: @@ -203,59 +225,62 @@ 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: 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: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" 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: 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: @@ -268,18 +293,34 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.5.1" - 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" + version: "1.4.0" + uri: + dependency: transitive + description: + name: uri + sha256: "889eea21e953187c6099802b7b4cf5219ba8f3518f604a1033064d45b1b8268a" + url: "https://pub.dev" + source: hosted + 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: @@ -288,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.0.0 <4.0.0" - flutter: ">=3.3.0" + dart: ">=3.5.0 <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index a489f6a7..7b9be4ff 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,47 +1,9 @@ -name: window_manager -description: This plugin allows Flutter desktop apps to resizing and repositioning the window. -version: 0.3.7 +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: ">=2.18.0 <4.0.0" - flutter: ">=3.3.0" - -dependencies: - flutter: - sdk: flutter - path: ^1.8.2 - screen_retriever: ^0.1.9 + sdk: ">=3.0.0 <4.0.0" dev_dependencies: - dependency_validator: ^3.0.0 - flutter_lints: ^2.0.0 - flutter_test: - sdk: flutter - -flutter: - plugin: - platforms: - linux: - pluginClass: WindowManagerPlugin - macos: - 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 + melos: ^6.2.0