[gtk: 1/2] GDK W32: Ensure correct stacking of popup windows



commit 2ace3113d69db8bacb915161559ec91a4477f148
Author: Руслан Ижбулатов <lrn1986 gmail com>
Date:   Sat Jun 1 19:11:13 2019 +0000

    GDK W32: Ensure correct stacking of popup windows
    
    1) In the SetWindowPos() function (and the WINDOWPOS struct) the
       "hWndInsertAfter" argument/field means the window that will be
       directly above after the change, not the window that will be
       directly below. MSDN says "precedes" for SetWindowPos(), but
       WINDOWPOS documentation is more precise: this is the window
       behind which the affected window will be placed. Apparently,
       Z-axis goes back-to-front.
       Therefore, logging should be reworded correctly.
    
    2) When we switch away from the application and then switch back
       to a transient window, we need to bring up its transient-owner
       (and its transient-owner's owner and so forth) as well,
       otherwise our transient (modal) window might be transient for
       something that might not be visible.
    
    3) When we bring up a window, we should bring all of its children
       (popup windows) on top of it.
       Because Windows doesn't provide a function to bring one window
       on top of the other, we have to work around this by calling
       SetWindowPos() twice, swapping the windows between the calls.

 gdk/win32/gdkevents-win32.c | 68 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 63 insertions(+), 5 deletions(-)
---
diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c
index 1a8b86c335..8f0ef549d7 100644
--- a/gdk/win32/gdkevents-win32.c
+++ b/gdk/win32/gdkevents-win32.c
@@ -1646,6 +1646,27 @@ should_window_be_always_on_top (GdkSurface *window)
   return FALSE;
 }
 
+static void
+restack_children (GdkSurface *window)
+{
+  GList *popup;
+
+  for (popup = window->children; popup; popup = popup->next)
+    {
+      GdkSurface *child = GDK_SURFACE (popup->data);
+      /* Windows doesn't have a function to put window A *above* window B.
+       * Instead we put window A immediately *below* window B,
+       * then put window B immediately below window A.
+       * SWP_NOSENDCHANGING prevents our own handler from triggering.
+       */
+      SetWindowPos (GDK_SURFACE_HWND (child), GDK_SURFACE_HWND (window), 0, 0, 0, 0,
+                    SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING);
+      g_print ("Put popup %p (%p) above %p (%p)\n", child, GDK_SURFACE_HWND (child), window, 
GDK_SURFACE_HWND (window));
+      SetWindowPos (GDK_SURFACE_HWND (window), GDK_SURFACE_HWND (child), 0, 0, 0, 0,
+                    SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER);
+    }
+}
+
 static void
 ensure_stacking_on_unminimize (MSG *msg)
 {
@@ -1687,7 +1708,7 @@ ensure_stacking_on_unminimize (MSG *msg)
   if (lowest_transient != NULL)
     {
       GDK_NOTE (EVENTS,
-               g_print (" restacking %p above %p",
+               g_print (" restacking %p below %p",
                         msg->hwnd, lowest_transient));
       SetWindowPos (msg->hwnd, lowest_transient, 0, 0, 0, 0,
                    SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER);
@@ -1770,8 +1791,20 @@ ensure_stacking_on_activate_app (MSG       *msg,
       impl->type_hint == GDK_SURFACE_TYPE_HINT_DIALOG ||
       impl->transient_owner != NULL)
     {
+      GdkSurface *child = window;
+      GdkSurface *owner = impl->transient_owner;
+
       SetWindowPos (msg->hwnd, HWND_TOP, 0, 0, 0, 0,
                    SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER);
+      /* Bing the whole hierarchy of transients back up */
+      while (owner != NULL)
+        {
+          SetWindowPos (GDK_SURFACE_HWND (owner), GDK_SURFACE_HWND (child), 0, 0, 0, 0,
+                        SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER);
+          restack_children (owner);
+          child = owner;
+          owner = GDK_WIN32_SURFACE (owner)->transient_owner;
+        }
       return;
     }
 
@@ -1810,7 +1843,7 @@ ensure_stacking_on_activate_app (MSG       *msg,
           ((window_ontop && rover_ontop) || (!window_ontop && !rover_ontop)))
         {
          GDK_NOTE (EVENTS,
-                   g_print (" restacking %p above %p",
+                   g_print (" restacking %p below %p",
                             msg->hwnd, rover));
          SetWindowPos (msg->hwnd, rover, 0, 0, 0, 0,
                        SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER);
@@ -3090,7 +3123,11 @@ gdk_event_translate (MSG  *msg,
             transient-type windows. */
          if (!(old_state & GDK_SURFACE_STATE_ICONIFIED) &&
              (new_state & GDK_SURFACE_STATE_ICONIFIED))
-           ensure_stacking_on_unminimize (msg);
+            {
+             ensure_stacking_on_unminimize (msg);
+             restack_children (window);
+
+           }
        }
 
       /* Show, New size or position => configure event */
@@ -3430,7 +3467,8 @@ gdk_event_translate (MSG  *msg,
                                   (LOWORD (msg->wParam) == WA_INACTIVE ? "INACTIVE" : "???"))),
                                 HIWORD (msg->wParam) ? " minimized" : "",
                                 (HWND) msg->lParam));
-      if (window->surface_type == GDK_SURFACE_POPUP)
+      if (window->surface_type == GDK_SURFACE_POPUP ||
+          window->surface_type == GDK_SURFACE_TEMP)
         {
           /* Popups cannot be activated or de-activated - 
            * they only support keyboard focus, which GTK
@@ -3453,6 +3491,23 @@ gdk_event_translate (MSG  *msg,
          break;
        }
 
+      if (LOWORD (msg->wParam) == WA_INACTIVE && msg->lParam != 0)
+        {
+          GdkSurface *other_surface = gdk_win32_handle_table_lookup ((HWND) msg->lParam);
+          if (other_surface != NULL &&
+              (other_surface->surface_type == GDK_SURFACE_POPUP ||
+               other_surface->surface_type == GDK_SURFACE_TEMP))
+            {
+              /* We're being deactivated in favour of some popup or temp window.
+               * Since only toplevels can have visual focus, pretend that
+               * nothing happened.
+               */
+              *ret_valp = 0;
+              return_val = TRUE;
+              break;
+            }
+        }
+
       if (LOWORD (msg->wParam) == WA_INACTIVE)
        gdk_synthesize_surface_state (window, GDK_SURFACE_STATE_FOCUSED, 0);
       else
@@ -3472,7 +3527,10 @@ gdk_event_translate (MSG  *msg,
                                 msg->wParam ? "YES" : "NO",
                                 (gint64) msg->lParam));
       if (msg->wParam && GDK_SURFACE_IS_MAPPED (window))
-       ensure_stacking_on_activate_app (msg, window);
+       {
+         ensure_stacking_on_activate_app (msg, window);
+         restack_children (window);
+       }
       break;
     case WM_NCHITTEST:
       /* TODO: pass all messages to DwmDefWindowProc() first! */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]