diff --git a/CHANGELOG.md b/CHANGELOG.md index ff8e28d..9edefbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## 1.1.0 +- Added methods to add a toolbar to the window on macOS and change its style. +- Added methods to enable/disable the window's shadow on macOS. +- Added method to make the window fully transparent on macOS. +- Added methods to ignore mouse events on macOS. +- Added method to set the window's subtitle on macOS. +- Added methods and widgets to create visual effect subviews on macOS. +- Improved documentation of various widgets and classes. + +**Breaking change:** +Migrated to [macos_window_utils](https://pub.dev/packages/macos_window_utils). See the [migration guide](https://github.com/alexmercerind/flutter_acrylic/blob/master/MIGRATIONGUIDE.md) for more information. + ## 1.0.0+2 - Hotfix: Fixes a problem with too many rebuilds in TitlebarSafeArea. diff --git a/MIGRATIONGUIDE.md b/MIGRATIONGUIDE.md new file mode 100644 index 0000000..b333544 --- /dev/null +++ b/MIGRATIONGUIDE.md @@ -0,0 +1,65 @@ +# Migration Guide +## ^1.0.0 → 1.1.0 +If you have followed the **“Additional setup for macOS”** instructions for flutter_acrylic 1.0.0, your `MainFlutterWindow.swift` file should like like this: + +```swift +import Cocoa +import FlutterMacOS +import flutter_acrylic + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let windowFrame = self.frame + let blurryContainerViewController = BlurryContainerViewController() + self.contentViewController = blurryContainerViewController + self.setFrame(windowFrame, display: true) + + /* Initialize the flutter_acrylic plugin */ + MainFlutterWindowManipulator.start(mainFlutterWindow: self) + RegisterGeneratedPlugins(registry: blurryContainerViewController.flutterViewController) + + super.awakeFromNib() + } +} +``` + +In 1.1.0, flutter_acrylic has been made a dependent of [macos_window_utils](https://pub.dev/packages/macos_window_utils), which needs to be initialized instead of flutter_acrylic. To do so, the following changes need to be made: + ++ Replace `import flutter_acrylic` with `import macos_window_utils`. ++ Replace `BlurryContainerViewController` with `MacOSWindowUtilsViewController`. + +Once you are done, your finished code should like something like this: + +```diff +import Cocoa +import FlutterMacOS +-import flutter_acrylic ++import macos_window_utils + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let windowFrame = self.frame +- let blurryContainerViewController = BlurryContainerViewController() +- self.contentViewController = blurryContainerViewController ++ let macOSWindowUtilsViewController = MacOSWindowUtilsViewController() ++ self.contentViewController = macOSWindowUtilsViewController + self.setFrame(windowFrame, display: true) + +- /* Initialize the flutter_acrylic plugin */ ++ /* Initialize the macos_window_utils plugin */ + MainFlutterWindowManipulator.start(mainFlutterWindow: self) +- RegisterGeneratedPlugins(registry: blurryContainerViewController.flutterViewController) ++ RegisterGeneratedPlugins(registry: macOSWindowUtilsViewController.flutterViewController) + + super.awakeFromNib() + } +} +``` + +Additionally, you may need to open the `Podfile` in your Xcode project and make sure the deployment target in the first line is set to `10.14.6` or above: + +```podspec +platform :osx, '10.14.6' +``` + +If your app does not support the macOS platform, no migration is necessary. \ No newline at end of file diff --git a/README.md b/README.md index d37d9b4..aca31c9 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Mention in your `pubspec.yaml`. ```yaml dependencies: ... - flutter_acrylic: ^1.0.0 + flutter_acrylic: ^1.1.0 ``` ## Example @@ -371,21 +371,23 @@ You can see the [example](https://github.com/alexmercerind/flutter_acrylic/blob/ **Additional setup for macOS:** +flutter_acrylic depends on the [macos_window_utils](https://pub.dev/packages/macos_window_utils) plugin, which needs to be initialized as follows: + Open the `macos/Runner.xcworkspace` folder of your project using Xcode, press ⇧ + ⌘ + O and search for `MainFlutterWindow.swift`. -Insert `import flutter_acrylic` at the top of the file. +Insert `import macos_window_utils` at the top of the file. Then, replace the code above the `super.awakeFromNib()`-line with the following code: ```swift let windowFrame = self.frame -let blurryContainerViewController = BlurryContainerViewController() -self.contentViewController = blurryContainerViewController +let macOSWindowUtilsViewController = MacOSWindowUtilsViewController() +self.contentViewController = macOSWindowUtilsViewController self.setFrame(windowFrame, display: true) -/* Initialize the flutter_acrylic plugin */ +/* Initialize the macos_window_utils plugin */ MainFlutterWindowManipulator.start(mainFlutterWindow: self) -RegisterGeneratedPlugins(registry: blurryContainerViewController.flutterViewController) +RegisterGeneratedPlugins(registry: macOSWindowUtilsViewController.flutterViewController) ``` Assuming you're starting with the default configuration, the finished code should look something like this: @@ -393,7 +395,7 @@ Assuming you're starting with the default configuration, the finished code shoul ```diff import Cocoa import FlutterMacOS -+import flutter_acrylic ++import macos_window_utils class MainFlutterWindow: NSWindow { override func awakeFromNib() { @@ -405,21 +407,27 @@ class MainFlutterWindow: NSWindow { - RegisterGeneratedPlugins(registry: flutterViewController) + let windowFrame = self.frame -+ let blurryContainerViewController = BlurryContainerViewController() -+ self.contentViewController = blurryContainerViewController ++ let macOSWindowUtilsViewController = MacOSWindowUtilsViewController() ++ self.contentViewController = macOSWindowUtilsViewController + self.setFrame(windowFrame, display: true) -+ /* Initialize the flutter_acrylic plugin */ ++ /* Initialize the macos_window_utils plugin */ + MainFlutterWindowManipulator.start(mainFlutterWindow: self) -+ RegisterGeneratedPlugins(registry: blurryContainerViewController.flutterViewController) ++ RegisterGeneratedPlugins(registry: macOSWindowUtilsViewController.flutterViewController) super.awakeFromNib() } } ``` -Now press ⇧ + ⌘ + O once more and search for `Runner.xcodeproj`. Go to `info` > `Deployment Target` and set the `macOS Deployment Target` to `10.13` or above. +Now press ⇧ + ⌘ + O once more and search for `Runner.xcodeproj`. Go to `info` > `Deployment Target` and set the `macOS Deployment Target` to `10.14.6` or above. + +Additionally, you may need to open the `Podfile` in your Xcode project and make sure the deployment target in the first line is set to `10.14.6` or above: + +```podspec +platform :osx, '10.14.6' +``` Depending on your use case, you may want to extend the area of the window that Flutter can draw to to the entire window, such that you are able to draw onto the window's title bar as well (for example when you're only trying to make the sidebar transparent while the rest of the window is meant to stay opaque). diff --git a/example/lib/main.dart b/example/lib/main.dart index fc1ecdf..ca143d7 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -116,11 +116,13 @@ class MyAppBodyState extends State { /// Lets the madness begin! (macOS only.) /// - /// This method plays a silly little effect that is achieved using visual effect subviews. - /// It is designed to showcase a low-level approach to using visual effect subviews without - /// relying on the [VisualEffectSubviewContainer] widget. + /// This method plays a silly little effect that is achieved using visual + /// effect subviews. It is designed to showcase a low-level approach to using + /// visual effect subviews without relying on the + /// [VisualEffectSubviewContainer] widget. /// - /// In most cases, using the container widget is preferable, though, due to its ease of use. + /// In most cases, using the container widget is preferable, though, due to + /// its ease of use. void letTheMadnessBegin() async { final random = Random(); final windowWidth = MediaQuery.of(context).size.width; @@ -156,7 +158,8 @@ class MyAppBodyState extends State { )); if (frameX > windowWidth) { - // Remember to remove the visual effect subview when you no longer need it. + // Remember to remove the visual effect subview when you no longer + // need it. Window.removeVisualEffectSubview(subviewId); timer.cancel(); } @@ -166,9 +169,10 @@ class MyAppBodyState extends State { @override Widget build(BuildContext context) { - // The [TitlebarSafeArea] widget is required when running on macOS and enabling - // the full-size content view using [Window.setFullSizeContentView]. It ensures - // that its child is not covered by the macOS title bar. + // The [TitlebarSafeArea] widget is required when running on macOS and + // enabling the full-size content view using + // [Window.setFullSizeContentView]. It ensures that its child is not covered + // by the macOS title bar. return TitlebarSafeArea( child: SidebarFrame( macOSBlurViewState: macOSBlurViewState, @@ -194,7 +198,8 @@ class MyAppBodyState extends State { padding: const EdgeInsets.symmetric( vertical: 8.0, horizontal: 12.0), child: Text( - 'This is an example of a sidebar that has been implemented using the TransparentMacOSSidebar widget.', + 'This is an example of a sidebar that has been ' + 'implemented using the TransparentMacOSSidebar widget.', style: TextStyle( color: brightness.getForegroundColor(context), ), @@ -204,7 +209,8 @@ class MyAppBodyState extends State { padding: const EdgeInsets.symmetric( vertical: 4.0, horizontal: 12.0), child: Text( - 'Check out the sidebar_frame.dart file to see how it has been implemented!', + 'Check out the sidebar_frame.dart file to see how it ' + 'has been implemented!', style: TextStyle( color: brightness.getForegroundColor(context), ), @@ -215,7 +221,8 @@ class MyAppBodyState extends State { padding: const EdgeInsets.symmetric( vertical: 4.0, horizontal: 12.0), child: Text( - 'Press the following button if you would like to see some visual effect subview madness:', + 'Press the following button if you would like to see ' + 'some visual effect subview madness:', style: TextStyle( color: brightness.getForegroundColor(context), ), @@ -431,7 +438,11 @@ class MyAppBodyState extends State { function: () => Window.enableFullSizeContentView(), description: - 'This expands the area that Flutter can draw to to fill the entire window. It is recommended to enable the full-size content view when making the titlebar transparent.'), + 'This expands the area that Flutter ' + 'can draw to to fill the entire ' + 'window. It is recommended to enable ' + 'the full-size content view when ' + 'making the titlebar transparent.'), MacOSActionMenuItem( name: 'Disable Full Size Content View', function: () => @@ -501,7 +512,8 @@ class MyAppBodyState extends State { function: () => Window .setWindowBackgroundColorToDefaultColor(), description: - 'Sets the window background color to the default (opaque) window color.'), + 'Sets the window background color to ' + 'the default (opaque) window color.'), MacOSActionMenuItem( name: 'Set Window Background Color to Clear', @@ -528,8 +540,8 @@ class MyAppBodyState extends State { MacOSBlurViewState.inactive); }), MacOSActionMenuItem( - name: - 'Set Blur View State to Follows Window Active State', + name: 'Set Blur View State to Follows ' + 'Window Active State', function: () { setState(() { macOSBlurViewState = MacOSBlurViewState @@ -552,7 +564,10 @@ class MyAppBodyState extends State { toolbarStyle: MacOSToolbarStyle.automatic), description: - 'For this method to have an effect, the window needs to have had a toolbar added beforehand. This can be achieved using the “Add Toolbar” action.', + 'For this method to have an effect, the ' + 'window needs to have had a toolbar ' + 'added beforehand. This can be achieved ' + 'using the “Add Toolbar” action.', ), MacOSActionMenuItem( name: 'Set Toolbar Style to Expanded', @@ -560,7 +575,10 @@ class MyAppBodyState extends State { toolbarStyle: MacOSToolbarStyle.expanded, ), description: - 'For this method to have an effect, the window needs to have had a toolbar added beforehand. This can be achieved using the “Add Toolbar” action.', + 'For this method to have an effect, ' + 'the window needs to have had a toolbar ' + 'added beforehand. This can be achieved ' + 'using the “Add Toolbar” action.', ), MacOSActionMenuItem( name: 'Set Toolbar Style to Preference', @@ -568,14 +586,20 @@ class MyAppBodyState extends State { toolbarStyle: MacOSToolbarStyle.preference), description: - 'For this method to have an effect, the window needs to have had a toolbar added beforehand. This can be achieved using the “Add Toolbar” action.', + 'For this method to have an effect, the ' + 'window needs to have had a toolbar ' + 'added beforehand. This can be achieved ' + 'using the “Add Toolbar” action.', ), MacOSActionMenuItem( name: 'Set Toolbar Style to Unified', function: () => Window.setToolbarStyle( toolbarStyle: MacOSToolbarStyle.unified), description: - 'For this method to have an effect, the window needs to have had a toolbar added beforehand. This can be achieved using the “Add Toolbar” action.', + 'For this method to have an effect, the ' + 'window needs to have had a toolbar ' + 'added beforehand. This can be achieved ' + 'using the “Add Toolbar” action.', ), MacOSActionMenuItem( name: 'Set Toolbar Style to Unified Compact', @@ -583,7 +607,10 @@ class MyAppBodyState extends State { toolbarStyle: MacOSToolbarStyle.unifiedCompact), description: - 'For this method to have an effect, the window needs to have had a toolbar added beforehand. This can be achieved using the “Add Toolbar” action.', + 'For this method to have an effect, the ' + 'window needs to have had a toolbar ' + 'added beforehand. This can be achieved ' + 'using the “Add Toolbar” action.', ), MacOSActionMenuItem( name: 'Enable Shadow', @@ -597,13 +624,24 @@ class MyAppBodyState extends State { name: 'Invalidate Shadows', function: () => Window.invalidateShadows(), description: - 'This is a fairly technical action and is included here for completeness\' sake. Normally, it should not be necessary to use it.', + 'This is a fairly technical action and ' + 'is included here for completeness\' ' + 'sake. Normally, it should not be ' + 'necessary to use it.', ), MacOSActionMenuItem( name: 'Add Empty Mask Image', function: () => Window.addEmptyMaskImage(), description: - 'This will effectively disable the `NSVisualEffectView`\'s effect.\n\n**Warning:** It is recommended to disable the window\'s shadow using `Window.disableShadow()` when using this method. Keeping the shadow enabled when using an empty mask image can cause visual artifacts and performance issues.', + 'This will effectively disable the ' + '`NSVisualEffectView`\'s effect.\n\n' + '**Warning:** It is recommended to ' + 'disable the window\'s shadow using ' + '`Window.disableShadow()` when using ' + 'this method. Keeping the shadow ' + 'enabled when using an empty mask image ' + 'can cause visual artifacts and ' + 'performance issues.', ), MacOSActionMenuItem( name: 'Remove Mask Image', @@ -614,7 +652,19 @@ class MyAppBodyState extends State { function: () => Window.makeWindowFullyTransparent(), description: - 'Makes a window fully transparent (with no blur effect). This is a convenience function which executes:\n```dart\nsetWindowBackgroundColorToClear();\nmakeTitlebarTransparent();\naddEmptyMaskImage();\ndisableShadow();\n```\n**Warning:** When the window is fully transparent, its highlight effect (the thin white line at the top of the window) is still visible. This is considered a bug and may change in a future version.', + 'Makes a window fully transparent ' + '(with no blur effect). This is a ' + 'convenience function which executes:\n' + '```dart\n' + 'setWindowBackgroundColorToClear();\n' + 'makeTitlebarTransparent();\n' + 'addEmptyMaskImage();\n' + 'disableShadow();\n```\n**Warning:** ' + 'When the window is fully transparent, ' + 'its highlight effect (the thin white ' + 'line at the top of the window) is ' + 'still visible. This is considered a ' + 'bug and may change in a future version.', ), MacOSActionMenuItem( name: 'Ignore Mouse Events', @@ -624,14 +674,30 @@ class MyAppBodyState extends State { () => Window.acknowledgeMouseEvents()); }, description: - 'This action can be used to make parts of the window click-through, which may be desirable when used in conjunction with `Window.makeWindowFullyTransparent()`.\n\n**Note:** Executing this action will make this widow click-through, thus making it impossible to perform the “Acknowledge Mouse Events” again. For this reason, the example app automatically starts acknowledging mouse events again after five seconds.', + 'This action can be used to make parts ' + 'of the window click-through, which may ' + 'be desirable when used in conjunction ' + 'with ' + '`Window.makeWindowFullyTransparent()`.' + '\n\n**Note:** Executing this action ' + 'will make this widow click-through, ' + 'thus making it impossible to perform ' + 'the “Acknowledge Mouse Events” again. ' + 'For this reason, the example app ' + 'automatically starts acknowledging ' + 'mouse events again after five seconds.', ), MacOSActionMenuItem( name: 'Acknowledge Mouse Events', function: () => Window.acknowledgeMouseEvents(), description: - 'This action is included here for completeness\' sake, however it is technically impossible to run it after performing the “Ignore Mouse Events” action, since the “show all actions” button can then no longer be clicked.', + 'This action is included here for ' + 'completeness\' sake, however it is ' + 'technically impossible to run it after ' + 'performing the “Ignore Mouse Events” ' + 'action, since the “show all actions” ' + 'button can then no longer be clicked.', ), MacOSActionMenuItem( name: 'Set Subtitle', @@ -642,7 +708,10 @@ class MyAppBodyState extends State { name: 'Remove Subtitle', function: () => Window.setSubtitle(''), description: - 'The action works by setting the subtitle to an empty string using `Window.setSubtitle(\'\')`. There is no method called `Window.removeSubtitle()`.', + 'The action works by setting the ' + 'subtitle to an empty string using ' + '`Window.setSubtitle(\'\')`. There is no ' + 'method called `Window.removeSubtitle()`.', ), ], ), diff --git a/example/lib/widgets/macos_action_menu/action_list/action_list.dart b/example/lib/widgets/macos_action_menu/action_list/action_list.dart index 6b4c56a..6ef596d 100644 --- a/example/lib/widgets/macos_action_menu/action_list/action_list.dart +++ b/example/lib/widgets/macos_action_menu/action_list/action_list.dart @@ -38,8 +38,8 @@ class ActionList extends StatelessWidget { })) .values .toList(), - // This SizedBox allows for a “scroll past end” effect and is necessary to prevent - // the DescriptionDisplay from covering the ActionList. + // This SizedBox allows for a “scroll past end” effect and is necessary + // to prevent the DescriptionDisplay from covering the ActionList. const SizedBox(height: DescriptionDisplay.maxHeight), ], ); diff --git a/example/lib/widgets/macos_action_menu/action_list/action_list_item.dart b/example/lib/widgets/macos_action_menu/action_list/action_list_item.dart index 3978e2a..68c8f21 100644 --- a/example/lib/widgets/macos_action_menu/action_list/action_list_item.dart +++ b/example/lib/widgets/macos_action_menu/action_list/action_list_item.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; /// An item to be displayed in the action list. @@ -22,11 +24,39 @@ class ActionListItem extends StatefulWidget { } class _ActionListItemState extends State { + final _globalKey = GlobalKey(); bool _isBeingHoveredOver = false; + void _ensureVisible() { + if (_globalKey.currentContext == null) { + return; + } + + Scrollable.ensureVisible(_globalKey.currentContext!, + alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtStart); + Scrollable.ensureVisible(_globalKey.currentContext!, + alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtEnd); + } + + @override + void initState() { + // Run `_ensureVisible` through a timer to ensure that it runs after the + // `build` method has already run. + if (widget.isSelected) { + Timer(const Duration(), _ensureVisible); + } + + super.initState(); + } + @override Widget build(BuildContext context) { + if (widget.isSelected) { + _ensureVisible(); + } + return GestureDetector( + key: _globalKey, onTap: widget.isSelected ? widget.perform : widget.select, child: MouseRegion( cursor: SystemMouseCursors.click, diff --git a/example/lib/widgets/macos_action_menu/description_display.dart b/example/lib/widgets/macos_action_menu/description_display.dart index 826034f..d1e1635 100644 --- a/example/lib/widgets/macos_action_menu/description_display.dart +++ b/example/lib/widgets/macos_action_menu/description_display.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; -/// A widget that displays an action menu item's description at the bottom -/// of the action menu. +/// A widget that displays an action menu item's description at the bottom of +/// the action menu. class DescriptionDisplay extends StatelessWidget { static const maxHeight = 150.0; diff --git a/example/lib/widgets/sidebar_frame/sidebar_frame.dart b/example/lib/widgets/sidebar_frame/sidebar_frame.dart index c6b0da7..7d38092 100644 --- a/example/lib/widgets/sidebar_frame/sidebar_frame.dart +++ b/example/lib/widgets/sidebar_frame/sidebar_frame.dart @@ -44,12 +44,13 @@ class _SidebarFrameState extends State { begin: _isOpen ? sidebarWidth : 0.0, end: _isOpen ? sidebarWidth : 0.0), builder: (BuildContext context, double value, Widget? child) { - // The TransparentMacOSSidebar needs to be built inside the TweenAnimationBuilder's `build` method - // because it needs to be rebuilt whenever its size changes so that the visual effect subview gets - // updated. - // If you ever find yourself in a situation where a rebuild cannot be guaranteed, check out the - // VisualEffectSubviewContainerResizeEventRelay widget, which lets you control the sidebar's - // update behavior manually. + // The TransparentMacOSSidebar needs to be built inside the + // TweenAnimationBuilder's `build` method because it needs to be + // rebuilt whenever its size changes so that the visual effect + // subview gets updated. If you ever find yourself in a + // situation where a rebuild cannot be guaranteed, check out the + // VisualEffectSubviewContainerResizeEventRelay class, which + // lets you control the sidebar's update behavior manually. return TransparentMacOSSidebar( state: widget.macOSBlurViewState, child: Container( diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift index be99bbc..e81fa90 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,9 +6,9 @@ import FlutterMacOS import Foundation import bitsdojo_window_macos -import flutter_acrylic +import macos_window_utils func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin")) - FlutterAcrylicPlugin.register(with: registry.registrar(forPlugin: "FlutterAcrylicPlugin")) + MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin")) } diff --git a/example/macos/Podfile b/example/macos/Podfile index dade8df..7ed4260 100644 --- a/example/macos/Podfile +++ b/example/macos/Podfile @@ -1,4 +1,4 @@ -platform :osx, '10.11' +platform :osx, '10.14.6' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index 6798ccf..cf775ba 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -4,11 +4,14 @@ PODS: - flutter_acrylic (0.1.0): - FlutterMacOS - FlutterMacOS (1.0.0) + - macos_window_utils (1.0.0): + - FlutterMacOS DEPENDENCIES: - bitsdojo_window_macos (from `Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos`) - flutter_acrylic (from `Flutter/ephemeral/.symlinks/plugins/flutter_acrylic/macos`) - FlutterMacOS (from `Flutter/ephemeral`) + - macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos`) EXTERNAL SOURCES: bitsdojo_window_macos: @@ -17,12 +20,15 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/flutter_acrylic/macos FlutterMacOS: :path: Flutter/ephemeral + macos_window_utils: + :path: Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos SPEC CHECKSUMS: bitsdojo_window_macos: 44e3b8fe3dd463820e0321f6256c5b1c16bb6a00 flutter_acrylic: c3df24ae52ab6597197837ce59ef2a8542640c17 FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811 + macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663 -PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c +PODFILE CHECKSUM: ff0a9a3ce75ee73f200ca7e2f47745698c917ef9 COCOAPODS: 1.11.3 diff --git a/example/macos/Runner/MainFlutterWindow.swift b/example/macos/Runner/MainFlutterWindow.swift index b050dfd..ecbe2bc 100644 --- a/example/macos/Runner/MainFlutterWindow.swift +++ b/example/macos/Runner/MainFlutterWindow.swift @@ -1,18 +1,18 @@ import Cocoa import FlutterMacOS -import flutter_acrylic +import macos_window_utils class MainFlutterWindow: NSWindow { override func awakeFromNib() { let windowFrame = self.frame - let blurryContainerViewController = BlurryContainerViewController() // new - self.contentViewController = blurryContainerViewController // new + let macOSWindowUtilsViewController = MacOSWindowUtilsViewController() + self.contentViewController = macOSWindowUtilsViewController self.setFrame(windowFrame, display: true) - - /* Initialize the flutter_acrylic plugin */ - MainFlutterWindowManipulator.start(mainFlutterWindow: self) // new - - RegisterGeneratedPlugins(registry: blurryContainerViewController.flutterViewController) // new + + /* Initialize the macos_window_utils plugin */ + MainFlutterWindowManipulator.start(mainFlutterWindow: self) + + RegisterGeneratedPlugins(registry: macOSWindowUtilsViewController.flutterViewController) super.awakeFromNib() } diff --git a/example/pubspec.lock b/example/pubspec.lock index ae189ff..92a4b73 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -110,7 +110,7 @@ packages: path: ".." relative: true source: path - version: "1.0.0+2" + version: "1.1.0" flutter_markdown: dependency: "direct main" description: @@ -123,6 +123,13 @@ packages: description: flutter source: sdk version: "0.0.0" + macos_window_utils: + dependency: transitive + description: + name: macos_window_utils + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" markdown: dependency: transitive description: @@ -227,5 +234,5 @@ packages: source: hosted version: "2.6.1" sdks: - dart: ">=2.17.0 <3.0.0" + dart: ">=2.18.5 <3.0.0" flutter: ">=3.0.0" diff --git a/lib/macos/converters/blur_view_state_to_visual_effect_view_state_converter.dart b/lib/macos/converters/blur_view_state_to_visual_effect_view_state_converter.dart new file mode 100644 index 0000000..5a7ac42 --- /dev/null +++ b/lib/macos/converters/blur_view_state_to_visual_effect_view_state_converter.dart @@ -0,0 +1,18 @@ +import 'package:flutter_acrylic/macos/macos_blur_view_state.dart'; +import 'package:macos_window_utils/macos/ns_visual_effect_view_state.dart'; + +class BlurViewStateToVisualEffectViewStateConverter { + BlurViewStateToVisualEffectViewStateConverter._(); + + static NSVisualEffectViewState convertBlurViewStateToVisualEffectViewState( + MacOSBlurViewState blurViewState) { + switch (blurViewState) { + case MacOSBlurViewState.active: + return NSVisualEffectViewState.active; + case MacOSBlurViewState.inactive: + return NSVisualEffectViewState.inactive; + case MacOSBlurViewState.followsWindowActiveState: + return NSVisualEffectViewState.followsWindowActiveState; + } + } +} diff --git a/lib/macos/converters/mac_toolbar_style_to_window_toolbar_style_converter.dart b/lib/macos/converters/mac_toolbar_style_to_window_toolbar_style_converter.dart new file mode 100644 index 0000000..a235360 --- /dev/null +++ b/lib/macos/converters/mac_toolbar_style_to_window_toolbar_style_converter.dart @@ -0,0 +1,22 @@ +import 'package:flutter_acrylic/macos/macos_toolbar_style.dart'; +import 'package:macos_window_utils/macos/ns_window_toolbar_style.dart'; + +class MacOSToolbarStyleToWindowToolbarStyleConverter { + MacOSToolbarStyleToWindowToolbarStyleConverter._(); + + static NSWindowToolbarStyle convertMacOSToolbarStyleToWindowToolbarStyle( + MacOSToolbarStyle toolbarStyle) { + switch (toolbarStyle) { + case MacOSToolbarStyle.automatic: + return NSWindowToolbarStyle.automatic; + case MacOSToolbarStyle.expanded: + return NSWindowToolbarStyle.expanded; + case MacOSToolbarStyle.preference: + return NSWindowToolbarStyle.preference; + case MacOSToolbarStyle.unified: + return NSWindowToolbarStyle.unified; + case MacOSToolbarStyle.unifiedCompact: + return NSWindowToolbarStyle.unifiedCompact; + } + } +} diff --git a/lib/macos/converters/window_effect_to_material_converter.dart b/lib/macos/converters/window_effect_to_material_converter.dart new file mode 100644 index 0000000..6431d53 --- /dev/null +++ b/lib/macos/converters/window_effect_to_material_converter.dart @@ -0,0 +1,78 @@ +import 'package:flutter_acrylic/flutter_acrylic.dart'; +import 'package:macos_window_utils/macos/ns_visual_effect_view_material.dart'; + +class WindowEffectToMaterialConverter { + WindowEffectToMaterialConverter._(); + + static NSVisualEffectViewMaterial convertWindowEffectToMaterial( + WindowEffect windowEffect) { + switch (windowEffect.index) { + case 0: // disabled + return NSVisualEffectViewMaterial.windowBackground; + + case 1: // solid + return NSVisualEffectViewMaterial.windowBackground; + + case 2: // transparent + return NSVisualEffectViewMaterial.underWindowBackground; + + case 3: // aero + return NSVisualEffectViewMaterial.hudWindow; + + case 4: // acrylic + return NSVisualEffectViewMaterial.fullScreenUI; + + case 5: // mica + return NSVisualEffectViewMaterial.headerView; + + case 6: // tabbed + return NSVisualEffectViewMaterial.headerView; + + /* The following effects are macOS-only: */ + case 7: // titlebar + return NSVisualEffectViewMaterial.titlebar; + + case 8: // selection + return NSVisualEffectViewMaterial.selection; + + case 9: // menu + return NSVisualEffectViewMaterial.menu; + + case 10: // popover + return NSVisualEffectViewMaterial.popover; + + case 11: // sidebar + return NSVisualEffectViewMaterial.sidebar; + + case 12: // headerView + return NSVisualEffectViewMaterial.headerView; + + case 13: // sheet + return NSVisualEffectViewMaterial.sheet; + + case 14: // windowBackground + return NSVisualEffectViewMaterial.windowBackground; + + case 15: // hudWindow + return NSVisualEffectViewMaterial.hudWindow; + + case 16: // fullScreenUI + return NSVisualEffectViewMaterial.fullScreenUI; + + case 17: // toolTip + return NSVisualEffectViewMaterial.toolTip; + + case 18: // contentBackground + return NSVisualEffectViewMaterial.contentBackground; + + case 19: // underWindowBackground + return NSVisualEffectViewMaterial.underWindowBackground; + + case 20: // underPageBackground + return NSVisualEffectViewMaterial.underPageBackground; + + default: + return NSVisualEffectViewMaterial.windowBackground; + } + } +} diff --git a/lib/macos/macos_blur_view_state.dart b/lib/macos/macos_blur_view_state.dart index 4e98cb3..8c0dbcf 100644 --- a/lib/macos/macos_blur_view_state.dart +++ b/lib/macos/macos_blur_view_state.dart @@ -6,6 +6,7 @@ enum MacOSBlurViewState { /// The backdrop should always appear inactive. inactive, - /// The backdrop should automatically appear active when the window is active, and inactive when it is not. + /// The backdrop should automatically appear active when the window is active, + /// and inactive when it is not. followsWindowActiveState } diff --git a/lib/macos/macos_toolbar_style.dart b/lib/macos/macos_toolbar_style.dart index a6b4a9a..f1c00b2 100644 --- a/lib/macos/macos_toolbar_style.dart +++ b/lib/macos/macos_toolbar_style.dart @@ -1,17 +1,20 @@ /// Available toolbar styles (macOS only). enum MacOSToolbarStyle { - /// A style indicating that the system determines the toolbar’s appearance and location. + /// A style indicating that the system determines the toolbar’s appearance + /// and location. automatic, /// A style indicating that the toolbar appears below the window title. expanded, - /// A style indicating that the toolbar appears below the window title with toolbar items centered in the toolbar. + /// A style indicating that the toolbar appears below the window title with + /// toolbar items centered in the toolbar. preference, /// A style indicating that the toolbar appears next to the window title. unified, - /// A style indicating that the toolbar appears next to the window title and with reduced margins to allow more focus on the window’s contents. + /// A style indicating that the toolbar appears next to the window title and + /// with reduced margins to allow more focus on the window’s contents. unifiedCompact, } diff --git a/lib/macos/visual_effect_view_properties.dart b/lib/macos/visual_effect_view_properties.dart index 8609940..66592e0 100644 --- a/lib/macos/visual_effect_view_properties.dart +++ b/lib/macos/visual_effect_view_properties.dart @@ -1,4 +1,9 @@ import 'package:flutter_acrylic/flutter_acrylic.dart'; +import 'package:flutter_acrylic/macos/converters/blur_view_state_to_visual_effect_view_state_converter.dart'; +import 'package:flutter_acrylic/macos/converters/window_effect_to_material_converter.dart'; + +import 'package:macos_window_utils/macos/visual_effect_view_properties.dart' + as MacOSWindowUtilsVisualEffectSubviewProperties; /// Visual effect subview properties (macOS only). /// @@ -13,7 +18,8 @@ class VisualEffectSubviewProperties { /// The x position of the subview's frame. final double? frameX; - /// The y position of the subview's frame, starting at the bottom of the window. + /// The y position of the subview's frame, starting at the bottom of the + /// window. final double? frameY; /// The alpha value of the subview. @@ -22,7 +28,8 @@ class VisualEffectSubviewProperties { /// The corner Radius of the subview. final double? cornerRadius; - /// A bitmask indicating which corners should follow the `cornerRadius` property. + /// A bitmask indicating which corners should follow the `cornerRadius` + /// property. final int? cornerMask; /// The effect/material of the subview. @@ -58,49 +65,29 @@ class VisualEffectSubviewProperties { effect == null && state == null; - /// Creates a map in which the properties of this instance are contained. - /// - /// Only non-null properties will be present in that map. - Map toMap() { - final result = {}; - - if (frameWidth != null) { - result['frameWidth'] = frameWidth; - } - - if (frameHeight != null) { - result['frameHeight'] = frameHeight; - } - - if (frameX != null) { - result['frameX'] = frameX; - } - - if (frameY != null) { - result['frameY'] = frameY; - } - - if (alphaValue != null) { - result['alphaValue'] = alphaValue; - } - - if (cornerRadius != null) { - result['cornerRadius'] = cornerRadius; - } - - if (cornerMask != null) { - result['cornerMask'] = cornerMask; - } - - if (effect != null) { - result['effect'] = effect!.index; - } - - if (state != null) { - result['state'] = state!.name; - } - - return result; + MacOSWindowUtilsVisualEffectSubviewProperties.VisualEffectSubviewProperties + toMacOSWindowUtilsVisualEffectSubviewProperties() { + final material = effect == null + ? null + : WindowEffectToMaterialConverter.convertWindowEffectToMaterial( + effect!); + final visualEffectViewState = state == null + ? null + : BlurViewStateToVisualEffectViewStateConverter + .convertBlurViewStateToVisualEffectViewState(state!); + + return MacOSWindowUtilsVisualEffectSubviewProperties + .VisualEffectSubviewProperties( + frameWidth: frameWidth, + frameHeight: frameHeight, + frameX: frameX, + frameY: frameY, + alphaValue: alphaValue, + cornerRadius: cornerRadius, + cornerMask: cornerMask, + material: material, + state: visualEffectViewState, + ); } @override @@ -132,6 +119,7 @@ class VisualEffectSubviewProperties { @override String toString() { - return '$frameWidth $frameHeight $frameX $frameY $alphaValue $cornerRadius $cornerMask $effect $state'; + return '$frameWidth $frameHeight $frameX $frameY $alphaValue $cornerRadius' + '$cornerMask $effect $state'; } } diff --git a/lib/widgets/titlebar_safe_area.dart b/lib/widgets/titlebar_safe_area.dart index 490b192..493bdf7 100644 --- a/lib/widgets/titlebar_safe_area.dart +++ b/lib/widgets/titlebar_safe_area.dart @@ -43,9 +43,10 @@ class TitlebarSafeArea extends StatelessWidget { /// A widget that provides a safe area for its child. /// - /// The safe area is the area on the top of the window that is not covered by the title bar. - /// This widget has no effect when the full-size content view is disabled or when the app is - /// running on a platform other than macOS. + /// The safe area is the area on the top of the window that is not covered by + /// the title bar. This widget has no effect when the full-size content view + /// is disabled or when the app is running on a platform other than macOS. + /// /// Example: /// ```dart /// TitlebarSafeArea( diff --git a/lib/widgets/transparent_macos_bottom_bar.dart b/lib/widgets/transparent_macos_bottom_bar.dart index bacdfdb..85fdf7e 100644 --- a/lib/widgets/transparent_macos_bottom_bar.dart +++ b/lib/widgets/transparent_macos_bottom_bar.dart @@ -3,25 +3,6 @@ import 'package:flutter_acrylic/flutter_acrylic.dart'; import 'package:flutter_acrylic/widgets/visual_effect_subview_container/visual_effect_subview_container.dart'; import 'package:flutter_acrylic/widgets/visual_effect_subview_container/visual_effect_subview_container_resize_event_relay.dart'; -/// A widget that applies a visual effect subview to a child widget that represents the -/// application's bottom bar. -/// -/// The `alphaValue` is applied to the visual effect subview. It does not affect the -/// opacity of the `child`. -/// -/// By default, a [TransparentMacOSBottomBar] updates its visual effect view whenever its -/// `build` method runs. If manual control over its update behavior is desired, it can -/// be supplied a [VisualEffectSubviewContainerResizeEventRelay] through which its -/// update behavior can be controlled manually. -/// -/// Usage example: -/// ```dart -/// TransparentMacOSBottomBar( -/// child: Container( -/// height: 40.0, -/// ), -/// ) -/// ``` class TransparentMacOSBottomBar extends StatelessWidget { final Widget child; final double alphaValue; @@ -29,6 +10,26 @@ class TransparentMacOSBottomBar extends StatelessWidget { final MacOSBlurViewState state; final VisualEffectSubviewContainerResizeEventRelay? resizeEventRelay; + /// A widget that applies a visual effect subview to a child widget that + /// represents the application's bottom bar. + /// + /// The [alphaValue] is applied to the visual effect subview. It does not + /// affect the opacity of the [child]. + /// + /// By default, a [TransparentMacOSBottomBar] updates its visual effect view + /// whenever its [build] method runs. If manual control over its update + /// behavior is desired, it can be supplied a + /// [VisualEffectSubviewContainerResizeEventRelay] through which its update + /// behavior can be controlled manually. + /// + /// Usage example: + /// ```dart + /// TransparentMacOSBottomBar( + /// child: Container( + /// height: 40.0, + /// ), + /// ) + /// ``` const TransparentMacOSBottomBar( {Key? key, this.alphaValue = 1.0, @@ -45,10 +46,10 @@ class TransparentMacOSBottomBar extends StatelessWidget { effect: effect, resizeEventRelay: resizeEventRelay, state: state, - // Due to the fact that visual effect subviews cannot be updated while the window is - // being resized, doing so can cause visual artifacts. To hide those artifacts, the - // TransparentMacOSBottomBar widget adds a large negative right margin to the visual - // effect subview. + // Due to the fact that visual effect subviews cannot be updated while the + // window is being resized, doing so can cause visual artifacts. To hide + // those artifacts, the TransparentMacOSBottomBar widget adds a large + // negative right margin to the visual effect subview. padding: EdgeInsets.only(right: -7680.0), child: child, ); diff --git a/lib/widgets/transparent_macos_sidebar.dart b/lib/widgets/transparent_macos_sidebar.dart index 07c42dd..4c35d3d 100644 --- a/lib/widgets/transparent_macos_sidebar.dart +++ b/lib/widgets/transparent_macos_sidebar.dart @@ -3,25 +3,6 @@ import 'package:flutter_acrylic/flutter_acrylic.dart'; import 'package:flutter_acrylic/widgets/visual_effect_subview_container/visual_effect_subview_container.dart'; import 'package:flutter_acrylic/widgets/visual_effect_subview_container/visual_effect_subview_container_resize_event_relay.dart'; -/// A widget that applies a visual effect subview to a child widget that represents the -/// application's sidebar. -/// -/// The `alphaValue` is applied to the visual effect subview. It does not affect the -/// opacity of the `child`. -/// -/// By default, a [TransparentMacOSSidebar] updates its visual effect view whenever its -/// `build` method runs. If manual control over its update behavior is desired, it can -/// be supplied a [VisualEffectSubviewContainerResizeEventRelay] through which its -/// update behavior can be controlled manually. -/// -/// Usage example: -/// ```dart -/// TransparentMacOSSidebar( -/// child: Container( -/// width: 250.0, -/// ), -/// ) -/// ``` class TransparentMacOSSidebar extends StatelessWidget { final Widget child; final double alphaValue; @@ -29,6 +10,26 @@ class TransparentMacOSSidebar extends StatelessWidget { final MacOSBlurViewState state; final VisualEffectSubviewContainerResizeEventRelay? resizeEventRelay; + /// A widget that applies a visual effect subview to a child widget that + /// represents the application's sidebar. + /// + /// The [alphaValue] is applied to the visual effect subview. It does not + /// affect the opacity of the [child]. + /// + /// By default, a [TransparentMacOSSidebar] updates its visual effect view + /// whenever its [build] method runs. If manual control over its update + /// behavior is desired, it can be supplied a + /// [VisualEffectSubviewContainerResizeEventRelay] through which its update + /// behavior can be controlled manually. + /// + /// Usage example: + /// ```dart + /// TransparentMacOSSidebar( + /// child: Container( + /// width: 250.0, + /// ), + /// ) + /// ``` const TransparentMacOSSidebar( {Key? key, this.alphaValue = 1.0, @@ -45,10 +46,10 @@ class TransparentMacOSSidebar extends StatelessWidget { effect: effect, resizeEventRelay: resizeEventRelay, state: state, - // Due to the fact that visual effect subviews cannot be updated while the window is - // being resized, doing so can cause visual artifacts. To hide those artifacts, the - // TransparentMacOSSidebar widget adds a large negative top margin to the visual - // effect subview. + // Due to the fact that visual effect subviews cannot be updated while the + // window is being resized, doing so can cause visual artifacts. To hide + // those artifacts, the TransparentMacOSSidebar widget adds a large + // negative top margin to the visual effect subview. padding: EdgeInsets.only(top: -4320.0), child: child, ); diff --git a/lib/widgets/visual_effect_subview_container/visual_effect_subview_container.dart b/lib/widgets/visual_effect_subview_container/visual_effect_subview_container.dart index ed85149..938213c 100644 --- a/lib/widgets/visual_effect_subview_container/visual_effect_subview_container.dart +++ b/lib/widgets/visual_effect_subview_container/visual_effect_subview_container.dart @@ -4,7 +4,8 @@ import 'package:flutter_acrylic/flutter_acrylic.dart'; import 'visual_effect_subview_container_resize_event_relay.dart'; import 'visual_effect_subview_container_with_global_key.dart'; -/// A container that applies a visual effect subview to its content (macOS only). +/// A container that applies a visual effect subview to its content +/// (macOS only). class VisualEffectSubviewContainer extends StatefulWidget { final Widget child; final double alphaValue; @@ -26,24 +27,33 @@ class VisualEffectSubviewContainer extends StatefulWidget { /// Creates a [VisualEffectSubviewContainer]. /// - /// The `alphaValue` is applied to the visual effect subview. It does not affect the opacity of the `child`. - /// Similarly, the `padding` only affects the subview, and does not affect the `child` either. + /// The [alphaValue] is applied to the visual effect subview. It does not + /// affect the opacity of the [child]. + /// Similarly, the [padding] only affects the subview, and does not affect + /// the [child] either. /// - /// The `cornerRadius` argument specifies the radius of the visual effect view's corners. Which corners are affected - /// is dependent on the `cornerMask` argument. Usage example: + /// The [cornerRadius] argument specifies the radius of the visual effect + /// view's corners. Which corners are affected is dependent on the + /// [cornerMask] argument. + /// + /// Usage example: /// /// ```dart /// VisualEffectSubviewContainer( /// effect: WindowEffect.hudWindow, /// cornerRadius: 32.0, - /// cornerMask: VisualEffectSubviewContainer.topLeftCorner + VisualEffectSubviewContainer.topRightCorner + VisualEffectSubviewContainer.bottomRightCorner, + /// cornerMask: VisualEffectSubviewContainer.topLeftCorner + + /// VisualEffectSubviewContainer.topRightCorner + + /// VisualEffectSubviewContainer.bottomRightCorner, /// child: Container(width: 128.0, height: 128.0), /// ) /// ``` /// - /// By default, a [VisualEffectSubviewContainer] updates its visual effect view whenever its `build` method runs. If - /// manual control over its update behavior is desired, it can be supplied a - /// [VisualEffectSubviewContainerResizeEventRelay] through which its update behavior can be controlled manually. + /// By default, a [VisualEffectSubviewContainer] updates its visual effect + /// view whenever its [build] method runs. If manual control over its update + /// behavior is desired, it can be supplied a + /// [VisualEffectSubviewContainerResizeEventRelay] through which its update + /// behavior can be controlled manually. const VisualEffectSubviewContainer( {Key? key, required this.child, diff --git a/lib/widgets/visual_effect_subview_container/visual_effect_subview_container_property_storage.dart b/lib/widgets/visual_effect_subview_container/visual_effect_subview_container_property_storage.dart index e1fcbc3..5f20dd7 100644 --- a/lib/widgets/visual_effect_subview_container/visual_effect_subview_container_property_storage.dart +++ b/lib/widgets/visual_effect_subview_container/visual_effect_subview_container_property_storage.dart @@ -2,8 +2,8 @@ import 'package:flutter_acrylic/flutter_acrylic.dart'; /// Storage for a [VisualEffectSubviewProperties] instance. /// -/// Provides methods to identify changes in said instance that need to be transmitted to the -/// Swift side. +/// Provides methods to identify changes in said instance that need to be +/// transmitted to the Swift side. class VisualEffectSubviewContainerPropertyStorage { VisualEffectSubviewProperties _currentProperties = VisualEffectSubviewProperties(); @@ -37,11 +37,13 @@ class VisualEffectSubviewContainerPropertyStorage { ); } - /// Returns a [VisualEffectSubviewProperties] instance in which only the fields whose values - /// need to be transmitted to the Swift side are populated. + /// Returns a [VisualEffectSubviewProperties] instance in which only the + /// fields whose values need to be transmitted to the Swift side are + /// populated. /// - /// Note that the frame's size and position are represented as an `NSSize` or an `NSPoint` - /// object respectively. For this reason, those two properties are treated as a single value. + /// Note that the frame's size and position are represented as an `NSSize` or + /// an `NSPoint` object respectively. For this reason, those two properties + /// are treated as a single value. VisualEffectSubviewProperties getDeltaProperties( VisualEffectSubviewProperties newProperties) { final propertyChange = _getPropertyChange(newProperties); @@ -102,7 +104,8 @@ class VisualEffectSubviewContainerPropertyStorage { } } -/// A change in a [VisualEffectSubviewContainer]'s [VisualEffectSubviewProperties]. +/// A change in a [VisualEffectSubviewContainer]'s +/// [VisualEffectSubviewProperties]. /// /// Each field corresponds to a property whose value may have changed. class _VisualEffectSubviewContainerPropertyChange { diff --git a/lib/widgets/visual_effect_subview_container/visual_effect_subview_container_with_global_key.dart b/lib/widgets/visual_effect_subview_container/visual_effect_subview_container_with_global_key.dart index 6dfa27e..b6e2cde 100644 --- a/lib/widgets/visual_effect_subview_container/visual_effect_subview_container_with_global_key.dart +++ b/lib/widgets/visual_effect_subview_container/visual_effect_subview_container_with_global_key.dart @@ -6,11 +6,6 @@ import 'package:flutter_acrylic/widgets/visual_effect_subview_container/visual_e import 'visual_effect_subview_container_property_storage.dart'; -/// A visual effect subview container which needs to be provided a global key. -/// -/// This widget is intended to be used by the [VisualEffectSubviewContainer] widget. -/// As a user of the [flutter_acrylic] package it is recommended to use that widget -/// instead, as it takes care of the global key creation by itself. class VisualEffectSubviewContainerWithGlobalKey extends StatefulWidget { final Widget child; final double alphaValue; @@ -28,6 +23,12 @@ class VisualEffectSubviewContainerWithGlobalKey extends StatefulWidget { static const bottomLeftCorner = VisualEffectSubviewProperties.bottomLeftCorner; + /// A visual effect subview container which needs to be provided a global key. + /// + /// This widget is intended to be used by the [VisualEffectSubviewContainer] + /// widget. As a user of the [flutter_acrylic] package it is recommended to + /// use that widget instead, as it takes care of the global key creation by + /// itself. const VisualEffectSubviewContainerWithGlobalKey( {required GlobalKey key, required this.child, @@ -90,7 +91,8 @@ class _VisualEffectSubviewContainerWithGlobalKeyState super.initState(); } - /// Removes the previously added visual effect subview from the application window. + /// Removes the previously added visual effect subview from the application + /// window. void _removeVisualEffectSubviewFromApplicationWindow() { if (_visualEffectSubviewId == null) { return; @@ -108,10 +110,10 @@ class _VisualEffectSubviewContainerWithGlobalKeyState /// Modifies the visual effect subview. /// - /// This method takes the current position and size of the visual effect subview and - /// compares the values of all of the subview's properties to their previous values. - /// If any differences are identified, the visual effect subview will be updated on - /// the Swift side. + /// This method takes the current position and size of the visual effect + /// subview and compares the values of all of the subview's properties to + /// their previous values. If any differences are identified, the visual + /// effect subview will be updated on the Swift side. void _modifyVisualEffectSubview( {required double xPosition, required double yPosition, @@ -141,8 +143,8 @@ class _VisualEffectSubviewContainerWithGlobalKeyState } } - /// Determines the position and size of this widget relative to the application - /// window and modifies the visual effect subview accordingly. + /// Determines the position and size of this widget relative to the + /// application window and modifies the visual effect subview accordingly. void _updateVisualEffectSubview() { final renderObject = (widget.key as GlobalKey) .currentContext! diff --git a/lib/window.dart b/lib/window.dart index ed89068..9ac0351 100644 --- a/lib/window.dart +++ b/lib/window.dart @@ -3,10 +3,14 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_acrylic/macos/converters/blur_view_state_to_visual_effect_view_state_converter.dart'; +import 'package:flutter_acrylic/macos/converters/mac_toolbar_style_to_window_toolbar_style_converter.dart'; import 'package:flutter_acrylic/macos/macos_blur_view_state.dart'; import 'package:flutter_acrylic/macos/macos_toolbar_style.dart'; import 'package:flutter_acrylic/macos/visual_effect_view_properties.dart'; +import 'package:flutter_acrylic/macos/converters/window_effect_to_material_converter.dart'; import 'package:flutter_acrylic/window_effect.dart'; +import 'package:macos_window_utils/window_manipulator.dart'; /// Platform channel name. const _kChannelName = "com.alexmercerind/flutter_acrylic"; @@ -29,152 +33,6 @@ const _kEnterFullscreen = "EnterFullscreen"; /// Exits fullscreen. const _kExitFullscreen = "ExitFullscreen"; -/// Gets the height of the titlebar. -const _kGetTitlebarHeight = "GetTitlebarHeight"; - -/// Overrides the brightness setting of the window (macOS only). -const _kOverrideMacOSBrightness = "OverrideMacOSBrightness"; - -/// (macOS only). -const _kSetDocumentEdited = "SetDocumentEdited"; - -/// (macOS only). -const _kSetDocumentUnedited = "SetDocumentUnedited"; - -/// (macOS only). -const _kSetRepresentedFile = "SetRepresentedFile"; - -/// (macOS only). -const _kSetRepresentedURL = "SetRepresentedURL"; - -/// (macOS only). -const _kHideTitle = "HideTitle"; - -/// (macOS only). -const _kShowTitle = "ShowTitle"; - -/// (macOS only). -const _kMakeTitlebarTransparent = "MakeTitlebarTransparent"; - -/// (macOS only). -const _kMakeTitlebarOpaque = "MakeTitlebarOpaque"; - -/// (macOS only). -const _kEnableFullSizeContentView = "EnableFullSizeContentView"; - -/// (macOS only). -const _kDisableFullSizeContentView = "DisableFullSizeContentView"; - -/// (macOS only). -const _kZoomWindow = "ZoomWindow"; - -/// (macOS only). -const _kUnzoomWindow = "UnzoomWindow"; - -/// (macOS only). -const _kIsWindowZoomed = "IsWindowZoomed"; - -/// (macOS only). -const _kIsWindowFullscreened = "IsWindowFullscreened"; - -/// (macOS only). -const _kHideZoomButton = "HideZoomButton"; - -/// (macOS only). -const _kShowZoomButton = "ShowZoomButton"; - -/// (macOS only). -const _kHideMiniaturizeButton = "HideMiniaturizeButton"; - -/// (macOS only). -const _kShowMiniaturizeButton = "ShowMiniaturizeButton"; - -/// (macOS only). -const _kHideCloseButton = "HideCloseButton"; - -/// (macOS only). -const _kShowCloseButton = "ShowCloseButton"; - -/// (macOS only). -const _kEnableZoomButton = "EnableZoomButton"; - -/// (macOS only). -const _kDisableZoomButton = "DisableZoomButton"; - -/// (macOS only). -const _kEnableMiniaturizeButton = "EnableMiniaturizeButton"; - -/// (macOS only). -const _kDisableMiniaturizeButton = "DisableMiniaturizeButton"; - -/// (macOS only). -const _kEnableCloseButton = "EnableCloseButton"; - -/// (macOS only). -const _kDisableCloseButton = "DisableCloseButton"; - -/// (macOS only). -const _kIsWindowInLiveResize = "IsWindowInLiveResize"; - -/// (macOS only). -const _kSetWindowAlphaValue = "SetWindowAlphaValue"; - -/// (macOS only). -const _kIsWindowVisible = "IsWindowVisible"; - -/// (macOS only). -const _kSetWindowBackgroundColorToDefaultColor = - "SetWindowBackgroundColorToDefaultColor"; - -/// (macOS only). -const _kSetWindowBackgroundColorToClear = "SetWindowBackgroundColorToClear"; - -/// (macOS only). -const _kSetBlurViewState = "SetBlurViewState"; - -/// (macOS only). -const _kAddVisualEffectSubview = "AddVisualEffectSubview"; - -/// (macOS only). -const _kUpdateVisualEffectSubviewProperties = - "UpdateVisualEffectSubviewProperties"; - -/// (macOS only). -const _kRemoveVisualEffectSubview = "RemoveVisualEffectSubview"; - -/// (macOS only). -const _kAddToolbar = "AddToolbar"; - -/// (macOS only). -const _kRemoveToolbar = "RemoveToolbar"; - -/// (macOS only). -const _kSetToolbarStyle = "SetToolbarStyle"; - -/// (macOS only) -const _kEnableShadow = "EnableShadow"; - -/// (macOS only) -const _kDisableShadow = "DisableShadow"; - -/// (macOS only) -const _kInvalidateShadows = "InvalidateShadows"; - -/// (macOS only) -const _kAddEmptyMaskImage = "AddEmptyMaskImage"; - -/// (macOS only) -const _kRemoveMaskImage = "RemoveMaskImage"; - -/// (macOS only) -const _kIgnoreMouseEvents = "IgnoreMouseEvents"; - -/// (macOS only) -const _kAcknowledgeMouseEvents = "AcknowledgeMouseEvents"; - -/// (macOS only) -const _kSetSubtitle = "SetSubtitle"; - final MethodChannel _kChannel = const MethodChannel(_kChannelName); final Completer _kCompleter = new Completer(); @@ -194,16 +52,26 @@ class Window { /// } /// ``` static Future initialize() async { + if (Platform.isMacOS) { + WindowManipulator.initialize(); + setEffect(effect: WindowEffect.values[0]); + return; + } + await _kChannel.invokeMethod(_kInitialize); _kCompleter.complete(); } /// Sets specified effect for the window. /// - /// When using [WindowEffect.mica], [dark] argument can be used to switch between light or dark mode of Mica. + /// When using [WindowEffect.mica], [dark] argument can be used to switch + /// between light or dark mode of Mica. /// - /// When using [WindowEffect.acrylic], [WindowEffect.aero], [WindowEffect.disabled], [WindowEffect.solid] or [WindowEffect.transparent], - /// [color] argument can be used to change the resulting tint (or color) of the window background. + /// When using [WindowEffect.acrylic], [WindowEffect.aero], + /// [WindowEffect.disabled], [WindowEffect.solid] or + /// [WindowEffect.transparent], + /// [color] argument can be used to change the resulting tint (or color) of + /// the window background. /// /// _Examples_ /// @@ -226,6 +94,13 @@ class Window { Color color: Colors.transparent, bool dark: true, }) async { + if (Platform.isMacOS) { + final material = + WindowEffectToMaterialConverter.convertWindowEffectToMaterial(effect); + WindowManipulator.setMaterial(material); + return; + } + await _kCompleter.future; await _kChannel.invokeMethod( _kSetEffect, @@ -244,107 +119,122 @@ class Window { /// Hides window controls. static Future hideWindowControls() async { + if (Platform.isMacOS) { + WindowManipulator.hideCloseButton(); + WindowManipulator.hideMiniaturizeButton(); + WindowManipulator.hideZoomButton(); + return; + } + await _kChannel.invokeMethod(_kHideWindowControls); } /// Shows window controls. static Future showWindowControls() async { + if (Platform.isMacOS) { + WindowManipulator.showCloseButton(); + WindowManipulator.showMiniaturizeButton(); + WindowManipulator.showZoomButton(); + return; + } + await _kChannel.invokeMethod(_kShowWindowControls); } /// Makes the Flutter window fullscreen. static Future enterFullscreen() async { + if (Platform.isMacOS) { + WindowManipulator.enterFullscreen(); + return; + } + await _kChannel.invokeMethod(_kEnterFullscreen); } /// Restores the Flutter window back to normal from fullscreen mode. static Future exitFullscreen() async { + if (Platform.isMacOS) { + WindowManipulator.exitFullscreen(); + return; + } + await _kChannel.invokeMethod(_kExitFullscreen); } /// Gets the height of the titlebar. /// /// This value is used to determine the [[TitlebarSafeArea]] widget. - /// If the full-size content view is enabled, this value will be the height of the titlebar. + /// If the full-size content view is enabled, this value will be the height + /// of the titlebar. /// If the full-size content view is disabled, this value will be 0. /// This value is only available on macOS. static Future getTitlebarHeight() async { - if (!Platform.isMacOS) { - throw new UnsupportedError( - 'getTitlebarHeight() is only available on macOS.'); + if (Platform.isMacOS) { + return WindowManipulator.getTitlebarHeight(); } - await _kCompleter.future; - return await _kChannel.invokeMethod(_kGetTitlebarHeight); + throw new UnsupportedError( + 'getTitlebarHeight() is only available on macOS.'); } /// Sets the document to be edited. /// - /// This will change the appearance of the close button on the titlebar. + /// This will change the appearance of the close button on the titlebar: + /// + /// image + /// /// This method is only available on macOS. static Future setDocumentEdited() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kSetDocumentEdited); + WindowManipulator.setDocumentEdited(); } /// Sets the document to be unedited. /// /// This method is only available on macOS. static Future setDocumentUnedited() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kSetDocumentUnedited); + WindowManipulator.setDocumentUnedited(); } /// Sets the represented file of the window. /// /// This method is only available on macOS. static Future setRepresentedFilename(String filename) async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kSetRepresentedFile, { - 'filename': filename, - }); + WindowManipulator.setRepresentedFilename(filename); } /// Sets the represented URL of the window. /// /// This method is only available on macOS. static Future setRepresentedUrl(String url) async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kSetRepresentedURL, { - 'url': url, - }); + WindowManipulator.setRepresentedUrl(url); } /// Hides the titlebar of the window. /// /// This method is only available on macOS. static Future hideTitle() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kHideTitle); + WindowManipulator.hideTitle(); } /// Shows the titlebar of the window. /// /// This method is only available on macOS. static Future showTitle() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kShowTitle); + WindowManipulator.showTitle(); } /// Makes the window's titlebar transparent. /// /// This method is only available on macOS. static Future makeTitlebarTransparent() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kMakeTitlebarTransparent); + WindowManipulator.makeTitlebarTransparent(); } /// Makes the window's titlebar opaque. /// /// This method is only available on macOS. static Future makeTitlebarOpaque() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kMakeTitlebarOpaque); + WindowManipulator.makeTitlebarOpaque(); } /// Enables the window's full-size content view. @@ -354,66 +244,67 @@ class Window { /// the titlebar transparent. /// This method is only available on macOS. static Future enableFullSizeContentView() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kEnableFullSizeContentView); + WindowManipulator.enableFullSizeContentView(); } /// Disables the window's full-size content view. /// /// This method is only available on macOS. static Future disableFullSizeContentView() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kDisableFullSizeContentView); + WindowManipulator.disableFullSizeContentView(); } /// Zooms the window. /// /// This method is only available on macOS. static Future zoomWindow() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kZoomWindow); + WindowManipulator.zoomWindow(); } /// Unzooms the window. /// /// This method is only available on macOS. static Future unzoomWindow() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kUnzoomWindow); + WindowManipulator.unzoomWindow(); } /// Returns if the window is zoomed. /// /// This method is only available on macOS. static Future isWindowZoomed() async { + if (Platform.isMacOS) { + return WindowManipulator.isWindowZoomed(); + } + if (!Platform.isMacOS) { throw new UnsupportedError( 'isWindowZoomed() is only available on macOS.'); } - await _kCompleter.future; - return await _kChannel.invokeMethod(_kIsWindowZoomed); + return false; } /// Returns if the window is fullscreened. /// /// This method is only available on macOS. static Future isWindowFullscreened() async { + if (Platform.isMacOS) { + return WindowManipulator.isWindowFullscreened(); + } + if (!Platform.isMacOS) { throw new UnsupportedError( 'isWindowFullscreened() is only available on macOS.'); } - await _kCompleter.future; - return await _kChannel.invokeMethod(_kIsWindowFullscreened); + return false; } /// Hides the window's zoom button. /// /// This method is only available on macOS. static Future hideZoomButton() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kHideZoomButton); + WindowManipulator.hideZoomButton(); } /// Shows the window's zoom button. @@ -421,16 +312,14 @@ class Window { /// The zoom button is visible by default. /// This method is only available on macOS. static Future showZoomButton() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kShowZoomButton); + WindowManipulator.showZoomButton(); } /// Hides the window's miniaturize button. /// /// This method is only available on macOS. static Future hideMiniaturizeButton() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kHideMiniaturizeButton); + WindowManipulator.hideMiniaturizeButton(); } /// Shows the window's miniaturize button. @@ -438,16 +327,14 @@ class Window { /// The miniaturize button is visible by default. /// This method is only available on macOS. static Future showMiniaturizeButton() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kShowMiniaturizeButton); + WindowManipulator.showMiniaturizeButton(); } /// Hides the window's close button. /// /// This method is only available on macOS. static Future hideCloseButton() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kHideCloseButton); + WindowManipulator.hideCloseButton(); } /// Shows the window's close button. @@ -455,8 +342,7 @@ class Window { /// The close button is visible by default. /// This method is only available on macOS. static Future showCloseButton() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kShowCloseButton); + WindowManipulator.showCloseButton(); } /// Enables the window's zoom button. @@ -464,16 +350,14 @@ class Window { /// The zoom button is enabled by default. /// This method is only available on macOS. static Future enableZoomButton() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kEnableZoomButton); + WindowManipulator.enableZoomButton(); } /// Disables the window's zoom button. /// /// This method is only available on macOS. static Future disableZoomButton() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kDisableZoomButton); + WindowManipulator.disableZoomButton(); } /// Enables the window's miniaturize button. @@ -481,16 +365,14 @@ class Window { /// The miniaturize button is enabled by default. /// This method is only available on macOS. static Future enableMiniaturizeButton() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kEnableMiniaturizeButton); + WindowManipulator.enableMiniaturizeButton(); } /// Disables the window's miniaturize button. /// /// This method is only available on macOS. static Future disableMiniaturizeButton() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kDisableMiniaturizeButton); + WindowManipulator.disableMiniaturizeButton(); } /// Enables the window's close button. @@ -498,51 +380,41 @@ class Window { /// The close button is enabled by default. /// This method is only available on macOS. static Future enableCloseButton() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kEnableCloseButton); + WindowManipulator.enableCloseButton(); } /// Disables the window's close button. /// /// This method is only available on macOS. static Future disableCloseButton() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kDisableCloseButton); + WindowManipulator.disableCloseButton(); } /// Gets whether the window is currently being resized by the user. /// /// This method is only available on macOS. static Future isWindowInLiveResize() async { - if (!Platform.isMacOS) { - throw UnsupportedError( - 'isWindowInLiveResize() is only available on macOS.'); + if (Platform.isMacOS) { + return WindowManipulator.isWindowInLiveResize(); } - await _kCompleter.future; - return await _kChannel.invokeMethod(_kIsWindowInLiveResize); + throw UnsupportedError( + 'isWindowInLiveResize() is only available on macOS.'); } /// Sets the window's alpha value. /// /// This method is only available on macOS. static Future setWindowAlphaValue(double value) async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kSetWindowAlphaValue, { - 'value': value, - }); + WindowManipulator.setWindowAlphaValue(value); + return; } /// Gets if the window is visible. /// /// This method is only available on macOS. static Future isWindowVisible() async { - if (!Platform.isMacOS) { - throw UnsupportedError('isWindowVisible() is only available on macOS.'); - } - - await _kCompleter.future; - return await _kChannel.invokeMethod(_kIsWindowVisible); + return WindowManipulator.isWindowVisible(); } /// Sets the window background color to the default (opaque) window color. @@ -550,8 +422,8 @@ class Window { /// This method mainly affects the window's titlebar. /// This method is only available on macOS. static Future setWindowBackgroundColorToDefaultColor() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kSetWindowBackgroundColorToDefaultColor); + WindowManipulator.setWindowBackgroundColorToDefaultColor(); + return; } /// Sets the window background color to clear. @@ -559,28 +431,31 @@ class Window { /// This method mainly affects the window's titlebar. /// This method is only available on macOS. static Future setWindowBackgroundColorToClear() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kSetWindowBackgroundColorToClear); + WindowManipulator.setWindowBackgroundColorToClear(); } /// Sets the blur view state. /// /// This method is only available on macOS. static Future setBlurViewState(MacOSBlurViewState state) async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kSetBlurViewState, { - 'state': state.toString().split('.').last, - }); + final visualEffectViewState = BlurViewStateToVisualEffectViewStateConverter + .convertBlurViewStateToVisualEffectViewState(state); + WindowManipulator.setNSVisualEffectViewState(visualEffectViewState); } - /// Adds a visual effect subview to the application's window and returns its ID. + /// Adds a visual effect subview to the application's window and returns its + /// ID. /// /// This method is only available on macOS. static Future addVisualEffectSubview( VisualEffectSubviewProperties properties) async { - await _kCompleter.future; - return await _kChannel.invokeMethod( - _kAddVisualEffectSubview, properties.toMap()); + if (Platform.isMacOS) { + final newProperties = + properties.toMacOSWindowUtilsVisualEffectSubviewProperties(); + return WindowManipulator.addVisualEffectSubview(newProperties); + } + + return -1; } /// Updates the properties of a visual effect subview. @@ -589,12 +464,10 @@ class Window { static Future updateVisualEffectSubviewProperties( int visualEffectSubviewId, VisualEffectSubviewProperties properties) async { - await _kCompleter.future; - await _kChannel - .invokeMethod(_kUpdateVisualEffectSubviewProperties, { - 'visualEffectSubviewId': visualEffectSubviewId, - ...properties.toMap(), - }); + final newProperties = + properties.toMacOSWindowUtilsVisualEffectSubviewProperties(); + WindowManipulator.updateVisualEffectSubviewProperties( + visualEffectSubviewId, newProperties); } /// Removes a visual effect subview from the application's window. @@ -602,35 +475,24 @@ class Window { /// This method is only available on macOS. static Future removeVisualEffectSubview( int visualEffectSubviewId) async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kRemoveVisualEffectSubview, { - 'visualEffectSubviewId': visualEffectSubviewId, - }); + WindowManipulator.removeVisualEffectSubview(visualEffectSubviewId); } /// Overrides the brightness setting of the window (macOS only). static Future overrideMacOSBrightness({ required bool dark, }) async { - await _kCompleter.future; - await _kChannel.invokeMethod( - _kOverrideMacOSBrightness, - { - 'dark': dark, - }, - ); + WindowManipulator.overrideMacOSBrightness(dark: dark); } /// Adds a toolbar to the window (macOS only). static Future addToolbar() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kAddToolbar, {}); + WindowManipulator.addToolbar(); } /// Removes the window's toolbar (macOS only). static Future removeToolbar() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kRemoveToolbar, {}); + WindowManipulator.removeToolbar(); } /// Sets the window's toolbar style (macOS only). @@ -645,22 +507,19 @@ class Window { /// ``` static Future setToolbarStyle( {required MacOSToolbarStyle toolbarStyle}) async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kSetToolbarStyle, { - 'toolbarStyle': toolbarStyle.name, - }); + final newToolbarStyle = MacOSToolbarStyleToWindowToolbarStyleConverter + .convertMacOSToolbarStyleToWindowToolbarStyle(toolbarStyle); + WindowManipulator.setToolbarStyle(toolbarStyle: newToolbarStyle); } /// Enables the window's shadow (macOS only). static Future enableShadow() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kEnableShadow, {}); + WindowManipulator.enableShadow(); } /// Disables the window's shadow (macOS only). static Future disableShadow() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kDisableShadow, {}); + WindowManipulator.disableShadow(); } /// Invalidates the window's shadow (macOS only). @@ -668,8 +527,7 @@ class Window { /// This is a fairly technical method and is included here for /// completeness' sake. Normally, it should not be necessary to use it. static Future invalidateShadows() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kInvalidateShadows, {}); + WindowManipulator.invalidateShadows(); } /// Adds an empty mask image to the window's view (macOS only). @@ -681,14 +539,12 @@ class Window { /// enabled when using an empty mask image can cause visual artifacts /// and performance issues. static Future addEmptyMaskImage() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kAddEmptyMaskImage, {}); + WindowManipulator.addEmptyMaskImage(); } /// Removes the window's mask image (macOS only). static Future removeMaskImage() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kRemoveMaskImage, {}); + WindowManipulator.removeMaskImage(); } /// Makes a window fully transparent (with no blur effect) (macOS only). @@ -717,8 +573,7 @@ class Window { /// may be desirable when used in conjunction with /// `Window.makeWindowFullyTransparent()`. static Future ignoreMouseEvents() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kIgnoreMouseEvents, {}); + WindowManipulator.ignoreMouseEvents(); } /// Makes the window acknowledge mouse events (macOS only). @@ -727,17 +582,13 @@ class Window { /// may be desirable when used in conjunction with /// `Window.makeWindowFullyTransparent()`. static Future acknowledgeMouseEvents() async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kAcknowledgeMouseEvents, {}); + WindowManipulator.acknowledgeMouseEvents(); } /// Sets the subtitle of the window (macOS only). /// /// To remove the subtitle, pass an empty string to this method. static Future setSubtitle(String subtitle) async { - await _kCompleter.future; - await _kChannel.invokeMethod(_kSetSubtitle, { - 'subtitle': subtitle, - }); + WindowManipulator.setSubtitle(subtitle); } } diff --git a/lib/window_effect.dart b/lib/window_effect.dart index 7a27321..31c32c9 100644 --- a/lib/window_effect.dart +++ b/lib/window_effect.dart @@ -17,15 +17,19 @@ enum WindowEffect { /// Works only on Windows. aero, - /// Acrylic is a type of brush that creates a translucent texture. You can apply acrylic to app surfaces to add depth and help establish a visual hierarchy. + /// Acrylic is a type of brush that creates a translucent texture. You can + /// apply acrylic to app surfaces to add depth and help establish a visual + /// hierarchy. /// Works only on Windows 10 version 1803 or higher. acrylic, - /// Mica is an opaque, dynamic material that incorporates theme and desktop wallpaper to paint the background of long-lived windows. + /// Mica is an opaque, dynamic material that incorporates theme and desktop + /// wallpaper to paint the background of long-lived windows. /// Works only on Windows 11 or greater. mica, - /// Tabbed is a Mica like material that incorporates theme and desktop wallpaper, but having more transparency. + /// Tabbed is a Mica like material that incorporates theme and desktop + /// wallpaper, but having more transparency. /// Works only on later Windows 11 versions (builds higher than 22523). tabbed, diff --git a/macos/Classes/BlurryContainerViewController.swift b/macos/Classes/BlurryContainerViewController.swift deleted file mode 100644 index fc0a68e..0000000 --- a/macos/Classes/BlurryContainerViewController.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// BlurryContainerViewController.swift -// flutter_acrylic -// -// Created by Adrian Samoticha on 21.10.22. -// - -import Foundation -import FlutterMacOS - -public class BlurryContainerViewController: NSViewController { - public let flutterViewController = FlutterViewController() - private var visualEffectSubviewRegistry = VisualEffectSubviewRegistry() - - public init() { - super.init(nibName: nil, bundle: nil) - } - - required public init?(coder: NSCoder) { - fatalError() - } - - override public func loadView() { - let blurView = NSVisualEffectView() - blurView.autoresizingMask = [.width, .height] - blurView.blendingMode = .behindWindow - blurView.state = .followsWindowActiveState - if #available(macOS 10.14, *) { - blurView.material = .underWindowBackground - } - self.view = blurView - } - - override public func viewDidLoad() { - super.viewDidLoad() - - self.addChild(flutterViewController) - - flutterViewController.view.frame = self.view.bounds - flutterViewController.view.autoresizingMask = [.width, .height] - self.view.addSubview(flutterViewController.view) - } - - public func addVisualEffectSubview(_ visualEffectSubview: VisualEffectSubview) -> UInt { - self.view.addSubview(visualEffectSubview, positioned: .below, relativeTo: flutterViewController.view) - return visualEffectSubviewRegistry.registerSubview(visualEffectSubview) - } - - public func getVisualEffectSubview(_ subviewId: UInt) -> VisualEffectSubview? { - return visualEffectSubviewRegistry.getSubviewFromId(subviewId) - } - - public func removeVisualEffectSubview(_ subviewId: UInt) { - let visualEffectSubview = visualEffectSubviewRegistry.getSubviewFromId(subviewId) - visualEffectSubview?.removeFromSuperview() - visualEffectSubviewRegistry.deregisterSubview(subviewId) - } -} diff --git a/macos/Classes/EffectIDToMaterialConverter.swift b/macos/Classes/EffectIDToMaterialConverter.swift deleted file mode 100644 index ec6d4c2..0000000 --- a/macos/Classes/EffectIDToMaterialConverter.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// EffectIDToMaterialConverter.swift -// flutter_acrylic -// -// Created by Adrian Samoticha on 21.10.22. -// - -import Foundation - -public class EffectIDToMaterialConverter { - @available(macOS 10.14, *) - public static func getMaterialFromEffectID(effectID: NSNumber) -> NSVisualEffectView.Material { - switch (effectID) { - /* Try to mimic the behavior of the following effects as - closely as possible: */ - case 0: // disabled - return NSVisualEffectView.Material.windowBackground - - case 1: // solid - return NSVisualEffectView.Material.windowBackground - - case 2: // transparent - return NSVisualEffectView.Material.underWindowBackground - - case 3: // aero - return NSVisualEffectView.Material.hudWindow - - case 4: // acrylic - return NSVisualEffectView.Material.fullScreenUI - - case 5: // mica - return NSVisualEffectView.Material.headerView - - case 6: // tabbed - return NSVisualEffectView.Material.headerView - - /* The following effects are macOS-only: */ - case 7: // titlebar - return NSVisualEffectView.Material.titlebar - - case 8: // selection - return NSVisualEffectView.Material.selection - - case 9: // menu - return NSVisualEffectView.Material.menu - - case 10: // popover - return NSVisualEffectView.Material.popover - - case 11: // sidebar - return NSVisualEffectView.Material.sidebar - - case 12: // headerView - return NSVisualEffectView.Material.headerView - - case 13: // sheet - return NSVisualEffectView.Material.sheet - - case 14: // windowBackground - return NSVisualEffectView.Material.windowBackground - - case 15: // hudWindow - return NSVisualEffectView.Material.hudWindow - - case 16: // fullScreenUI - return NSVisualEffectView.Material.fullScreenUI - - case 17: // toolTip - return NSVisualEffectView.Material.toolTip - - case 18: // contentBackground - return NSVisualEffectView.Material.contentBackground - - case 19: // underWindowBackground - return NSVisualEffectView.Material.underWindowBackground - - case 20: // underPageBackground - return NSVisualEffectView.Material.underPageBackground - - default: - return NSVisualEffectView.Material.windowBackground - } - } -} diff --git a/macos/Classes/FlutterAcrylicPlugin.swift b/macos/Classes/FlutterAcrylicPlugin.swift deleted file mode 100644 index 0521c93..0000000 --- a/macos/Classes/FlutterAcrylicPlugin.swift +++ /dev/null @@ -1,384 +0,0 @@ -import Cocoa -import FlutterMacOS - -public class FlutterAcrylicPlugin: NSObject, FlutterPlugin { - private var registrar: FlutterPluginRegistrar!; - private var channel: FlutterMethodChannel! - - private static func printUnsupportedMacOSVersionWarning() { - print("Warning: Transparency effects are not supported for your macOS Deployment Target.") - } - - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "com.alexmercerind/flutter_acrylic", binaryMessenger: registrar.messenger) - let instance = FlutterAcrylicPlugin(registrar, channel) - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public init(_ registrar: FlutterPluginRegistrar, _ channel: FlutterMethodChannel) { - super.init() - self.registrar = registrar - self.channel = channel - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - let methodName: String = call.method - let args: [String: Any] = call.arguments as? [String: Any] ?? [:] - - switch (methodName) { - case "Initialize": - if #available(macOS 10.14, *) { - let material = EffectIDToMaterialConverter.getMaterialFromEffectID(effectID: 0) - - MainFlutterWindowManipulator.setEffect(material: material) - } else { - FlutterAcrylicPlugin.printUnsupportedMacOSVersionWarning() - } - result(true) - break - - case "SetEffect": - if #available(macOS 10.14, *) { - let effectID = args["effect"] as! NSNumber - let material = EffectIDToMaterialConverter.getMaterialFromEffectID(effectID: effectID) - - MainFlutterWindowManipulator.setEffect(material: material) - } else { - FlutterAcrylicPlugin.printUnsupportedMacOSVersionWarning() - } - result(true) - break - - case "HideWindowControls": - MainFlutterWindowManipulator.hideZoomButton() - MainFlutterWindowManipulator.hideMiniaturizeButton() - MainFlutterWindowManipulator.hideCloseButton() - result(true) - break - - case "ShowWindowControls": - MainFlutterWindowManipulator.showZoomButton() - MainFlutterWindowManipulator.showMiniaturizeButton() - MainFlutterWindowManipulator.showCloseButton() - result(true) - break - - case "EnterFullscreen": - MainFlutterWindowManipulator.enterFullscreen() - result(true) - break - - case "ExitFullscreen": - MainFlutterWindowManipulator.exitFullscreen() - result(true) - break - - case "OverrideMacOSBrightness": - if #available(macOS 10.14, *) { - let dark = args["dark"] as! Bool - - MainFlutterWindowManipulator.setAppearance(dark: dark) - } else { - FlutterAcrylicPlugin.printUnsupportedMacOSVersionWarning() - } - result(true) - break - - case "SetDocumentEdited": - MainFlutterWindowManipulator.setDocumentEdited() - result(true) - break - - case "SetDocumentUnedited": - MainFlutterWindowManipulator.setDocumentUnedited() - result(true) - break - - case "SetRepresentedFile": - let filename = args["filename"] as! String - - MainFlutterWindowManipulator.setRepresentedFilename(filename: filename) - result(true) - break - - case "SetRepresentedURL": - let url = args["url"] as! String - - MainFlutterWindowManipulator.setRepresentedFilename(filename: url) - result(true) - break - - case "GetTitlebarHeight": - let titlebarHeight = MainFlutterWindowManipulator.getTitlebarHeight() as NSNumber - result(titlebarHeight) - break - - case "HideTitle": - MainFlutterWindowManipulator.hideTitle() - result(true) - break - - case "ShowTitle": - MainFlutterWindowManipulator.showTitle() - result(true) - break - - case "MakeTitlebarTransparent": - MainFlutterWindowManipulator.makeTitlebarTransparent() - result(true) - break - - case "MakeTitlebarOpaque": - MainFlutterWindowManipulator.makeTitlebarOpaque() - result(true) - break - - case "EnableFullSizeContentView": - MainFlutterWindowManipulator.enableFullSizeContentView() - result(true) - break - - case "DisableFullSizeContentView": - MainFlutterWindowManipulator.disableFullSizeContentView() - result(true) - break - - case "ZoomWindow": - MainFlutterWindowManipulator.zoomWindow() - result(true) - break - - case "UnzoomWindow": - MainFlutterWindowManipulator.unzoomWindow() - result(true) - break - - case "IsWindowZoomed": - let isWindowZoomed = MainFlutterWindowManipulator.isWindowZoomed() - result(isWindowZoomed) - break - - case "IsWindowFullscreened": - let isWindowFullscreened = MainFlutterWindowManipulator.isWindowFullscreened() - result(isWindowFullscreened) - break - - case "HideZoomButton": - MainFlutterWindowManipulator.hideZoomButton() - result(true) - break - - case "ShowZoomButton": - MainFlutterWindowManipulator.showZoomButton() - result(true) - break - - case "HideMiniaturizeButton": - MainFlutterWindowManipulator.hideMiniaturizeButton() - result(true) - break - - case "ShowMiniaturizeButton": - MainFlutterWindowManipulator.showMiniaturizeButton() - result(true) - break - - case "HideCloseButton": - MainFlutterWindowManipulator.hideCloseButton() - result(true) - break - - case "ShowCloseButton": - MainFlutterWindowManipulator.showCloseButton() - result(true) - break - - case "EnableZoomButton": - MainFlutterWindowManipulator.enableZoomButton() - result(true) - break - - case "DisableZoomButton": - MainFlutterWindowManipulator.disableZoomButton() - result(true) - break - - case "EnableMiniaturizeButton": - MainFlutterWindowManipulator.enableMiniaturizeButton() - result(true) - break - - case "DisableMiniaturizeButton": - MainFlutterWindowManipulator.disableMiniaturizeButton() - result(true) - break - - case "EnableCloseButton": - MainFlutterWindowManipulator.enableCloseButton() - result(true) - break - - case "DisableCloseButton": - MainFlutterWindowManipulator.disableCloseButton() - result(true) - break - - case "IsWindowInLiveResize": - let isWindowInLiveResize = MainFlutterWindowManipulator.isWindowInLiveResize() - result(isWindowInLiveResize) - break - - case "SetWindowAlphaValue": - let alphaValue = args["value"] as! NSNumber - MainFlutterWindowManipulator.setWindowAlphaValue(alphaValue: alphaValue as! CGFloat) - result(true) - break - - case "IsWindowVisible": - let isWindowVisible = MainFlutterWindowManipulator.isWindowVisible() - result(isWindowVisible) - break - - case "SetWindowBackgroundColorToDefaultColor": - MainFlutterWindowManipulator.setWindowBackgroundColorToDefaultColor() - result(true) - break - - case "SetWindowBackgroundColorToClear": - MainFlutterWindowManipulator.setWindowBackgroundColorToClear() - result(true) - break - - case "SetBlurViewState": - let blurViewStateString = args["state"] as! String - let state = blurViewStateString == "active" ? NSVisualEffectView.State.active : - blurViewStateString == "inactive" ? NSVisualEffectView.State.inactive : - NSVisualEffectView.State.followsWindowActiveState - MainFlutterWindowManipulator.setBlurViewState(state: state) - result(true) - break - - case "AddVisualEffectSubview": - let visualEffectSubview = VisualEffectSubview() - let visualEffectSubviewId = MainFlutterWindowManipulator.addVisualEffectSubview(visualEffectSubview) - - if #available(macOS 10.14, *) { - let properties = VisualEffectSubviewProperties.fromArgs(args) - properties.applyToVisualEffectSubview(visualEffectSubview) - } else { - FlutterAcrylicPlugin.printUnsupportedMacOSVersionWarning() - } - - result(visualEffectSubviewId) - break - - case "UpdateVisualEffectSubviewProperties": - let visualEffectSubviewId = args["visualEffectSubviewId"] as! UInt - let visualEffectSubview = MainFlutterWindowManipulator.getVisualEffectSubview(visualEffectSubviewId) - - if (visualEffectSubview != nil) { - if #available(macOS 10.14, *) { - let properties = VisualEffectSubviewProperties.fromArgs(args) - properties.applyToVisualEffectSubview(visualEffectSubview!) - } else { - FlutterAcrylicPlugin.printUnsupportedMacOSVersionWarning() - } - } - - result(visualEffectSubview != nil) - break - - case "RemoveVisualEffectSubview": - let visualEffectSubviewId = args["visualEffectSubviewId"] as! UInt - MainFlutterWindowManipulator.removeVisualEffectSubview(visualEffectSubviewId) - - result(true) - break - - case "AddToolbar": - MainFlutterWindowManipulator.addToolbar() - - result(true) - break - - case "RemoveToolbar": - MainFlutterWindowManipulator.removeToolbar() - - result(true) - break - - case "SetToolbarStyle": - let toolbarStyleName = args["toolbarStyle"] as! String - - if #available(macOS 11.0, *) { - let toolbarStyle = ToolbarStyleNameToEnumConverter.getToolbarStyleFromName(name: toolbarStyleName) - - if toolbarStyle != nil { - MainFlutterWindowManipulator.setToolbarStyle(toolbarStyle: toolbarStyle!) - } - } else { - FlutterAcrylicPlugin.printUnsupportedMacOSVersionWarning() - } - - result(true) - break - - case "EnableShadow": - MainFlutterWindowManipulator.enableShadow() - - result(true) - break - - case "DisableShadow": - MainFlutterWindowManipulator.disableShadow() - - result(true) - break - - case "InvalidateShadows": - MainFlutterWindowManipulator.invalidateShadows() - - result(true) - break - - case "AddEmptyMaskImage": - MainFlutterWindowManipulator.addEmptyMaskImage() - - result(true) - break - - case "RemoveMaskImage": - MainFlutterWindowManipulator.removeMaskImage() - - result(true) - break - - case "IgnoreMouseEvents": - MainFlutterWindowManipulator.ignoreMouseEvents() - - result(true) - break - - case "AcknowledgeMouseEvents": - MainFlutterWindowManipulator.acknowledgeMouseEvents() - - result(true) - break - - case "SetSubtitle": - let subtitle = args["subtitle"] as! String - if #available(macOS 11.0, *) { - MainFlutterWindowManipulator.setSubtitle(subtitle) - } else { - FlutterAcrylicPlugin.printUnsupportedMacOSVersionWarning() - } - - result(true) - break - - default: - result(FlutterMethodNotImplemented) - break - } - } -} diff --git a/macos/Classes/MainFlutterWindowManipulator.swift b/macos/Classes/MainFlutterWindowManipulator.swift deleted file mode 100644 index 2d45058..0000000 --- a/macos/Classes/MainFlutterWindowManipulator.swift +++ /dev/null @@ -1,496 +0,0 @@ -// -// MainFlutterWindowManipulator.swift -// flutter_acrylic -// -// Created by Adrian Samoticha on 21.10.22. -// - -import Foundation - -public class MainFlutterWindowManipulator { - private static var mainFlutterWindow: NSWindow? - - private static func printNotStartedWarning() { - print("Warning: The MainFlutterWindowManipulator has not been started. Please make sure the flutter_acrylic plugin is initialized correctly in your MainFlutterWindow.swift file.") - } - - public static func start(mainFlutterWindow: NSWindow) { - self.mainFlutterWindow = mainFlutterWindow - - showTitle() - makeTitlebarOpaque() - disableFullSizeContentView() - setWindowBackgroundColorToDefaultColor() - } - - public static func hideTitle() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.titleVisibility = NSWindow.TitleVisibility.hidden - } - - public static func showTitle() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.titleVisibility = NSWindow.TitleVisibility.visible - } - - public static func makeTitlebarTransparent() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.titlebarAppearsTransparent = true - } - - public static func makeTitlebarOpaque() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.titlebarAppearsTransparent = false - } - - public static func enableFullSizeContentView() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.styleMask.insert(.fullSizeContentView) - } - - public static func disableFullSizeContentView() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.styleMask.remove(.fullSizeContentView) - } - - public static func zoomWindow() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.setIsZoomed(true) - } - - public static func unzoomWindow() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.setIsZoomed(false) - } - - public static func isWindowZoomed() -> Bool { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return false - } - - return self.mainFlutterWindow!.isZoomed - } - - public static func enterFullscreen() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - if (!isWindowFullscreened()) { - self.mainFlutterWindow!.toggleFullScreen(self) - } - } - - public static func exitFullscreen() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - if (isWindowFullscreened()) { - self.mainFlutterWindow!.toggleFullScreen(self) - } - } - - public static func isWindowFullscreened() -> Bool { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return false - } - - let isFullscreenEnabled = self.mainFlutterWindow!.styleMask.contains(NSWindow.StyleMask.fullScreen) - return isFullscreenEnabled - } - - public static func hideZoomButton() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.standardWindowButton(.zoomButton)!.isHidden = true - } - - public static func showZoomButton() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.standardWindowButton(.zoomButton)!.isHidden = false - } - - public static func hideMiniaturizeButton() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.standardWindowButton(.miniaturizeButton)!.isHidden = true - } - - public static func showMiniaturizeButton() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.standardWindowButton(.miniaturizeButton)!.isHidden = false - } - - public static func hideCloseButton() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.standardWindowButton(.closeButton)!.isHidden = true - } - - public static func showCloseButton() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.standardWindowButton(.closeButton)!.isHidden = false - } - - public static func enableZoomButton() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.standardWindowButton(.zoomButton)!.isEnabled = true - } - - public static func disableZoomButton() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.standardWindowButton(.zoomButton)!.isEnabled = false - } - - public static func enableMiniaturizeButton() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.standardWindowButton(.miniaturizeButton)!.isEnabled = true - } - - public static func disableMiniaturizeButton() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.standardWindowButton(.miniaturizeButton)!.isEnabled = false - } - - public static func enableCloseButton() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.standardWindowButton(.closeButton)!.isEnabled = true - } - - public static func disableCloseButton() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.standardWindowButton(.closeButton)!.isEnabled = false - } - - public static func setDocumentEdited() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.isDocumentEdited = true - } - - public static func setDocumentUnedited() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.isDocumentEdited = false - } - - public static func setRepresentedFilename(filename: String) { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.representedFilename = filename - } - - public static func setRepresentedURL(url: String) { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.representedURL = URL(string: url) - } - - public static func isWindowInLiveResize() -> Bool { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return false - } - - return self.mainFlutterWindow!.inLiveResize - } - - public static func setWindowAlphaValue(alphaValue: CGFloat) { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.alphaValue = alphaValue - } - - public static func isWindowVisible() -> Bool { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return false - } - - return self.mainFlutterWindow!.isVisible - } - - public static func setWindowBackgroundColorToDefaultColor() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.backgroundColor = .windowBackgroundColor - } - - public static func setWindowBackgroundColorToClear() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.backgroundColor = .clear - } - - public static func setBlurViewState(state: NSVisualEffectView.State) { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - let blurryContainerViewController = self.mainFlutterWindow?.contentViewController as! BlurryContainerViewController; - (blurryContainerViewController.view as! NSVisualEffectView).state = state - } - - @available(macOS 10.14, *) - public static func setAppearance(dark: Bool) { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.contentView?.superview?.appearance = NSAppearance(named: dark ? .darkAqua : .aqua) - - self.mainFlutterWindow!.invalidateShadow() - } - - @available(macOS 10.14, *) - public static func setMaterial(material: NSVisualEffectView.Material) { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - let blurryContainerViewController = self.mainFlutterWindow!.contentViewController as! BlurryContainerViewController; - (blurryContainerViewController.view as! NSVisualEffectView).material = material - - self.mainFlutterWindow!.invalidateShadow() - } - - @available(macOS 10.14, *) - public static func setEffect(material: NSVisualEffectView.Material) { - setMaterial(material: material) - } - - public static func getTitlebarHeight() -> CGFloat { - let windowFrameHeight = (self.mainFlutterWindow!.contentView?.frame.height)! - let contentLayoutRectHeight = self.mainFlutterWindow!.contentLayoutRect.height - let fullSizeContentViewNoContentAreaHeight = windowFrameHeight - contentLayoutRectHeight - return fullSizeContentViewNoContentAreaHeight - } - - public static func addVisualEffectSubview(_ visualEffectSubview: VisualEffectSubview) -> UInt { - let blurryContainerViewController = self.mainFlutterWindow?.contentViewController as! BlurryContainerViewController; - return blurryContainerViewController.addVisualEffectSubview(visualEffectSubview) - } - - public static func getVisualEffectSubview(_ subviewId: UInt) -> VisualEffectSubview? { - let blurryContainerViewController = self.mainFlutterWindow?.contentViewController as! BlurryContainerViewController; - return blurryContainerViewController.getVisualEffectSubview(subviewId) - } - - public static func removeVisualEffectSubview(_ subviewId: UInt) { - let blurryContainerViewController = self.mainFlutterWindow?.contentViewController as! BlurryContainerViewController; - blurryContainerViewController.removeVisualEffectSubview(subviewId) - } - - public static func addToolbar() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - if #available(macOS 10.13, *) { - let newToolbar = NSToolbar() - - self.mainFlutterWindow!.toolbar = newToolbar - } - } - - public static func removeToolbar() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.toolbar = nil - } - - @available(macOS 11.0, *) - public static func setToolbarStyle(toolbarStyle: NSWindow.ToolbarStyle) { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.toolbarStyle = toolbarStyle - } - - public static func enableShadow() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.hasShadow = true - } - - public static func disableShadow() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.hasShadow = false - } - - public static func invalidateShadows() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.invalidateShadow() - } - - public static func addEmptyMaskImage() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - let blurryContainerViewController = self.mainFlutterWindow!.contentViewController as! BlurryContainerViewController; - (blurryContainerViewController.view as! NSVisualEffectView).maskImage = NSImage() - } - - public static func removeMaskImage() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - let blurryContainerViewController = self.mainFlutterWindow!.contentViewController as! BlurryContainerViewController; - (blurryContainerViewController.view as! NSVisualEffectView).maskImage = nil - } - - public static func ignoreMouseEvents() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.ignoresMouseEvents = true - } - - public static func acknowledgeMouseEvents() { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.ignoresMouseEvents = false - } - - @available(macOS 11.0, *) - public static func setSubtitle(_ subtitle: String) { - if (self.mainFlutterWindow == nil) { - printNotStartedWarning() - return - } - - self.mainFlutterWindow!.subtitle = subtitle - } -} diff --git a/macos/Classes/ToolbarStyleNameToEnumConverter.swift b/macos/Classes/ToolbarStyleNameToEnumConverter.swift deleted file mode 100644 index b08af0c..0000000 --- a/macos/Classes/ToolbarStyleNameToEnumConverter.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// ToolbarNameToEnumConverter.swift -// flutter_acrylic -// -// Created by Adrian Samoticha on 27.11.22. -// - -import Foundation - -class ToolbarStyleNameToEnumConverter { - @available(macOS 11.0, *) - public static func getToolbarStyleFromName(name: String) -> NSWindow.ToolbarStyle? { - switch (name) { - case "automatic": - return NSWindow.ToolbarStyle.automatic - - case "expanded": - return NSWindow.ToolbarStyle.expanded - - case "preference": - return NSWindow.ToolbarStyle.preference - - case "unified": - return NSWindow.ToolbarStyle.unified - - case "unifiedCompact": - return NSWindow.ToolbarStyle.unifiedCompact - - default: - return nil - } - } -} diff --git a/macos/Classes/VisualEffectSubview.swift b/macos/Classes/VisualEffectSubview.swift deleted file mode 100644 index 19d672c..0000000 --- a/macos/Classes/VisualEffectSubview.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// VisualEffectSubview.swift -// flutter_acrylic -// -// Created by Adrian Samoticha on 21.10.22. -// - -import Foundation - -public class VisualEffectSubview: NSVisualEffectView { - public override func hitTest(_ point: NSPoint) -> NSView? { - return nil - } -} diff --git a/macos/Classes/VisualEffectSubviewProperties.swift b/macos/Classes/VisualEffectSubviewProperties.swift deleted file mode 100644 index ca17df8..0000000 --- a/macos/Classes/VisualEffectSubviewProperties.swift +++ /dev/null @@ -1,137 +0,0 @@ -// -// VisualEffectSubviewProperties.swift -// flutter_acrylic -// -// Created by Adrian Samoticha on 22.10.22. -// - -import Foundation - -/// This class holds properties that can be applied to a VisualEffectSubview. -@available(macOS 10.14, *) -class VisualEffectSubviewProperties { - public let frameSize: NSSize? - public let frameOrigin: NSPoint? - public let alphaValue: CGFloat? - public let cornerRadius: CGFloat? - public let maskedCorners: CACornerMask? - public let material: NSVisualEffectView.Material? - public let state: NSVisualEffectView.State? - - init(frameSize: NSSize?, frameOrigin: NSPoint?, alphaValue: CGFloat?, cornerRadius: CGFloat?, maskedCorners: CACornerMask?, material: NSVisualEffectView.Material?, state: NSVisualEffectView.State?) { - self.frameSize = frameSize - self.frameOrigin = frameOrigin - self.alphaValue = alphaValue - self.cornerRadius = cornerRadius - self.maskedCorners = maskedCorners - self.material = material - self.state = state - } - - /// Decodes the args and returns an instance of CACornerMask if the “cornerMask” argument is not nil. Returns nil otherwise. - private static func getCornerMaskFromArgs(_ args: [String: Any]) -> CACornerMask? { - let cornerMaskArgument = args["cornerMask"] - - if (cornerMaskArgument == nil) { - return nil - } - - let cornerMaskInteger = cornerMaskArgument as! UInt - - let completeCornerMask = [ - CACornerMask.layerMinXMaxYCorner, - CACornerMask.layerMaxXMaxYCorner, - CACornerMask.layerMaxXMinYCorner, - CACornerMask.layerMinXMinYCorner - ] - let cornerMaskArray = [0, 1, 2, 3] - .filter {((cornerMaskInteger >> $0) & 1) == 1} - .map {completeCornerMask[$0]} - return CACornerMask(cornerMaskArray) - } - - /// Decodes the “effect” argument and returns the associated NSVisualEffectView.Material if it is not nil. Returns nil otherwise. - private static func getMaterialFromArgs(_ args: [String: Any]) -> NSVisualEffectView.Material? { - let effectArgument = args["effect"] - - if (effectArgument == nil) { - return nil - } - - let effectID = effectArgument as! NSNumber - return EffectIDToMaterialConverter.getMaterialFromEffectID(effectID: effectID) - } - - /// Decodes the “state” argument and returns the associated NSVisualEffectView.State if it is not nil. Returns nil otherwise. - private static func getStateFromArgs(_ args: [String: Any]) -> NSVisualEffectView.State? { - let stateArgument = args["state"] - - if (stateArgument == nil) { - return nil - } - - let stateString = stateArgument as! String - let state = stateString == "active" ? NSVisualEffectView.State.active : - stateString == "inactive" ? NSVisualEffectView.State.inactive : - NSVisualEffectView.State.followsWindowActiveState - return state; - } - - /// Produces a VisualEffectSubviewProperties instance from an argument dictionary. - public static func fromArgs(_ args: [String: Any]) -> VisualEffectSubviewProperties { - let frameSize = args["frameWidth"] != nil && args["frameHeight"] != nil ? NSSize( - width: args["frameWidth"] as! CGFloat, - height: args["frameHeight"] as! CGFloat - ) : nil - let frameOrigin = args["frameX"] != nil && args["frameY"] != nil ? NSPoint( - x: args["frameX"] as! CGFloat, - y: args["frameY"] as! CGFloat - ) : nil - let alphaValue = args["alphaValue"] as? CGFloat - let cornerRadius = args["cornerRadius"] as? CGFloat - let maskedCorners = getCornerMaskFromArgs(args) - let material = getMaterialFromArgs(args) - let state = getStateFromArgs(args) - - return VisualEffectSubviewProperties(frameSize: frameSize, frameOrigin: frameOrigin, alphaValue: alphaValue, cornerRadius: cornerRadius, maskedCorners: maskedCorners, material: material, state: state) - } - - /// Applies the stored properties to a provided VisualEffectSubview. - public func applyToVisualEffectSubview(_ visualEffectSubview: VisualEffectSubview) { - if (frameSize != nil) { - visualEffectSubview.setFrameSize(frameSize!) - } - - if (frameOrigin != nil) { - visualEffectSubview.setFrameOrigin(frameOrigin!) - } - - if (alphaValue != nil) { - visualEffectSubview.alphaValue = alphaValue! - } - - if (cornerRadius != nil) { - if (cornerRadius! != 0) { - visualEffectSubview.wantsLayer = true - visualEffectSubview.layer?.cornerRadius = cornerRadius! - } else { - if (visualEffectSubview.wantsLayer) { - visualEffectSubview.layer?.cornerRadius = cornerRadius! - } - } - } - - if (maskedCorners != nil) { - visualEffectSubview.wantsLayer = true - visualEffectSubview.layer?.maskedCorners = maskedCorners! - } - - if (material != nil) { - visualEffectSubview.material = material! - } - - if (state != nil) { - visualEffectSubview.state = state! - } - } -} diff --git a/macos/Classes/VisualEffectSubviewRegistry.swift b/macos/Classes/VisualEffectSubviewRegistry.swift deleted file mode 100644 index 12fcd39..0000000 --- a/macos/Classes/VisualEffectSubviewRegistry.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// VisualEffectSubviewHandler.swift -// flutter_acrylic -// -// Created by Adrian Samoticha on 22.10.22. -// - -import Foundation - -/// This class is used to register and retrieve VisualEffectSubviews. -class VisualEffectSubviewRegistry { - /// Maps subview IDs to subview instances. - private var idToSubview: [UInt: VisualEffectSubview] = [:] - - /// The ID value of the subview that is going to be registered next. - private var nextSubviewId: UInt = 0 - - /// Returns a new, yet unused ID value. - private func getNewId() -> UInt { - nextSubviewId += 1 - return nextSubviewId - 1 - } - - /// Registers a subview and returns its ID. - public func registerSubview(_ subview: VisualEffectSubview) -> UInt { - let subviewId = getNewId() - idToSubview[subviewId] = subview - return subviewId - } - - /// Deregisters a subview. - public func deregisterSubview(_ id: UInt) { - idToSubview.removeValue(forKey: id) - } - - /// Returns the subview with the given ID. Returns nil if the function is unused. - public func getSubviewFromId(_ subviewId: UInt) -> VisualEffectSubview? { - return idToSubview[subviewId] - } -} diff --git a/macos/flutter_acrylic.podspec b/macos/flutter_acrylic.podspec deleted file mode 100644 index 492ca61..0000000 --- a/macos/flutter_acrylic.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 flutter_acrylic.podspec` to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'flutter_acrylic' - s.version = '0.1.0' - s.summary = 'Flutter library for window acrylic, mica & transparency effects.' - s.description = <<-DESC - Flutter library for window acrylic, mica & transparency effects (Windows, macOS & Linux). - DESC - s.homepage = 'https://github.com/alexmercerind/flutter_acrylic' - s.license = { :file => '../LICENSE' } - s.author = { 'Adrian Samoticha' => 'adrian@samoticha.de' } - 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 \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 24c46bc..9aef014 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -53,6 +53,13 @@ packages: description: flutter source: sdk version: "0.0.0" + macos_window_utils: + dependency: "direct main" + description: + name: macos_window_utils + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" matcher: dependency: transitive description: @@ -136,5 +143,5 @@ packages: source: hosted version: "2.1.2" sdks: - dart: ">=2.17.0-0 <3.0.0" + dart: ">=2.18.5 <3.0.0" flutter: ">=1.20.0" diff --git a/pubspec.yaml b/pubspec.yaml index 9c31ce2..e7b2654 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_acrylic description: Window acrylic, mica & transparency effects for Flutter on Windows, macOS & Linux. -version: 1.0.0+2 +version: 1.1.0 homepage: https://github.com/alexmercerind/flutter_acrylic repository: https://github.com/alexmercerind/flutter_acrylic documentation: https://github.com/alexmercerind/flutter_acrylic/blob/master/README.md @@ -10,6 +10,7 @@ environment: flutter: ">=1.20.0" dependencies: + macos_window_utils: ^1.0.0 flutter: sdk: flutter @@ -30,8 +31,6 @@ flutter: platforms: windows: pluginClass: FlutterAcrylicPlugin - macos: - pluginClass: FlutterAcrylicPlugin linux: pluginClass: FlutterAcrylicPlugin diff --git a/test/widgets/visual_effect_subview_container/visual_effect_subview_container_property_storage_test.dart b/test/widgets/visual_effect_subview_container/visual_effect_subview_container_property_storage_test.dart index 778b87b..49a9337 100644 --- a/test/widgets/visual_effect_subview_container/visual_effect_subview_container_property_storage_test.dart +++ b/test/widgets/visual_effect_subview_container/visual_effect_subview_container_property_storage_test.dart @@ -21,9 +21,9 @@ void main() { frameY: 0.0, )); - // On the Swift side, the frame size is represented by an `NSSize` object. For this reason, - // even if only one metric changes, both metrics will need to be transmitted, so that an - // `NSSize` object can be created. + // On the Swift side, the frame size is represented by an `NSSize` object. + // For this reason, even if only one metric changes, both metrics will need + // to be transmitted, so that an `NSSize` object can be created. expect( delta0, VisualEffectSubviewProperties( @@ -61,8 +61,8 @@ void main() { }); testWidgets( - 'visual effect subview container property storage frame position change test', - (tester) async { + 'visual effect subview container property storage frame position change ' + 'test', (tester) async { final storage = VisualEffectSubviewContainerPropertyStorage(); storage.updateProperties(VisualEffectSubviewProperties( frameWidth: 1.0, @@ -78,9 +78,9 @@ void main() { frameY: 0.0, )); - // On the Swift side, the frame position is represented by an `NSPoint` object. For this reason, - // even if only one value changes, both values will need to be transmitted, so that an - // `NSPoint` object can be created. + // On the Swift side, the frame position is represented by an `NSPoint` + // object. For this reason, even if only one value changes, both values will + // need to be transmitted, so that an `NSPoint` object can be created. expect( delta0, VisualEffectSubviewProperties( @@ -118,8 +118,8 @@ void main() { }); testWidgets( - 'visual effect subview container property storage alpha value change test', - (tester) async { + 'visual effect subview container property storage alpha value change ' + 'test', (tester) async { final storage = VisualEffectSubviewContainerPropertyStorage(); storage.updateProperties(VisualEffectSubviewProperties( alphaValue: 1.0, diff --git a/test/widgets/visual_effect_subview_container/visual_effect_subview_container_resize_event_relay_test.dart b/test/widgets/visual_effect_subview_container/visual_effect_subview_container_resize_event_relay_test.dart index fd67aa7..d1e7c9f 100644 --- a/test/widgets/visual_effect_subview_container/visual_effect_subview_container_resize_event_relay_test.dart +++ b/test/widgets/visual_effect_subview_container/visual_effect_subview_container_resize_event_relay_test.dart @@ -3,16 +3,16 @@ import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets( - 'visual effect subview container resize event relay no resize event triggered', - (tester) async { + 'visual effect subview container resize event relay no resize event ' + 'triggered', (tester) async { final relay = VisualEffectSubviewContainerResizeEventRelay( disableUpdateOnBuild: false); relay.registerForceUpdateFunction(expectAsync0(() {}, count: 0)); }); testWidgets( - 'visual effect subview container resize event relay resize event triggered once', - (tester) async { + 'visual effect subview container resize event relay resize event ' + 'triggered once', (tester) async { final relay = VisualEffectSubviewContainerResizeEventRelay( disableUpdateOnBuild: false); relay.registerForceUpdateFunction(expectAsync0(() {}, count: 1)); @@ -20,8 +20,8 @@ void main() { }); testWidgets( - 'visual effect subview container resize event relay resize event triggered twice', - (tester) async { + 'visual effect subview container resize event relay resize event ' + 'triggered twice', (tester) async { final relay = VisualEffectSubviewContainerResizeEventRelay( disableUpdateOnBuild: false); relay.registerForceUpdateFunction(expectAsync0(() {}, count: 2));