From 89a351fd66cfb1ba7faa52b5ce6b9a158d052721 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 22 Feb 2022 18:41:41 -0800 Subject: [PATCH] macos: improve placement of windows This does some very basic window management so that we place surfaces in locations where they can actually be interacted with correctly. --- gdk/macos/gdkmacosdisplay-private.h | 4 + gdk/macos/gdkmacosdisplay-wm.c | 141 ++++++++++++++++++++++++++++ gdk/macos/gdkmacossurface.c | 19 ++-- gdk/macos/gdkmacostoplevelsurface.c | 40 ++------ gdk/macos/meson.build | 1 + 5 files changed, 163 insertions(+), 42 deletions(-) create mode 100644 gdk/macos/gdkmacosdisplay-wm.c diff --git a/gdk/macos/gdkmacosdisplay-private.h b/gdk/macos/gdkmacosdisplay-private.h index c25ff3b0c1..be2290b89a 100644 --- a/gdk/macos/gdkmacosdisplay-private.h +++ b/gdk/macos/gdkmacosdisplay-private.h @@ -174,6 +174,10 @@ void _gdk_macos_display_set_drag (GdkMacosDisp void _gdk_macos_display_set_drop (GdkMacosDisplay *self, NSInteger sequence_number, GdkDrop *drop); +void _gdk_macos_display_position_surface (GdkMacosDisplay *self, + GdkMacosSurface *surface, + int *x, + int *y); G_END_DECLS diff --git a/gdk/macos/gdkmacosdisplay-wm.c b/gdk/macos/gdkmacosdisplay-wm.c new file mode 100644 index 0000000000..a0dd6dddae --- /dev/null +++ b/gdk/macos/gdkmacosdisplay-wm.c @@ -0,0 +1,141 @@ +/* + * Copyright © 2022 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "gdkmacosdisplay-private.h" +#include "gdkmacosmonitor.h" +#include "gdkmacossurface-private.h" +#include "gdkmacostoplevelsurface-private.h" + +static void +_gdk_macos_display_position_toplevel_with_parent (GdkMacosDisplay *self, + GdkMacosSurface *surface, + GdkMacosSurface *parent, + int *x, + int *y) +{ + GdkRectangle surface_rect; + GdkRectangle parent_rect; + GdkRectangle workarea; + GdkMonitor *monitor; + + g_assert (GDK_IS_MACOS_DISPLAY (self)); + g_assert (GDK_IS_MACOS_TOPLEVEL_SURFACE (surface)); + g_assert (GDK_IS_MACOS_TOPLEVEL_SURFACE (parent)); + + /* If x/y is set, we should place relative to parent */ + if (GDK_SURFACE (surface)->x != 0 || GDK_SURFACE (surface)->y != 0) + { + *x = parent->root_x + GDK_SURFACE (surface)->x; + *y = parent->root_y + GDK_SURFACE (surface)->y; + return; + } + + /* Try to center on top of the parent but also try to make the whole thing + * visible in case that lands us under the topbar/panel/etc. + */ + surface_rect.x = surface->root_x + surface->shadow_left; + surface_rect.y = surface->root_y + surface->shadow_top; + surface_rect.width = GDK_SURFACE (surface)->width - surface->shadow_left - surface->shadow_right; + surface_rect.height = GDK_SURFACE (surface)->height - surface->shadow_top - surface->shadow_bottom; + + parent_rect.x = parent->root_x + surface->shadow_left; + parent_rect.y = parent->root_y + surface->shadow_top; + parent_rect.width = GDK_SURFACE (parent)->width - surface->shadow_left - surface->shadow_right; + parent_rect.height = GDK_SURFACE (parent)->height - surface->shadow_top - surface->shadow_bottom; + + /* Try to place centered atop parent */ + surface_rect.x = parent_rect.x + ((parent_rect.width - surface_rect.width) / 2); + surface_rect.y = parent_rect.y + ((parent_rect.height - surface_rect.height) / 2); + + /* Now make sure that we don't overlap the top-bar */ + monitor = _gdk_macos_surface_get_best_monitor (parent); + gdk_macos_monitor_get_workarea (monitor, &workarea); + + if (surface_rect.x < workarea.x) + surface_rect.x = workarea.x; + + if (surface_rect.y < workarea.y) + surface_rect.y = workarea.y; + + *x = surface_rect.x - surface->shadow_left; + *y = surface_rect.y - surface->shadow_top; +} + +static void +_gdk_macos_display_position_toplevel (GdkMacosDisplay *self, + GdkMacosSurface *surface, + int *x, + int *y) +{ + cairo_rectangle_int_t surface_rect; + GdkRectangle workarea; + GdkMonitor *monitor; + CGPoint mouse; + + g_assert (GDK_IS_MACOS_DISPLAY (self)); + g_assert (GDK_IS_MACOS_TOPLEVEL_SURFACE (surface)); + + mouse = [NSEvent mouseLocation]; + monitor = _gdk_macos_display_get_monitor_at_display_coords (self, mouse.x, mouse.y); + gdk_macos_monitor_get_workarea (monitor, &workarea); + + /* First place at top-left of current monitor */ + surface_rect.width = GDK_SURFACE (surface)->width - surface->shadow_left - surface->shadow_right; + surface_rect.height = GDK_SURFACE (surface)->height - surface->shadow_top - surface->shadow_bottom; + surface_rect.x = workarea.x + ((workarea.width - surface_rect.width) / 2); + surface_rect.y = workarea.y + ((workarea.height - surface_rect.height) / 2); + + if (surface_rect.x < workarea.x) + surface_rect.x = workarea.x; + + if (surface_rect.y < workarea.y) + surface_rect.y = workarea.y; + + /* TODO: If there is another window at this same position, perhaps we should move it */ + + *x = surface_rect.x - surface->shadow_left; + *y = surface_rect.y - surface->shadow_top; +} + +/* + * _gdk_macos_display_position_surface: + * + * Tries to position a window on a screen without landing in edges + * and other weird areas the user can't use. + */ +void +_gdk_macos_display_position_surface (GdkMacosDisplay *self, + GdkMacosSurface *surface, + int *x, + int *y) +{ + GdkSurface *transient_for; + + g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); + g_return_if_fail (GDK_IS_MACOS_TOPLEVEL_SURFACE (surface)); + + transient_for = GDK_SURFACE (surface)->transient_for; + + if (transient_for != NULL) + _gdk_macos_display_position_toplevel_with_parent (self, surface, GDK_MACOS_SURFACE (transient_for), x, y); + else + _gdk_macos_display_position_toplevel (self, surface, x, y); +} diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index ded5c19189..50e873a78d 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -746,15 +746,18 @@ _gdk_macos_surface_configure (GdkMacosSurface *self) content_rect.origin.y + content_rect.size.height, &self->root_x, &self->root_y); - if (surface->parent != NULL) + if (self->did_initial_present) { - surface->x = self->root_x - GDK_MACOS_SURFACE (surface->parent)->root_x; - surface->y = self->root_y - GDK_MACOS_SURFACE (surface->parent)->root_y; - } - else - { - surface->x = self->root_x; - surface->y = self->root_y; + if (surface->parent != NULL) + { + surface->x = self->root_x - GDK_MACOS_SURFACE (surface->parent)->root_x; + surface->y = self->root_y - GDK_MACOS_SURFACE (surface->parent)->root_y; + } + else + { + surface->x = self->root_x; + surface->y = self->root_y; + } } if (surface->width != content_rect.size.width || diff --git a/gdk/macos/gdkmacostoplevelsurface.c b/gdk/macos/gdkmacostoplevelsurface.c index 862eb8f1e9..27289787ca 100644 --- a/gdk/macos/gdkmacostoplevelsurface.c +++ b/gdk/macos/gdkmacostoplevelsurface.c @@ -194,43 +194,15 @@ _gdk_macos_toplevel_surface_present (GdkToplevel *toplevel, _gdk_macos_toplevel_surface_unfullscreen (self); } - if (GDK_SURFACE (self)->transient_for != NULL) + if (!GDK_MACOS_SURFACE (self)->did_initial_present) { - } - else - { - [nswindow setAnimationBehavior:NSWindowAnimationBehaviorDocumentWindow]; + int x = 0, y = 0; - if (!self->decorated && - !GDK_MACOS_SURFACE (self)->did_initial_present && - GDK_SURFACE (self)->x == 0 && - GDK_SURFACE (self)->y == 0 && - (GDK_MACOS_SURFACE (self)->shadow_left || - GDK_MACOS_SURFACE (self)->shadow_top)) - { - int x = GDK_SURFACE (self)->x; - int y = GDK_SURFACE (self)->y; + _gdk_macos_display_position_surface (GDK_MACOS_DISPLAY (display), + GDK_MACOS_SURFACE (self), + &x, &y); - monitor = _gdk_macos_surface_get_best_monitor (GDK_MACOS_SURFACE (self)); - - if (monitor != NULL) - { - GdkRectangle visible; - - gdk_macos_monitor_get_workarea (monitor, &visible); - - if (x < visible.x) - x = visible.x; - - if (y < visible.y) - y = visible.y; - } - - x -= GDK_MACOS_SURFACE (self)->shadow_left; - y -= GDK_MACOS_SURFACE (self)->shadow_top; - - _gdk_macos_surface_move (GDK_MACOS_SURFACE (self), x, y); - } + _gdk_macos_surface_move (GDK_MACOS_SURFACE (self), x, y); } _gdk_macos_surface_show (GDK_MACOS_SURFACE (self)); diff --git a/gdk/macos/meson.build b/gdk/macos/meson.build index 3a10dbf944..f3d9fbb336 100644 --- a/gdk/macos/meson.build +++ b/gdk/macos/meson.build @@ -9,6 +9,7 @@ gdk_macos_sources = files([ 'gdkmacosdisplay.c', 'gdkmacosdisplay-settings.c', 'gdkmacosdisplay-translate.c', + 'gdkmacosdisplay-wm.c', 'gdkmacosdrag.c', 'gdkmacosdrop.c', 'gdkmacosdragsurface.c',