Skip to content

Commit a28acae

Browse files
Merge pull request #16 from Adrian-Samoticha/issue_14
Issue #14
2 parents 537d267 + 89609c8 commit a28acae

33 files changed

+2350
-158
lines changed

CHANGELOG.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,51 @@
1+
## 1.1.0
2+
- 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:
3+
- Managing Sheets
4+
- `windowWillBeginSheet`
5+
- `windowDidEndSheet`
6+
- Sizing Windows
7+
- `windowWillResize`
8+
- `windowDidResize`
9+
- `windowWillStartLiveResize`
10+
- `windowDidEndLiveResize`
11+
- Minimizing Windows
12+
- `windowWillMiniaturize`
13+
- `windowDidMiniaturize`
14+
- `windowDidDeminiaturize`
15+
- Zooming Window
16+
- `windowWillUseStandardFrame`
17+
- `windowShouldZoom`
18+
- Managing Full-Screen Presentation
19+
- `windowWillEnterFullScreen`
20+
- `windowDidEnterFullScreen`
21+
- `windowWillExitFullScreen`
22+
- `windowDidExitFullScreen`
23+
- Moving Windows
24+
- `windowWillMove`
25+
- `windowDidMove`
26+
- `windowDidChangeScreen`
27+
- `windowDidChangeScreenProfile`
28+
- `windowDidChangeBackingProperties`
29+
- Closing Windows
30+
- `windowShouldClose`
31+
- `windowWillClose`
32+
- Managing Key Status
33+
- `windowDidBecomeKey`
34+
- `windowDidResignKey`
35+
- Managing Main Status
36+
- `windowDidBecomeMain`
37+
- `windowDidResignMain`
38+
- Exposing Windows
39+
- `windowDidExpose`
40+
- Managing Occlusion State
41+
- `windowDidChangeOcclusionState`
42+
- Managing Presentation in Version Browsers
43+
- `windowWillEnterVersionBrowser`
44+
- `windowDidEnterVersionBrowser`
45+
- `windowWillExitVersionBrowser`
46+
- `windowDidExitVersionBrowser`
47+
- Add an `NSAppPresentationOptions` class that allows the window's fullscreen presentation options to be modified.
48+
149
## 1.0.2
250

351
- Fix incompatibility with Flutter 3.7.0.

README.md

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ and the Flutter guide for
1515

1616
## Features
1717

18-
**macos_window_utils** offers, among other things, the following features:
18+
**macos_window_utils** provides, among other things, the following features:
1919

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

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

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

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

