diff --git a/examples/multi_window_ref_app/lib/app/main_window.dart b/examples/multi_window_ref_app/lib/app/main_window.dart index 062d4f98b02c6..0073ab0566c11 100644 --- a/examples/multi_window_ref_app/lib/app/main_window.dart +++ b/examples/multi_window_ref_app/lib/app/main_window.dart @@ -13,8 +13,8 @@ import 'regular_window_edit_dialog.dart'; class MainWindow extends StatefulWidget { MainWindow({super.key, required WindowController mainController}) { - _windowManagerModel.add( - KeyedWindowController(isMainWindow: true, key: UniqueKey(), controller: mainController)); + _windowManagerModel.add(KeyedWindowController( + isMainWindow: true, key: UniqueKey(), controller: mainController)); } final WindowManagerModel _windowManagerModel = WindowManagerModel(); @@ -38,7 +38,8 @@ class _MainWindowState extends State { flex: 60, child: SingleChildScrollView( scrollDirection: Axis.vertical, - child: _ActiveWindowsTable(windowManagerModel: widget._windowManagerModel), + child: _ActiveWindowsTable( + windowManagerModel: widget._windowManagerModel), ), ), Expanded( @@ -66,15 +67,18 @@ class _MainWindowState extends State { listenable: widget._windowManagerModel, builder: (BuildContext context, Widget? _) { final List childViews = []; - for (final KeyedWindowController controller in widget._windowManagerModel.windows) { + for (final KeyedWindowController controller + in widget._windowManagerModel.windows) { if (controller.parent == null && !controller.isMainWindow) { childViews.add(WindowControllerRender( controller: controller.controller, key: controller.key, windowSettings: widget._settings, windowManagerModel: widget._windowManagerModel, - onDestroyed: () => widget._windowManagerModel.remove(controller.key), - onError: () => widget._windowManagerModel.remove(controller.key), + onDestroyed: () => + widget._windowManagerModel.remove(controller.key), + onError: () => + widget._windowManagerModel.remove(controller.key), )); } } @@ -130,7 +134,8 @@ class _ActiveWindowsTable extends StatelessWidget { ), numeric: true), ], - rows: (windowManagerModel.windows).map((KeyedWindowController controller) { + rows: (windowManagerModel.windows) + .map((KeyedWindowController controller) { return DataRow( key: controller.key, color: WidgetStateColor.resolveWith((states) { @@ -142,8 +147,9 @@ class _ActiveWindowsTable extends StatelessWidget { selected: controller.controller == windowManagerModel.selected, onSelectChanged: (selected) { if (selected != null) { - windowManagerModel - .select(selected ? controller.controller.rootView.viewId : null); + windowManagerModel.select(selected + ? controller.controller.rootView.viewId + : null); } }, cells: [ @@ -151,40 +157,20 @@ class _ActiveWindowsTable extends StatelessWidget { DataCell( ListenableBuilder( listenable: controller.controller, - builder: (BuildContext context, Widget? _) => Text(controller - .controller.type - .toString() - .replaceFirst('WindowArchetype.', ''))), + builder: (BuildContext context, Widget? _) => Text( + controller.controller.type + .toString() + .replaceFirst('WindowArchetype.', ''))), ), DataCell( ListenableBuilder( listenable: controller.controller, - builder: (BuildContext context, Widget? _) => Row(children: [ + builder: (BuildContext context, Widget? _) => + Row(children: [ IconButton( - icon: const Icon(Icons.edit_outlined), - onPressed: () { - if (controller.controller.type == WindowArchetype.regular) { - showRegularWindowEditDialog( - context, - initialWidth: controller.controller.contentSize.width, - initialHeight: controller.controller.contentSize.height, - initialTitle: "", - onSave: (double? width, double? height, String? title) { - final regularController = - controller.controller as RegularWindowController; - if (width != null && height != null) { - regularController.updateContentSize( - WindowSizing(preferredSize: Size(width, height)), - ); - } - if (title != null) { - regularController.setTitle(title); - } - }, - ); - } - }, - ), + icon: const Icon(Icons.edit_outlined), + onPressed: () => _showWindowEditDialog( + controller, context)), IconButton( icon: const Icon(Icons.delete_outlined), onPressed: () async { @@ -199,6 +185,17 @@ class _ActiveWindowsTable extends StatelessWidget { ); }); } + + void _showWindowEditDialog( + KeyedWindowController controller, BuildContext context) { + if (controller.controller.type != WindowArchetype.regular) { + return; + } + + showRegularWindowEditDialog( + context: context, + controller: controller.controller as RegularWindowController); + } } class _WindowCreatorCard extends StatelessWidget { @@ -243,7 +240,8 @@ class _WindowCreatorCard extends StatelessWidget { onDestroyed: () => windowManagerModel.remove(key), ), title: "Regular", - contentSize: WindowSizing(preferredSize: windowSettings.regularSize), + contentSize: WindowSizing( + preferredSize: windowSettings.regularSize), ))); }, child: const Text('Regular'), diff --git a/examples/multi_window_ref_app/lib/app/regular_window_edit_dialog.dart b/examples/multi_window_ref_app/lib/app/regular_window_edit_dialog.dart index 638ff1d4f0745..64b2bca18a688 100644 --- a/examples/multi_window_ref_app/lib/app/regular_window_edit_dialog.dart +++ b/examples/multi_window_ref_app/lib/app/regular_window_edit_dialog.dart @@ -5,64 +5,180 @@ import 'package:flutter/material.dart'; void showRegularWindowEditDialog( - BuildContext context, { - double? initialWidth, - double? initialHeight, - String? initialTitle, - Function(double?, double?, String?)? onSave, -}) { - final TextEditingController widthController = - TextEditingController(text: initialWidth?.toString() ?? ''); - final TextEditingController heightController = - TextEditingController(text: initialHeight?.toString() ?? ''); - final TextEditingController titleController = TextEditingController(text: initialTitle ?? ''); - + {required BuildContext context, + required RegularWindowController controller}) { showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text("Edit Window Properties"), - content: StatefulBuilder( - builder: (context, setState) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - controller: widthController, - keyboardType: TextInputType.number, - decoration: InputDecoration(labelText: "Width"), - ), - TextField( - controller: heightController, - keyboardType: TextInputType.number, - decoration: InputDecoration(labelText: "Height"), - ), - TextField( - controller: titleController, - decoration: InputDecoration(labelText: "Title"), - ), - ], - ); - }, - ), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: Text("Cancel"), + context: context, + builder: (context) => _RegularWindowEditDialog( + controller: controller, onClose: () => Navigator.pop(context))); +} + +class _RegularWindowEditDialog extends StatefulWidget { + const _RegularWindowEditDialog( + {required this.controller, required this.onClose}); + + final RegularWindowController controller; + final VoidCallback onClose; + + @override + State createState() => _RegularWindowEditDialogState(); +} + +class _RegularWindowEditDialogState extends State<_RegularWindowEditDialog> { + late Size initialSize; + late String initialTitle; + late bool initialFullscreen; + late bool initialMaximized; + late bool initialMinimized; + + late final TextEditingController widthController; + late final TextEditingController heightController; + late final TextEditingController titleController; + + bool? nextIsFullscreen; + bool? nextIsMaximized; + bool? nextIsMinized; + + @override + void initState() { + super.initState(); + initialSize = widget.controller.contentSize; + initialTitle = ""; // TODO: Get the title + initialFullscreen = widget.controller.isFullscreen(); + initialMaximized = widget.controller.isMaximized(); + initialMinimized = widget.controller.isMinimized(); + + widthController = TextEditingController(text: initialSize.width.toString()); + heightController = + TextEditingController(text: initialSize.height.toString()); + titleController = TextEditingController(text: initialTitle); + + widget.controller.addListener(_onNotification); + } + + void _onNotification() { + // We listen on the state of the controller. If a value that the user + // can edit changes from what it was initially set to, we invalidate + // their current change and store the new "initial" value. + if (widget.controller.contentSize != initialSize) { + initialSize = widget.controller.contentSize; + widthController.text = widget.controller.contentSize.width.toString(); + heightController.text = widget.controller.contentSize.height.toString(); + } + if (widget.controller.isFullscreen() != initialFullscreen) { + setState(() { + initialFullscreen = widget.controller.isFullscreen(); + nextIsFullscreen = null; + }); + } + if (widget.controller.isMaximized() != initialMaximized) { + setState(() { + initialMaximized = widget.controller.isMaximized(); + nextIsMaximized = null; + }); + } + if (widget.controller.isMinimized() != initialMinimized) { + setState(() { + initialMinimized = widget.controller.isMinimized(); + nextIsMinized = null; + }); + } + } + + @override + void dispose() { + super.dispose(); + widget.controller.removeListener(_onNotification); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text("Edit Window Properties"), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: widthController, + keyboardType: TextInputType.number, + decoration: InputDecoration(labelText: "Width"), ), - TextButton( - onPressed: () { - double? width = double.tryParse(widthController.text); - double? height = double.tryParse(heightController.text); - String? title = titleController.text.isEmpty ? null : titleController.text; - - onSave?.call(width, height, title); - Navigator.of(context).pop(); - }, - child: Text("Save"), + TextField( + controller: heightController, + keyboardType: TextInputType.number, + decoration: InputDecoration(labelText: "Height"), ), + TextField( + controller: titleController, + decoration: InputDecoration(labelText: "Title"), + ), + CheckboxListTile( + title: const Text('Fullscreen'), + value: nextIsFullscreen ?? initialFullscreen, + onChanged: (bool? value) { + if (value != null) { + setState(() => nextIsFullscreen = value); + } + }), + CheckboxListTile( + title: const Text('Maximized'), + value: nextIsMaximized ?? initialMaximized, + onChanged: (bool? value) { + if (value != null) { + setState(() => nextIsMaximized = value); + } + }), + CheckboxListTile( + title: const Text('Minimized'), + value: nextIsMinized ?? initialMinimized, + onChanged: (bool? value) { + if (value != null) { + setState(() => nextIsMinized = value); + } + }) ], + ), + actions: [ + TextButton( + onPressed: () => widget.onClose(), + child: Text("Cancel"), + ), + TextButton( + onPressed: () => _onSave(), + child: Text("Save"), + ), + ], + ); + } + + void _onSave() { + double? width = double.tryParse(widthController.text); + double? height = double.tryParse(heightController.text); + String? title = titleController.text.isEmpty ? null : titleController.text; + if (width != null && height != null) { + widget.controller.updateContentSize( + WindowSizing(preferredSize: Size(width, height)), ); - }, - ); + } + if (title != null) { + widget.controller.setTitle(title); + } + if (nextIsFullscreen != null) { + if (widget.controller.isFullscreen() != nextIsFullscreen) { + widget.controller.setFullscreen(nextIsFullscreen!); + } + } + if (nextIsMaximized != null) { + if (widget.controller.isMaximized() != nextIsMaximized) { + widget.controller.setMaximized(nextIsMaximized!); + } + } + if (nextIsMinized != null) { + if (widget.controller.isMinimized() != nextIsMinized) { + widget.controller.setMinimized(nextIsMinized!); + } + } + + widget.onClose(); + } } diff --git a/packages/flutter/lib/src/widgets/window.dart b/packages/flutter/lib/src/widgets/window.dart index 0404d6d06e53d..3e290f9b6430d 100644 --- a/packages/flutter/lib/src/widgets/window.dart +++ b/packages/flutter/lib/src/widgets/window.dart @@ -165,7 +165,12 @@ abstract class RegularWindowController extends WindowController { /// [title] new title of the window. void setTitle(String title); - /// Requests the window to be maximized. This has not effect + /// Requests that the window be displayed in its current size and position. + /// If the window is minimized or maximized, the window returns to the size + /// and position that it had before that state was applied. + void activate(); + + /// Requests the window to be maximized. This has no effect /// if the window is currently full screen or minimized, but may /// affect the window size upon restoring it from minimized or /// full screen state. @@ -175,7 +180,7 @@ abstract class RegularWindowController extends WindowController { bool isMaximized(); /// Requests window to be minimized. - void minimize(); + void setMinimized(bool minimized); /// Returns whether window is currently minimized. bool isMinimized(); diff --git a/packages/flutter/lib/src/widgets/window_macos.dart b/packages/flutter/lib/src/widgets/window_macos.dart index 7a72439016525..0eee673f49adf 100644 --- a/packages/flutter/lib/src/widgets/window_macos.dart +++ b/packages/flutter/lib/src/widgets/window_macos.dart @@ -137,6 +137,11 @@ class RegularWindowControllerMacOS extends RegularWindowController { return Size(size.width, size.height); } + @override + void activate() { + // TODO: Implement + } + @override void setMaximized(bool maximized) { _ensureNotDestroyed(); @@ -150,9 +155,13 @@ class RegularWindowControllerMacOS extends RegularWindowController { } @override - void minimize() { + void setMinimized(bool minimized) { _ensureNotDestroyed(); - _minimize(getWindowHandle()); + if (minimized) { + _minimize(getWindowHandle()); + } else { + // TODO: Unminimize + } } @override diff --git a/packages/flutter/lib/src/widgets/window_win32.dart b/packages/flutter/lib/src/widgets/window_win32.dart index 4e4ba24c9fb2a..61bff21593275 100644 --- a/packages/flutter/lib/src/widgets/window_win32.dart +++ b/packages/flutter/lib/src/widgets/window_win32.dart @@ -143,15 +143,19 @@ class RegularWindowControllerWin32 extends RegularWindowController ffi.calloc.free(ffiSizing); } + @override + void activate() { + _ensureNotDestroyed(); + _showWindow(getWindowHandle(), SW_RESTORE); + } + @override bool isFullscreen() { return false; } - @override - void setFullscreen(bool fullscreen, {int? displayId}) { - - } + @override + void setFullscreen(bool fullscreen, {int? displayId}) {} @override bool isMaximized() { @@ -166,9 +170,13 @@ class RegularWindowControllerWin32 extends RegularWindowController } @override - void minimize() { + void setMinimized(bool minimized) { _ensureNotDestroyed(); - _showWindow(getWindowHandle(), SW_MINIMIZE); + if (minimized) { + _showWindow(getWindowHandle(), SW_MINIMIZE); + } else { + _showWindow(getWindowHandle(), SW_RESTORE); + } } @override