@@ -188,6 +188,8 @@ const int _GDK_WINDOW_STATE_ICONIFIED = 1 << 1;
188188const int _GDK_WINDOW_STATE_MAXIMIZED = 1 << 2 ;
189189const int _GDK_WINDOW_STATE_FULLSCREEN = 1 << 4 ;
190190
191+ const int _GDK_WINDOW_TYPE_HINT_DIALOG = 1 ;
192+
191193/// Wraps GtkWindow
192194class _GtkWindow extends _GtkContainer {
193195 /// Create a new GtkWindow
@@ -198,6 +200,21 @@ class _GtkWindow extends _GtkContainer {
198200 _gtkWindowPresent (instance);
199201 }
200202
203+ /// Sets the parent window.
204+ void setTransientFor (_GtkWindow parent) {
205+ _gtkWindowSetTransientFor (instance, parent.instance);
206+ }
207+
208+ /// Set if this window is modal to its parent.
209+ void setModal (bool modal) {
210+ _gtkWindowSetModal (instance, modal);
211+ }
212+
213+ /// Set the type of this window.
214+ void setTypeHint (int hint) {
215+ _gtkWindowSetTypeHint (instance, hint);
216+ }
217+
201218 /// Sets the title of the window.
202219 void setTitle (String title) {
203220 final ffi.Pointer <ffi.Uint8 > titleBuffer = _stringToNative (title);
@@ -293,6 +310,24 @@ class _GtkWindow extends _GtkContainer {
293310 @ffi .Native <ffi.Void Function (ffi.Pointer <ffi.NativeType >)>(symbol: 'gtk_window_present' )
294311 external static void _gtkWindowPresent (ffi.Pointer <ffi.NativeType > window);
295312
313+ @ffi .Native <ffi.Void Function (ffi.Pointer <ffi.NativeType >, ffi.Bool )>(
314+ symbol: 'gtk_window_set_modal' ,
315+ )
316+ external static void _gtkWindowSetModal (ffi.Pointer <ffi.NativeType > window, bool modal);
317+
318+ @ffi .Native <ffi.Void Function (ffi.Pointer <ffi.NativeType >, ffi.Int )>(
319+ symbol: 'gtk_window_set_type_hint' ,
320+ )
321+ external static void _gtkWindowSetTypeHint (ffi.Pointer <ffi.NativeType > window, int hint);
322+
323+ @ffi .Native <ffi.Void Function (ffi.Pointer <ffi.NativeType >, ffi.Pointer <ffi.NativeType >)>(
324+ symbol: 'gtk_window_set_transient_for' ,
325+ )
326+ external static void _gtkWindowSetTransientFor (
327+ ffi.Pointer <ffi.NativeType > window,
328+ ffi.Pointer <ffi.NativeType > parent,
329+ );
330+
296331 @ffi .Native <ffi.Void Function (ffi.Pointer <ffi.NativeType >, ffi.Pointer <ffi.Uint8 >)>(
297332 symbol: 'gtk_window_set_title' ,
298333 )
@@ -512,6 +547,9 @@ class WindowingOwnerLinux extends WindowingOwner {
512547 );
513548 }
514549
550+ /// GTK windows keyed by view ID.
551+ final Map <int , _GtkWindow > _windows = < int , _GtkWindow > {};
552+
515553 @internal
516554 @override
517555 RegularWindowController createRegularWindowController ({
@@ -520,12 +558,15 @@ class WindowingOwnerLinux extends WindowingOwner {
520558 String ? title,
521559 required RegularWindowControllerDelegate delegate,
522560 }) {
523- return RegularWindowControllerLinux (
561+ final RegularWindowControllerLinux controller = RegularWindowControllerLinux (
562+ owner: this ,
524563 delegate: delegate,
525564 preferredSize: preferredSize,
526565 preferredConstraints: preferredConstraints,
527566 title: title,
528567 );
568+ _windows[controller.rootView.viewId] = controller._window;
569+ return controller;
529570 }
530571
531572 @internal
@@ -537,7 +578,16 @@ class WindowingOwnerLinux extends WindowingOwner {
537578 BaseWindowController ? parent,
538579 String ? title,
539580 }) {
540- throw UnimplementedError ('Dialog windows are not yet implemented on Linux.' );
581+ final DialogWindowControllerLinux controller = DialogWindowControllerLinux (
582+ owner: this ,
583+ delegate: delegate,
584+ preferredSize: preferredSize,
585+ preferredConstraints: preferredConstraints,
586+ parent: parent,
587+ title: title,
588+ );
589+ _windows[controller.rootView.viewId] = controller._window;
590+ return controller;
541591 }
542592}
543593
@@ -561,11 +611,13 @@ class RegularWindowControllerLinux extends RegularWindowController {
561611 /// * [RegularWindowController] , the base class for regular windows.
562612 @internal
563613 RegularWindowControllerLinux ({
614+ required WindowingOwnerLinux owner,
564615 required RegularWindowControllerDelegate delegate,
565616 Size ? preferredSize,
566617 BoxConstraints ? preferredConstraints,
567618 String ? title,
568- }) : _delegate = delegate,
619+ }) : _owner = owner,
620+ _delegate = delegate,
569621 _window = _GtkWindow (),
570622 super .empty () {
571623 if (! isWindowingEnabled) {
@@ -608,6 +660,7 @@ class RegularWindowControllerLinux extends RegularWindowController {
608660 _window.present ();
609661 }
610662
663+ final WindowingOwnerLinux _owner;
611664 final RegularWindowControllerDelegate _delegate;
612665 final _GtkWindow _window;
613666 late final _FlWindowMonitor _windowMonitor;
@@ -626,6 +679,7 @@ class RegularWindowControllerLinux extends RegularWindowController {
626679 _windowMonitor.close ();
627680 _windowMonitor.unref ();
628681 _destroyed = true ;
682+ _owner._windows.remove (rootView.viewId);
629683 }
630684
631685 @override
@@ -709,3 +763,163 @@ class RegularWindowControllerLinux extends RegularWindowController {
709763 }
710764 }
711765}
766+
767+ /// Implementation of [DialogWindowController] for the Linux platform.
768+ ///
769+ /// {@macro flutter.widgets.windowing.experimental}
770+ ///
771+ /// See also:
772+ ///
773+ /// * [DialogWindowController] , the base class for dialog windows.
774+ class DialogWindowControllerLinux extends DialogWindowController {
775+ /// Creates a new dialog window controller for Linux.
776+ ///
777+ /// When this constructor completes the native window has been created and
778+ /// has a view associated with it.
779+ ///
780+ /// {@macro flutter.widgets.windowing.experimental}
781+ ///
782+ /// See also:
783+ ///
784+ /// * [DialogWindowController] , the base class for dialog windows.
785+ @internal
786+ DialogWindowControllerLinux ({
787+ required WindowingOwnerLinux owner,
788+ required DialogWindowControllerDelegate delegate,
789+ Size ? preferredSize,
790+ BoxConstraints ? preferredConstraints,
791+ BaseWindowController ? parent,
792+ String ? title,
793+ }) : _owner = owner,
794+ _delegate = delegate,
795+ _parent = parent,
796+ _window = _GtkWindow (),
797+ super .empty () {
798+ if (! isWindowingEnabled) {
799+ throw UnsupportedError (_kWindowingDisabledErrorMessage);
800+ }
801+
802+ _window.setTypeHint (_GDK_WINDOW_TYPE_HINT_DIALOG );
803+ if (parent != null ) {
804+ final _GtkWindow ? parentWindow = owner._windows[parent.rootView.viewId];
805+ if (parentWindow != null ) {
806+ _window.setTransientFor (parentWindow);
807+ _window.setModal (true );
808+ }
809+ }
810+
811+ _windowMonitor = _FlWindowMonitor (
812+ _window,
813+ // onConfigure
814+ notifyListeners,
815+ // onStateChanged
816+ notifyListeners,
817+ // onIsActiveNotify
818+ notifyListeners,
819+ // onTitleNotify
820+ notifyListeners,
821+ // onClose
822+ () {
823+ _delegate.onWindowCloseRequested (this );
824+ },
825+ // onDestroy
826+ _delegate.onWindowDestroyed,
827+ );
828+ if (preferredSize != null ) {
829+ _window.setDefaultSize (preferredSize.width.toInt (), preferredSize.height.toInt ());
830+ }
831+ if (preferredConstraints != null ) {
832+ setConstraints (preferredConstraints);
833+ }
834+ if (title != null ) {
835+ setTitle (title);
836+ }
837+ final _FlView view = _FlView ();
838+ final int viewId = view.getId ();
839+ rootView = WidgetsBinding .instance.platformDispatcher.views.firstWhere (
840+ (FlutterView view) => view.viewId == viewId,
841+ );
842+ view.show ();
843+ _window.add (view);
844+ _window.present ();
845+ }
846+
847+ final WindowingOwnerLinux _owner;
848+ final DialogWindowControllerDelegate _delegate;
849+ final _GtkWindow _window;
850+ final BaseWindowController ? _parent;
851+ late final _FlWindowMonitor _windowMonitor;
852+ bool _destroyed = false ;
853+
854+ @override
855+ @internal
856+ Size get contentSize => _window.getSize ();
857+
858+ @override
859+ void destroy () {
860+ if (_destroyed) {
861+ return ;
862+ }
863+ _window.destroy ();
864+ _windowMonitor.close ();
865+ _windowMonitor.unref ();
866+ _destroyed = true ;
867+ _owner._windows.remove (rootView.viewId);
868+ }
869+
870+ @override
871+ @internal
872+ BaseWindowController ? get parent => _parent;
873+
874+ @override
875+ @internal
876+ String get title => _window.getTitle ();
877+
878+ @override
879+ @internal
880+ bool get isActivated => _window.isActive ();
881+
882+ @override
883+ @internal
884+ // NOTE: On Wayland this is never set, see https://gitlab.gnome.org/GNOME/gtk/-/issues/67
885+ bool get isMinimized => (_window.getWindow ().getState () & _GDK_WINDOW_STATE_ICONIFIED ) != 0 ;
886+
887+ @override
888+ @internal
889+ void setSize (Size size) {
890+ _window.resize (size.width.toInt (), size.height.toInt ());
891+ }
892+
893+ @override
894+ @internal
895+ void setConstraints (BoxConstraints constraints) {
896+ _window.setGeometryHints (
897+ minWidth: constraints.minWidth.toInt (),
898+ minHeight: constraints.minHeight.toInt (),
899+ maxWidth: constraints.maxWidth.isInfinite ? 0x7fffffff : constraints.maxWidth.toInt (),
900+ maxHeight: constraints.maxHeight.isInfinite ? 0x7fffffff : constraints.maxHeight.toInt (),
901+ );
902+ }
903+
904+ @override
905+ @internal
906+ void setTitle (String title) {
907+ _window.setTitle (title);
908+ }
909+
910+ @override
911+ @internal
912+ void activate () {
913+ _window.present ();
914+ }
915+
916+ @override
917+ @internal
918+ void setMinimized (bool minimized) {
919+ if (minimized) {
920+ _window.iconify ();
921+ } else {
922+ _window.deiconify ();
923+ }
924+ }
925+ }
0 commit comments