157+
### Using `NSWindowDelegate`
158+
159+
`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:
160+
161+
```dart
162+
Future<void> main() async {
163+
WidgetsFlutterBinding.ensureInitialized();
164+
165+
// By default, enableWindowDelegate is set to false to ensure compatibility
166+
// with other plugins. Set it to true if you wish to use NSWindowDelegate.
167+
await WindowManipulator.initialize(enableWindowDelegate: true);
168+
runApp(MyApp());
169+
}
170+
```
171+
172+
Afterwards, create a class that extends it:
173+
174+
```dart
175+
class _MyDelegate extends NSWindowDelegate {
176+
@override
177+
void windowDidEnterFullScreen() {
178+
print('The window has entered fullscreen mode.');
179+
180+
super.windowDidEnterFullScreen();
181+
}
182+
}
183+
```
184+
185+
This class overrides the `NSWindowDelegate`'s `windowDidEnterFullScreen` method in order to respond to it.
186+
187+
The following methods are currently supported by `NSWindowDelegate`:
188+
<details>
189+
<summary>Supported methods</summary>
190+
191+
- Managing Sheets
192+
- `windowWillBeginSheet`
193+
- `windowDidEndSheet`
194+
- Sizing Windows
195+
- `windowWillResize`
196+
- `windowDidResize`
197+
- `windowWillStartLiveResize`
198+
- `windowDidEndLiveResize`
199+
- Minimizing Windows
200+
- `windowWillMiniaturize`
201+
- `windowDidMiniaturize`
202+
- `windowDidDeminiaturize`
203+
- Zooming Window
204+
- `windowWillUseStandardFrame`
205+
- `windowShouldZoom`
206+
- Managing Full-Screen Presentation
207+
- `windowWillEnterFullScreen`
208+
- `windowDidEnterFullScreen`
209+
- `windowWillExitFullScreen`
210+
- `windowDidExitFullScreen`
211+
- Moving Windows
212+
- `windowWillMove`
213+
- `windowDidMove`
214+
- `windowDidChangeScreen`
215+
- `windowDidChangeScreenProfile`
216+
- `windowDidChangeBackingProperties`
217+
- Closing Windows
218+
- `windowShouldClose`
219+
- `windowWillClose`
220+
- Managing Key Status
221+
- `windowDidBecomeKey`
222+
- `windowDidResignKey`
223+
- Managing Main Status
224+
- `windowDidBecomeMain`
225+
- `windowDidResignMain`
226+
- Exposing Windows
227+
- `windowDidExpose`
228+
- Managing Occlusion State
229+
- `windowDidChangeOcclusionState`
230+
- Managing Presentation in Version Browsers
231+
- `windowWillEnterVersionBrowser`
232+
- `windowDidEnterVersionBrowser`
233+
- `windowWillExitVersionBrowser`
234+
- `windowDidExitVersionBrowser`
235+
236+
</details>
237+
238+
<br>
239+
240+
Then, add an instance of it via the `WindowManipulator.addNSWindowDelegate` method:
241+
242+
```dart
243+
final delegate = _MyDelegate();
244+
final handle = WindowManipulator.addNSWindowDelegate(delegate);
245+
```
246+
247+
`WindowManipulator.addNSWindowDelegate` returns a `NSWindowDelegateHandle` which can be used to remove this `NSWindowDelegate` again later:
248+
249+
```dart
250+
handle.removeFromHandler();
251+
```
252+
253+
### Using `NSAppPresentationOptions`
254+
255+
Say we would like to automatically hide the toolbar when the window is in fullscreen mode. Using `NSAppPresentationOptions` this can be done as follows:
256+
257+
```dart
258+
// Create NSAppPresentationOptions instance.
259+
final options = NSAppPresentationOptions.from({
260+
// fullScreen needs to be present as a fullscreen presentation option at all
261+
// times.
262+
NSAppPresentationOption.fullScreen,
263+
264+
// Hide the toolbar automatically in fullscreen mode.
265+
NSAppPresentationOption.autoHideToolbar,
266+
267+
// autoHideToolbar must be accompanied by autoHideMenuBar.
268+
NSAppPresentationOption.autoHideMenuBar,
269+
270+
// autoHideMenuBar must be accompanied by either autoHideDock or hideDock.
271+
NSAppPresentationOption.autoHideDock,
272+
});
273+
274+
// Apply the options as fullscreen presentation options.
275+
options.applyAsFullScreenPresentationOptions();
276+
```
277+
278+
**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.
279+
155280
## License
156281

157282
MIT License

example/lib/main.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import 'package:macos_window_utils/macos_window_utils.dart';
66

77
void main() async {
88
WidgetsFlutterBinding.ensureInitialized();
9-
await WindowManipulator.initialize();
9+
await WindowManipulator.initialize(enableWindowDelegate: true);
1010
runApp(const MyApp());
1111
}
1212

Lines changed: 48 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1+
import 'package:example/main_area/ns_window_delegate_demo/ns_window_delegate_demo.dart';
2+
import 'package:example/main_area/window_manipulator_demo/window_manipulator_demo.dart';
13
import 'package:flutter/cupertino.dart';
2-
import 'package:flutter/services.dart';
3-
4-
import 'command_list/command_list.dart';
5-
import 'command_list_provider.dart';
4+
import 'package:flutter/material.dart';
5+
import 'package:macos_ui/macos_ui.dart';
66

77
class MainArea extends StatefulWidget {
8-
const MainArea({
9-
Key? key,
10-
required this.setState,
11-
}) : super(key: key);
8+
const MainArea({super.key, required this.setState});
129

1310
final void Function(void Function()) setState;
1411

@@ -17,88 +14,58 @@ class MainArea extends StatefulWidget {
1714
}
1815

1916
class _MainAreaState extends State<MainArea> {
20-
final FocusNode _focusNode = FocusNode();
21-
String _searchTerm = '';
22-
int _selectedIndex = 0;
23-
24-
List<Command> get _filteredCommands => CommandListProvider.getCommands()
25-
.where((Command command) =>
26-
command.name.toLowerCase().contains(_searchTerm.toLowerCase()))
27-
.toList();
17+
final _tabController = MacosTabController(length: 2, initialIndex: 0);
2818

29-
void _setSelectedIndex(int newIndex) {
30-
if (_filteredCommands.isEmpty) {
31-
_selectedIndex = 0;
32-
return;
33-
}
34-
35-
_selectedIndex = newIndex.clamp(0, _filteredCommands.length - 1);
36-
}
19+
@override
20+
void initState() {
21+
_tabController.addListener(() => setState(() {}));
3722

38-
void _addToSelectedIndex(int value) {
39-
_setSelectedIndex(_selectedIndex + value);
23+
super.initState();
4024
}
4125

4226
@override
4327
Widget build(BuildContext context) {
44-
return Focus(
45-
focusNode: _focusNode,
46-
autofocus: true,
47-
onKeyEvent: (FocusNode _, KeyEvent event) {
48-
if (event is KeyDownEvent || event is KeyRepeatEvent) {
49-
if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
50-
setState(() {
51-
_addToSelectedIndex(1);
52-
});
53-
54-
return KeyEventResult.handled;
55-
}
56-
57-
if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
58-
widget.setState(() {
59-
_addToSelectedIndex(-1);
60-
});
61-
62-
return KeyEventResult.handled;
63-
}
64-
}
28+
return Column(
29+
children: [
30+
const SizedBox(height: 8.0),
31+
_SegmentedControl(
32+
tabController: _tabController,
33+
),
34+
Expanded(
35+
child: IndexedStack(
36+
index: _tabController.index,
37+
children: [
38+
WindowManipulatorDemo(
39+
setState: widget.setState,
40+
),
41+
const NSWindowDelegateDemo(),
42+
],
43+
),
44+
),
45+
],
46+
);
47+
}
48+
}
6549

66-
if (event is KeyDownEvent) {
67-
if (event.logicalKey == LogicalKeyboardKey.enter) {
68-
setState(() {
69-
final commands = _filteredCommands;
70-
final selectedCommand = commands[_selectedIndex];
71-
selectedCommand.function();
72-
});
50+
class _SegmentedControl extends StatelessWidget {
51+
const _SegmentedControl({
52+
required this.tabController,
53+
});
7354

74-
return KeyEventResult.handled;
75-
}
76-
}
55+
final MacosTabController tabController;
7756

78-
return KeyEventResult.ignored;
79-
},
80-
child: Column(
81-
children: [
82-
Padding(
83-
padding: const EdgeInsets.all(8.0),
84-
child: CupertinoSearchTextField(
85-
onChanged: (value) => setState(() {
86-
_setSelectedIndex(0);
87-
_searchTerm = value;
88-
}),
89-
),
90-
),
91-
Expanded(
92-
child: CommandList(
93-
selectedIndex: _selectedIndex,
94-
commands: _filteredCommands,
95-
setIndex: (int newIndex) => setState(() {
96-
_setSelectedIndex(newIndex);
97-
}),
98-
),
99-
),
100-
],
101-
),
57+
@override
58+
Widget build(BuildContext context) {
59+
return MacosSegmentedControl(
60+
tabs: const [
61+
MacosTab(
62+
label: 'WindowManipulator demo',
63+
),
64+
MacosTab(
65+
label: 'NSWindowDelegate demo',
66+
),
67+
],
68+
controller: tabController,
10269
);
10370
}
10471
}

0 commit comments

Comments
 (0)