compositor: Mirror window placement in RTL environment

If RTL environment (such as Hebrew or Arabic) is detected, the usual new
window placement algorithm is mirrored. New windows appear near the
right side of the screen and cascading is built from the top-right
corner of the screen.

Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1873
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3009>
This commit is contained in:
Shmuel Melamud 2023-05-18 03:39:38 +03:00 committed by Marge Bot
parent 985d0dc3ab
commit 993c42e11f

View File

@ -45,7 +45,8 @@ typedef enum
} MetaWindowDirection; } MetaWindowDirection;
static gint static gint
northwestcmp (gconstpointer a, gconstpointer b) northwest_cmp (gconstpointer a,
gconstpointer b)
{ {
MetaWindow *aw = (gpointer) a; MetaWindow *aw = (gpointer) a;
MetaWindow *bw = (gpointer) b; MetaWindow *bw = (gpointer) b;
@ -74,6 +75,39 @@ northwestcmp (gconstpointer a, gconstpointer b)
return 0; return 0;
} }
static gint
northeast_cmp (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
MetaWindow *aw = (gpointer) a;
MetaWindow *bw = (gpointer) b;
MetaRectangle *area = user_data;
MetaRectangle a_frame;
MetaRectangle b_frame;
int from_origin_a;
int from_origin_b;
int ax, ay, bx, by;
meta_window_get_frame_rect (aw, &a_frame);
meta_window_get_frame_rect (bw, &b_frame);
ax = (area->x + area->width) - (a_frame.x + a_frame.width);
ay = a_frame.y;
bx = (area->x + area->width) - (b_frame.x + b_frame.width);
by = b_frame.y;
/* probably there's a fast good-enough-guess we could use here. */
from_origin_a = sqrt (ax * ax + ay * ay);
from_origin_b = sqrt (bx * bx + by * by);
if (from_origin_a < from_origin_b)
return -1;
else if (from_origin_a > from_origin_b)
return 1;
else
return 0;
}
static void static void
find_next_cascade (MetaWindow *window, find_next_cascade (MetaWindow *window,
/* visible windows on relevant workspaces */ /* visible windows on relevant workspaces */
@ -88,7 +122,7 @@ find_next_cascade (MetaWindow *window,
MetaBackend *backend = meta_context_get_backend (context); MetaBackend *backend = meta_context_get_backend (context);
GList *tmp; GList *tmp;
GList *sorted; GList *sorted;
int cascade_x, cascade_y; int cascade_origin_x, cascade_x, cascade_y;
MetaRectangle titlebar_rect; MetaRectangle titlebar_rect;
int x_threshold, y_threshold; int x_threshold, y_threshold;
MetaRectangle frame_rect; MetaRectangle frame_rect;
@ -96,9 +130,7 @@ find_next_cascade (MetaWindow *window,
int cascade_stage; int cascade_stage;
MetaRectangle work_area; MetaRectangle work_area;
MetaLogicalMonitor *current; MetaLogicalMonitor *current;
gboolean ltr = meta_get_locale_direction () == META_LOCALE_DIRECTION_LTR;
sorted = g_list_copy (windows);
sorted = g_list_sort (sorted, northwestcmp);
/* This is a "fuzzy" cascade algorithm. /* This is a "fuzzy" cascade algorithm.
* For each window in the list, we find where we'd cascade a * For each window in the list, we find where we'd cascade a
@ -122,57 +154,81 @@ find_next_cascade (MetaWindow *window,
current = meta_backend_get_current_logical_monitor (backend); current = meta_backend_get_current_logical_monitor (backend);
meta_window_get_work_area_for_logical_monitor (window, current, &work_area); meta_window_get_work_area_for_logical_monitor (window, current, &work_area);
cascade_x = MAX (0, work_area.x); sorted = g_list_copy (windows);
cascade_y = MAX (0, work_area.y); if (ltr)
sorted = g_list_sort (sorted, northwest_cmp);
/* Find first cascade position that's not used. */ else
sorted = g_list_sort_with_data (sorted, northeast_cmp, &work_area);
meta_window_get_frame_rect (window, &frame_rect); meta_window_get_frame_rect (window, &frame_rect);
window_width = frame_rect.width; window_width = frame_rect.width;
window_height = frame_rect.height; window_height = frame_rect.height;
cascade_origin_x = ltr
? MAX (0, work_area.x)
: work_area.x + work_area.width - window_width;
cascade_x = cascade_origin_x;
cascade_y = MAX (0, work_area.y);
/* Find first cascade position that's not used. */
cascade_stage = 0; cascade_stage = 0;
tmp = sorted; tmp = sorted;
while (tmp != NULL) while (tmp != NULL)
{ {
MetaWindow *w; MetaWindow *w;
MetaRectangle w_frame_rect; MetaRectangle w_frame_rect;
int wx, wy; int wx, ww, wy;
gboolean nearby;
w = tmp->data; w = tmp->data;
/* we want frame position, not window position */ /* we want frame position, not window position */
meta_window_get_frame_rect (w, &w_frame_rect); meta_window_get_frame_rect (w, &w_frame_rect);
wx = w_frame_rect.x; wx = w_frame_rect.x;
ww = w_frame_rect.width;
wy = w_frame_rect.y; wy = w_frame_rect.y;
if (ABS (wx - cascade_x) < x_threshold && if (ltr)
ABS (wy - cascade_y) < y_threshold) nearby = ABS (wx - cascade_x) < x_threshold &&
ABS (wy - cascade_y) < y_threshold;
else
nearby = ABS ((wx + ww) - (cascade_x + window_width)) < x_threshold &&
ABS (wy - cascade_y) < y_threshold;
if (nearby)
{ {
meta_window_get_titlebar_rect (w, &titlebar_rect); meta_window_get_titlebar_rect (w, &titlebar_rect);
/* Cascade the window evenly by the titlebar height; this isn't a typo. */ /* Cascade the window evenly by the titlebar height; this isn't a typo. */
cascade_x = wx + titlebar_rect.height; cascade_x = ltr
? wx + titlebar_rect.height
: wx + ww - titlebar_rect.height - window_width;
cascade_y = wy + titlebar_rect.height; cascade_y = wy + titlebar_rect.height;
/* If we go off the screen, start over with a new cascade */ /* If we go off the screen, start over with a new cascade */
if (((cascade_x + window_width) > if (((cascade_x + window_width) >
(work_area.x + work_area.width)) || (work_area.x + work_area.width)) ||
(cascade_x < work_area.x) ||
((cascade_y + window_height) > ((cascade_y + window_height) >
(work_area.y + work_area.height))) (work_area.y + work_area.height)))
{ {
cascade_x = MAX (0, work_area.x); cascade_x = cascade_origin_x;
cascade_y = MAX (0, work_area.y); cascade_y = MAX (0, work_area.y);
#define CASCADE_INTERVAL 50 /* space between top-left corners of cascades */ #define CASCADE_INTERVAL 50 /* space between top-left corners of cascades */
cascade_stage += 1; cascade_stage += 1;
cascade_x += CASCADE_INTERVAL * cascade_stage; if (ltr)
cascade_x += CASCADE_INTERVAL * cascade_stage;
else
cascade_x -= CASCADE_INTERVAL * cascade_stage;
/* start over with a new cascade translated to the right, unless /* start over with a new cascade translated to the right
* we are out of space * (or to the left in RTL environment), unless we are out of space
*/ */
if ((cascade_x + window_width) < if (((cascade_x + window_width) <
(work_area.x + work_area.width)) (work_area.x + work_area.width)) &&
(cascade_x >= work_area.x))
{ {
tmp = sorted; tmp = sorted;
continue; continue;
@ -180,10 +236,10 @@ find_next_cascade (MetaWindow *window,
else else
{ {
/* All out of space, this cascade_x won't work */ /* All out of space, this cascade_x won't work */
cascade_x = MAX (0, work_area.x); cascade_x = cascade_origin_x;
break; break;
} }
} }
} }
else else
{ {
@ -435,6 +491,13 @@ leftmost_cmp (gconstpointer a, gconstpointer b)
return 0; return 0;
} }
static gint
rightmost_cmp (gconstpointer a,
gconstpointer b)
{
return -leftmost_cmp (a, b);
}
static gint static gint
topmost_cmp (gconstpointer a, gconstpointer b) topmost_cmp (gconstpointer a, gconstpointer b)
{ {
@ -469,9 +532,12 @@ center_tile_rect_in_area (MetaRectangle *rect,
* as a group) * as a group)
*/ */
fluff = (work_area->width % (rect->width+1)) / 2; fluff = (work_area->width % (rect->width + 1)) / 2;
rect->x = work_area->x + fluff; if (meta_get_locale_direction () == META_LOCALE_DIRECTION_LTR)
fluff = (work_area->height % (rect->height+1)) / 3; rect->x = work_area->x + fluff;
else
rect->x = work_area->x + work_area->width - rect->width - fluff;
fluff = (work_area->height % (rect->height + 1)) / 3;
rect->y = work_area->y + fluff; rect->y = work_area->y + fluff;
} }
@ -502,22 +568,23 @@ find_first_fit (MetaWindow *window,
*/ */
int retval; int retval;
GList *below_sorted; GList *below_sorted;
GList *right_sorted; GList *end_sorted;
GList *tmp; GList *tmp;
MetaRectangle rect; MetaRectangle rect;
MetaRectangle work_area; MetaRectangle work_area;
gboolean ltr = meta_get_locale_direction () == META_LOCALE_DIRECTION_LTR;
retval = FALSE; retval = FALSE;
/* Below each window */ /* Below each window */
below_sorted = g_list_copy (windows); below_sorted = g_list_copy (windows);
below_sorted = g_list_sort (below_sorted, leftmost_cmp); below_sorted = g_list_sort (below_sorted, ltr ? leftmost_cmp : rightmost_cmp);
below_sorted = g_list_sort (below_sorted, topmost_cmp); below_sorted = g_list_sort (below_sorted, topmost_cmp);
/* To the right of each window */ /* To the right of each window */
right_sorted = g_list_copy (windows); end_sorted = g_list_copy (windows);
right_sorted = g_list_sort (right_sorted, topmost_cmp); end_sorted = g_list_sort (end_sorted, topmost_cmp);
right_sorted = g_list_sort (right_sorted, leftmost_cmp); end_sorted = g_list_sort (end_sorted, ltr ? leftmost_cmp : rightmost_cmp);
meta_window_get_frame_rect (window, &rect); meta_window_get_frame_rect (window, &rect);
@ -576,8 +643,8 @@ find_first_fit (MetaWindow *window,
tmp = tmp->next; tmp = tmp->next;
} }
/* try to the right of each window */ /* try to the right (or left in RTL environment) of each window */
tmp = right_sorted; tmp = end_sorted;
while (tmp != NULL) while (tmp != NULL)
{ {
MetaWindow *w = tmp->data; MetaWindow *w = tmp->data;
@ -585,11 +652,14 @@ find_first_fit (MetaWindow *window,
meta_window_get_frame_rect (w, &frame_rect); meta_window_get_frame_rect (w, &frame_rect);
rect.x = frame_rect.x + frame_rect.width; if (ltr)
rect.x = frame_rect.x + frame_rect.width;
else
rect.x = frame_rect.x - rect.width;
rect.y = frame_rect.y; rect.y = frame_rect.y;
if (meta_rectangle_contains_rect (&work_area, &rect) && if (meta_rectangle_contains_rect (&work_area, &rect) &&
!rectangle_overlaps_some_window (&rect, right_sorted)) !rectangle_overlaps_some_window (&rect, end_sorted))
{ {
*new_x = rect.x; *new_x = rect.x;
*new_y = rect.y; *new_y = rect.y;
@ -602,9 +672,9 @@ find_first_fit (MetaWindow *window,
tmp = tmp->next; tmp = tmp->next;
} }
out: out:
g_list_free (below_sorted); g_list_free (below_sorted);
g_list_free (right_sorted); g_list_free (end_sorted);
return retval; return retval;
} }