Skip to content

Commit

Permalink
Merge pull request #16 from Adrian-Samoticha/issue_14
Browse files Browse the repository at this point in the history
Issue #14
  • Loading branch information
Adrian-Samoticha authored Feb 28, 2023
2 parents 537d267 + 89609c8 commit a28acae
Show file tree
Hide file tree
Showing 33 changed files with 2,350 additions and 158 deletions.
48 changes: 48 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,51 @@
## 1.1.0
- Add an abstract `NSWindowDelegate` that can be used to listen to events provided by [NSWindowDelegate](https://developer.apple.com/documentation/appkit/nswindowdelegate) such as window resizing, moving, exposing, and minimizing. The following methods are currently supported:
- Managing Sheets
- `windowWillBeginSheet`
- `windowDidEndSheet`
- Sizing Windows
- `windowWillResize`
- `windowDidResize`
- `windowWillStartLiveResize`
- `windowDidEndLiveResize`
- Minimizing Windows
- `windowWillMiniaturize`
- `windowDidMiniaturize`
- `windowDidDeminiaturize`
- Zooming Window
- `windowWillUseStandardFrame`
- `windowShouldZoom`
- Managing Full-Screen Presentation
- `windowWillEnterFullScreen`
- `windowDidEnterFullScreen`
- `windowWillExitFullScreen`
- `windowDidExitFullScreen`
- Moving Windows
- `windowWillMove`
- `windowDidMove`
- `windowDidChangeScreen`
- `windowDidChangeScreenProfile`
- `windowDidChangeBackingProperties`
- Closing Windows
- `windowShouldClose`
- `windowWillClose`
- Managing Key Status
- `windowDidBecomeKey`
- `windowDidResignKey`
- Managing Main Status
- `windowDidBecomeMain`
- `windowDidResignMain`
- Exposing Windows
- `windowDidExpose`
- Managing Occlusion State
- `windowDidChangeOcclusionState`
- Managing Presentation in Version Browsers
- `windowWillEnterVersionBrowser`
- `windowDidEnterVersionBrowser`
- `windowWillExitVersionBrowser`
- `windowDidExitVersionBrowser`
- Add an `NSAppPresentationOptions` class that allows the window's fullscreen presentation options to be modified.

## 1.0.2

- Fix incompatibility with Flutter 3.7.0.
Expand Down
127 changes: 126 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ and the Flutter guide for

## Features

**macos_window_utils** offers, among other things, the following features:
**macos_window_utils** provides, among other things, the following features:

+ Methods to set the application window's material.
+ Methods to enter/exit fullscreen mode or (un)zoom the window.
Expand All @@ -33,6 +33,8 @@ and the Flutter guide for
+ Methods and widgets to add, remove, and modify visual effect subviews.
+ Methods to set the window's level as well as reorder the window within its level.
+ Methods to modify the window's style mask.
+ An abstract `NSWindowDelegate` class that can be used detect `NSWindow` events, such as window resizing, moving, exposing, and minimizing.
+ An `NSAppPresentationOptions` class that allows modifications to the window's fullscreen presentation options.

Additionally, the package ships with an example project that showcases the plugin's features via an intuitive searchable user interface:

Expand Down Expand Up @@ -152,6 +154,129 @@ Future<void> main() async {

Afterwards, call any method of the `WindowManipulator` class to manipulate your application's window.

### Using `NSWindowDelegate`

`NSWindowDelegate` can be used to listen to `NSWindow` events, such as window resizing, moving, exposing, and minimizing. To use it, first make sure that `enableWindowDelegate` is set to `true` in your `WindowManipulator.initialize` call:

```dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// By default, enableWindowDelegate is set to false to ensure compatibility
// with other plugins. Set it to true if you wish to use NSWindowDelegate.
await WindowManipulator.initialize(enableWindowDelegate: true);
runApp(MyApp());
}
```

Afterwards, create a class that extends it:

```dart
class _MyDelegate extends NSWindowDelegate {
@override
void windowDidEnterFullScreen() {
print('The window has entered fullscreen mode.');
super.windowDidEnterFullScreen();
}
}
```

This class overrides the `NSWindowDelegate`'s `windowDidEnterFullScreen` method in order to respond to it.

The following methods are currently supported by `NSWindowDelegate`:
<details>
<summary>Supported methods</summary>

- Managing Sheets
- `windowWillBeginSheet`
- `windowDidEndSheet`
- Sizing Windows
- `windowWillResize`
- `windowDidResize`
- `windowWillStartLiveResize`
- `windowDidEndLiveResize`
- Minimizing Windows
- `windowWillMiniaturize`
- `windowDidMiniaturize`
- `windowDidDeminiaturize`
- Zooming Window
- `windowWillUseStandardFrame`
- `windowShouldZoom`
- Managing Full-Screen Presentation
- `windowWillEnterFullScreen`
- `windowDidEnterFullScreen`
- `windowWillExitFullScreen`
- `windowDidExitFullScreen`
- Moving Windows
- `windowWillMove`
- `windowDidMove`
- `windowDidChangeScreen`
- `windowDidChangeScreenProfile`
- `windowDidChangeBackingProperties`
- Closing Windows
- `windowShouldClose`
- `windowWillClose`
- Managing Key Status
- `windowDidBecomeKey`
- `windowDidResignKey`
- Managing Main Status
- `windowDidBecomeMain`
- `windowDidResignMain`
- Exposing Windows
- `windowDidExpose`
- Managing Occlusion State
- `windowDidChangeOcclusionState`
- Managing Presentation in Version Browsers
- `windowWillEnterVersionBrowser`
- `windowDidEnterVersionBrowser`
- `windowWillExitVersionBrowser`
- `windowDidExitVersionBrowser`

</details>

<br>

Then, add an instance of it via the `WindowManipulator.addNSWindowDelegate` method:

```dart
final delegate = _MyDelegate();
final handle = WindowManipulator.addNSWindowDelegate(delegate);
```

`WindowManipulator.addNSWindowDelegate` returns a `NSWindowDelegateHandle` which can be used to remove this `NSWindowDelegate` again later:

```dart
handle.removeFromHandler();
```

### Using `NSAppPresentationOptions`

Say we would like to automatically hide the toolbar when the window is in fullscreen mode. Using `NSAppPresentationOptions` this can be done as follows:

```dart
// Create NSAppPresentationOptions instance.
final options = NSAppPresentationOptions.from({
// fullScreen needs to be present as a fullscreen presentation option at all
// times.
NSAppPresentationOption.fullScreen,
// Hide the toolbar automatically in fullscreen mode.
NSAppPresentationOption.autoHideToolbar,
// autoHideToolbar must be accompanied by autoHideMenuBar.
NSAppPresentationOption.autoHideMenuBar,
// autoHideMenuBar must be accompanied by either autoHideDock or hideDock.
NSAppPresentationOption.autoHideDock,
});
// Apply the options as fullscreen presentation options.
options.applyAsFullScreenPresentationOptions();
```

**Note:** `NSAppPresentationOptions` uses the `NSWindow`'s delegate to change the window's fullscreen presentation options. Therefore, `enableWindowDelegate` needs to be set to `true` in your `WindowManipulator.initialize` call for it to work.

## License

MIT License
2 changes: 1 addition & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'package:macos_window_utils/macos_window_utils.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();
await WindowManipulator.initialize();
await WindowManipulator.initialize(enableWindowDelegate: true);
runApp(const MyApp());
}

Expand Down
129 changes: 48 additions & 81 deletions example/lib/main_area/main_area.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import 'package:example/main_area/ns_window_delegate_demo/ns_window_delegate_demo.dart';
import 'package:example/main_area/window_manipulator_demo/window_manipulator_demo.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';

import 'command_list/command_list.dart';
import 'command_list_provider.dart';
import 'package:flutter/material.dart';
import 'package:macos_ui/macos_ui.dart';

class MainArea extends StatefulWidget {
const MainArea({
Key? key,
required this.setState,
}) : super(key: key);
const MainArea({super.key, required this.setState});

final void Function(void Function()) setState;

Expand All @@ -17,88 +14,58 @@ class MainArea extends StatefulWidget {
}

class _MainAreaState extends State<MainArea> {
final FocusNode _focusNode = FocusNode();
String _searchTerm = '';
int _selectedIndex = 0;

List<Command> get _filteredCommands => CommandListProvider.getCommands()
.where((Command command) =>
command.name.toLowerCase().contains(_searchTerm.toLowerCase()))
.toList();
final _tabController = MacosTabController(length: 2, initialIndex: 0);

void _setSelectedIndex(int newIndex) {
if (_filteredCommands.isEmpty) {
_selectedIndex = 0;
return;
}

_selectedIndex = newIndex.clamp(0, _filteredCommands.length - 1);
}
@override
void initState() {
_tabController.addListener(() => setState(() {}));

void _addToSelectedIndex(int value) {
_setSelectedIndex(_selectedIndex + value);
super.initState();
}

@override
Widget build(BuildContext context) {
return Focus(
focusNode: _focusNode,
autofocus: true,
onKeyEvent: (FocusNode _, KeyEvent event) {
if (event is KeyDownEvent || event is KeyRepeatEvent) {
if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
setState(() {
_addToSelectedIndex(1);
});

return KeyEventResult.handled;
}

if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
widget.setState(() {
_addToSelectedIndex(-1);
});

return KeyEventResult.handled;
}
}
return Column(
children: [
const SizedBox(height: 8.0),
_SegmentedControl(
tabController: _tabController,
),
Expanded(
child: IndexedStack(
index: _tabController.index,
children: [
WindowManipulatorDemo(
setState: widget.setState,
),
const NSWindowDelegateDemo(),
],
),
),
],
);
}
}

if (event is KeyDownEvent) {
if (event.logicalKey == LogicalKeyboardKey.enter) {
setState(() {
final commands = _filteredCommands;
final selectedCommand = commands[_selectedIndex];
selectedCommand.function();
});
class _SegmentedControl extends StatelessWidget {
const _SegmentedControl({
required this.tabController,
});

return KeyEventResult.handled;
}
}
final MacosTabController tabController;

return KeyEventResult.ignored;
},
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: CupertinoSearchTextField(
onChanged: (value) => setState(() {
_setSelectedIndex(0);
_searchTerm = value;
}),
),
),
Expanded(
child: CommandList(
selectedIndex: _selectedIndex,
commands: _filteredCommands,
setIndex: (int newIndex) => setState(() {
_setSelectedIndex(newIndex);
}),
),
),
],
),
@override
Widget build(BuildContext context) {
return MacosSegmentedControl(
tabs: const [
MacosTab(
label: 'WindowManipulator demo',
),
MacosTab(
label: 'NSWindowDelegate demo',
),
],
controller: tabController,
);
}
}
Loading

0 comments on commit a28acae

Please sign in to comment.