From 19d2e8c7e1df6fcf12e5e5dcfe36430627eb8cc2 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Sat, 19 Jan 2002 03:50:03 +0000 Subject: [PATCH] give priority to keeping NW corner onscreen rather than SE, if we need to 2002-01-18 Havoc Pennington * src/window.c (constrain_position): give priority to keeping NW corner onscreen rather than SE, if we need to shift the window to fit inside constraints * src/frames.c (meta_frames_get_geometry): don't depend on the current window size * src/theme.c: move geometry stuff in here, to be calculated as part of the theme * src/core.c (meta_core_get_client_size): new function to replace meta_core_get_frame_size() so we don't have weird cycles in the geometry calculation --- ChangeLog | 23 ++++ src/core.c | 12 +- src/core.h | 10 +- src/frames.c | 347 ++++++++---------------------------------------- src/frames.h | 4 +- src/theme.c | 242 +++++++++++++++++++++++++++++++++ src/theme.h | 76 ++++++++++- src/window.c | 166 +++++++++++++++++++++-- src/window.h | 3 + src/workspace.c | 3 + 10 files changed, 570 insertions(+), 316 deletions(-) diff --git a/ChangeLog b/ChangeLog index 903bd3ac4..9c2da774a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2002-01-18 Havoc Pennington + + * src/window.c (constrain_position): give priority to keeping NW + corner onscreen rather than SE, if we need to shift the window + to fit inside constraints + + * src/frames.c (meta_frames_get_geometry): don't depend on the + current window size + + * src/theme.c: move geometry stuff in here, to be calculated as + part of the theme + + * src/core.c (meta_core_get_client_size): new function to replace + meta_core_get_frame_size() so we don't have weird cycles + in the geometry calculation + +2002-01-12 Havoc Pennington + + * src/window.c (meta_window_queue_move_resize): make this actually + queue, rather than being synchronous as it was before. We'll see + what breaks. Should be more efficient and reduce flickery stuff a + bit in some cases. + 2002-01-15 Havoc Pennington * src/keybindings.c (handle_tab_backward): fix crash diff --git a/src/core.c b/src/core.c index 399fe6d29..dddcf68f0 100644 --- a/src/core.c +++ b/src/core.c @@ -24,10 +24,10 @@ #include "workspace.h" void -meta_core_get_frame_size (Display *xdisplay, - Window frame_xwindow, - int *width, - int *height) +meta_core_get_client_size (Display *xdisplay, + Window frame_xwindow, + int *width, + int *height) { MetaDisplay *display; MetaWindow *window; @@ -39,9 +39,9 @@ meta_core_get_frame_size (Display *xdisplay, meta_bug ("No such frame window 0x%lx!\n", frame_xwindow); if (width) - *width = window->frame->rect.width; + *width = window->rect.width; if (height) - *height = window->frame->rect.height; + *height = window->rect.height; } MetaFrameFlags diff --git a/src/core.h b/src/core.h index bf8ccd119..b2146424e 100644 --- a/src/core.h +++ b/src/core.h @@ -23,15 +23,13 @@ #define META_CORE_H /* Don't include core headers here */ -#include #include -#include "frames.h" #include "common.h" -void meta_core_get_frame_size (Display *xdisplay, - Window frame_xwindow, - int *width, - int *height); +void meta_core_get_client_size (Display *xdisplay, + Window frame_xwindow, + int *width, + int *height); MetaFrameFlags meta_core_get_frame_flags (Display *xdisplay, Window frame_xwindow); diff --git a/src/frames.c b/src/frames.c index 84eedaef8..ed18b2e07 100644 --- a/src/frames.c +++ b/src/frames.c @@ -29,63 +29,6 @@ #define DEFAULT_INNER_BUTTON_BORDER 3 -struct _MetaFrameProperties -{ - /* Size of left/right/bottom sides */ - int left_width; - int right_width; - int bottom_height; - - /* Border of blue title region */ - GtkBorder title_border; - - /* Border inside title region, around title */ - GtkBorder text_border; - - /* padding on either side of spacer */ - int spacer_padding; - - /* Size of spacer */ - int spacer_width; - int spacer_height; - - /* indent of buttons from edges of frame */ - int right_inset; - int left_inset; - - /* Size of buttons */ - int button_width; - int button_height; - - /* Space around buttons */ - GtkBorder button_border; - - /* Space inside button which is clickable but doesn't draw the - * button icon - */ - GtkBorder inner_button_border; -}; - -typedef struct _MetaFrameGeometry MetaFrameGeometry; - -struct _MetaFrameGeometry -{ - int left_width; - int right_width; - int top_height; - int bottom_height; - - int width; - int height; - - GdkRectangle close_rect; - GdkRectangle max_rect; - GdkRectangle min_rect; - GdkRectangle spacer_rect; - GdkRectangle menu_rect; - GdkRectangle title_rect; -}; - static void meta_frames_class_init (MetaFramesClass *klass); static void meta_frames_init (MetaFrames *frames); static void meta_frames_destroy (GtkObject *object); @@ -276,7 +219,7 @@ meta_frames_init (MetaFrames *frames) { GTK_WINDOW (frames)->type = GTK_WINDOW_POPUP; - frames->props = g_new0 (MetaFrameProperties, 1); + frames->layout = meta_frame_layout_new (); frames->frames = g_hash_table_new (unsigned_long_hash, unsigned_long_equal); @@ -337,7 +280,7 @@ meta_frames_finalize (GObject *object) g_assert (g_hash_table_size (frames->frames) == 0); g_hash_table_destroy (frames->frames); - g_free (frames->props); + meta_frame_layout_free (frames->layout); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -395,35 +338,35 @@ meta_frames_style_set (GtkWidget *widget, GtkBorder *text_border; GtkBorder *button_border; GtkBorder *inner_button_border; - MetaFrameProperties props; + MetaFrameLayout layout; frames = META_FRAMES (widget); gtk_widget_style_get (widget, "left_width", - &props.left_width, + &layout.left_width, "right_width", - &props.right_width, + &layout.right_width, "bottom_height", - &props.bottom_height, + &layout.bottom_height, "title_border", &title_border, "text_border", &text_border, "spacer_padding", - &props.spacer_padding, + &layout.spacer_padding, "spacer_width", - &props.spacer_width, + &layout.spacer_width, "spacer_height", - &props.spacer_height, + &layout.spacer_height, "right_inset", - &props.right_inset, + &layout.right_inset, "left_inset", - &props.left_inset, + &layout.left_inset, "button_width", - &props.button_width, + &layout.button_width, "button_height", - &props.button_height, + &layout.button_height, "button_border", &button_border, "inner_button_border", @@ -431,34 +374,34 @@ meta_frames_style_set (GtkWidget *widget, NULL); if (title_border) - props.title_border = *title_border; + layout.title_border = *title_border; else - props.title_border = default_title_border; + layout.title_border = default_title_border; g_free (title_border); if (text_border) - props.text_border = *text_border; + layout.text_border = *text_border; else - props.text_border = default_text_border; + layout.text_border = default_text_border; g_free (text_border); if (button_border) - props.button_border = *button_border; + layout.button_border = *button_border; else - props.button_border = default_button_border; + layout.button_border = default_button_border; g_free (button_border); if (inner_button_border) - props.inner_button_border = *inner_button_border; + layout.inner_button_border = *inner_button_border; else - props.inner_button_border = default_inner_button_border; + layout.inner_button_border = default_inner_button_border; g_free (inner_button_border); - *(frames->props) = props; + *(frames->layout) = layout; { PangoFontMetrics *metrics; @@ -486,202 +429,23 @@ meta_frames_style_set (GtkWidget *widget, static void meta_frames_calc_geometry (MetaFrames *frames, - MetaUIFrame *frame, + MetaUIFrame *frame, MetaFrameGeometry *fgeom) { - int x; - int button_y; - int title_right_edge; - MetaFrameProperties props; - int buttons_height, title_height, spacer_height; int width, height; MetaFrameFlags flags; - props = *(frames->props); - - /* FIXME this is totally broken - the w/h here are the size of the - * frame xwindow, which is computed from the size the frame wants to - * be, which we are currently computing - stuff just happens to work - * now because we always go through this codepath twice, or don't - * really use these values, or something. - */ - meta_core_get_frame_size (gdk_display, frame->xwindow, - &width, &height); + meta_core_get_client_size (gdk_display, frame->xwindow, + &width, &height); flags = meta_core_get_frame_flags (gdk_display, frame->xwindow); - fgeom->width = width; - fgeom->height = height; - - buttons_height = props.button_height + - props.button_border.top + props.button_border.bottom; - title_height = frames->text_height + - props.text_border.top + props.text_border.bottom + - props.title_border.top + props.title_border.bottom; - spacer_height = props.spacer_height; - - fgeom->top_height = MAX (buttons_height, title_height); - fgeom->top_height = MAX (fgeom->top_height, spacer_height); - - fgeom->left_width = props.left_width; - fgeom->right_width = props.right_width; - - if (flags & META_FRAME_SHADED) - fgeom->bottom_height = 0; - else - fgeom->bottom_height = props.bottom_height; - - x = width - props.right_inset; - - /* center buttons */ - button_y = (fgeom->top_height - - (props.button_height + props.button_border.top + props.button_border.bottom)) / 2 + props.button_border.top; - - if ((flags & META_FRAME_ALLOWS_DELETE) && - x >= 0) - { - fgeom->close_rect.x = x - props.button_border.right - props.button_width; - fgeom->close_rect.y = button_y; - fgeom->close_rect.width = props.button_width; - fgeom->close_rect.height = props.button_height; - - x = fgeom->close_rect.x - props.button_border.left; - } - else - { - fgeom->close_rect.x = 0; - fgeom->close_rect.y = 0; - fgeom->close_rect.width = 0; - fgeom->close_rect.height = 0; - } - - if ((flags & META_FRAME_ALLOWS_MAXIMIZE) && - x >= 0) - { - fgeom->max_rect.x = x - props.button_border.right - props.button_width; - fgeom->max_rect.y = button_y; - fgeom->max_rect.width = props.button_width; - fgeom->max_rect.height = props.button_height; - - x = fgeom->max_rect.x - props.button_border.left; - } - else - { - fgeom->max_rect.x = 0; - fgeom->max_rect.y = 0; - fgeom->max_rect.width = 0; - fgeom->max_rect.height = 0; - } - - if ((flags & META_FRAME_ALLOWS_MINIMIZE) && - x >= 0) - { - fgeom->min_rect.x = x - props.button_border.right - props.button_width; - fgeom->min_rect.y = button_y; - fgeom->min_rect.width = props.button_width; - fgeom->min_rect.height = props.button_height; - - x = fgeom->min_rect.x - props.button_border.left; - } - else - { - fgeom->min_rect.x = 0; - fgeom->min_rect.y = 0; - fgeom->min_rect.width = 0; - fgeom->min_rect.height = 0; - } - - if ((fgeom->close_rect.width > 0 || - fgeom->max_rect.width > 0 || - fgeom->min_rect.width > 0) && - x >= 0) - { - fgeom->spacer_rect.x = x - props.spacer_padding - props.spacer_width; - fgeom->spacer_rect.y = (fgeom->top_height - props.spacer_height) / 2; - fgeom->spacer_rect.width = props.spacer_width; - fgeom->spacer_rect.height = props.spacer_height; - - x = fgeom->spacer_rect.x - props.spacer_padding; - } - else - { - fgeom->spacer_rect.x = 0; - fgeom->spacer_rect.y = 0; - fgeom->spacer_rect.width = 0; - fgeom->spacer_rect.height = 0; - } - - title_right_edge = x - props.title_border.right; - - /* Now x changes to be position from the left */ - x = props.left_inset; - - if (flags & META_FRAME_ALLOWS_MENU) - { - fgeom->menu_rect.x = x + props.button_border.left; - fgeom->menu_rect.y = button_y; - fgeom->menu_rect.width = props.button_width; - fgeom->menu_rect.height = props.button_height; - - x = fgeom->menu_rect.x + fgeom->menu_rect.width + props.button_border.right; - } - else - { - fgeom->menu_rect.x = 0; - fgeom->menu_rect.y = 0; - fgeom->menu_rect.width = 0; - fgeom->menu_rect.height = 0; - } - - /* If menu overlaps close button, then the menu wins since it - * lets you perform any operation including close - */ - if (fgeom->close_rect.width > 0 && - fgeom->close_rect.x < (fgeom->menu_rect.x + fgeom->menu_rect.height)) - { - fgeom->close_rect.width = 0; - fgeom->close_rect.height = 0; - } - - /* Check for maximize overlap */ - if (fgeom->max_rect.width > 0 && - fgeom->max_rect.x < (fgeom->menu_rect.x + fgeom->menu_rect.height)) - { - fgeom->max_rect.width = 0; - fgeom->max_rect.height = 0; - } - - /* Check for minimize overlap */ - if (fgeom->min_rect.width > 0 && - fgeom->min_rect.x < (fgeom->menu_rect.x + fgeom->menu_rect.height)) - { - fgeom->min_rect.width = 0; - fgeom->min_rect.height = 0; - } - - /* Check for spacer overlap */ - if (fgeom->spacer_rect.width > 0 && - fgeom->spacer_rect.x < (fgeom->menu_rect.x + fgeom->menu_rect.height)) - { - fgeom->spacer_rect.width = 0; - fgeom->spacer_rect.height = 0; - } - - /* We always fill as much vertical space as possible with title rect, - * rather than centering it like the buttons and spacer - */ - fgeom->title_rect.x = x + props.title_border.left; - fgeom->title_rect.y = props.title_border.top; - fgeom->title_rect.width = title_right_edge - fgeom->title_rect.x; - fgeom->title_rect.height = fgeom->top_height - props.title_border.top - props.title_border.bottom; - - /* Nuke title if it won't fit */ - if (fgeom->title_rect.width < 0 || - fgeom->title_rect.height < 0) - { - fgeom->title_rect.width = 0; - fgeom->title_rect.height = 0; - } + meta_frame_layout_calc_geometry (frames->layout, + GTK_WIDGET (frames), + frames->text_height, + flags, + width, height, + fgeom); } MetaFrames* @@ -789,25 +553,27 @@ meta_frames_get_geometry (MetaFrames *frames, int *top_height, int *bottom_height, int *left_width, int *right_width) { - MetaFrameGeometry fgeom; - + MetaFrameFlags flags; MetaUIFrame *frame; frame = meta_frames_lookup_window (frames, xwindow); if (frame == NULL) meta_bug ("No such frame 0x%lx\n", xwindow); + + flags = meta_core_get_frame_flags (gdk_display, frame->xwindow); - meta_frames_calc_geometry (frames, frame, &fgeom); - - if (top_height) - *top_height = fgeom.top_height; - if (bottom_height) - *bottom_height = fgeom.bottom_height; - if (left_width) - *left_width = fgeom.left_width; - if (right_width) - *right_width = fgeom.right_width; + /* We can't get the full geometry, because that depends on + * the client window size and probably we're being called + * by the core move/resize code to decide on the client + * window size + */ + meta_frame_layout_get_borders (frames->layout, + GTK_WIDGET (frames), + frames->text_height, + flags, + top_height, bottom_height, + left_width, right_width); } void @@ -1704,7 +1470,8 @@ meta_frames_expose_event (GtkWidget *widget, meta_frames_calc_geometry (frames, frame, &fgeom); flags = meta_core_get_frame_flags (gdk_display, frame->xwindow); - meta_core_get_frame_size (gdk_display, frame->xwindow, &width, &height); + width = fgeom.width; + height = fgeom.height; /* Black line around outside to give definition */ gdk_draw_rectangle (frame->window, @@ -1751,9 +1518,9 @@ meta_frames_expose_event (GtkWidget *widget, GdkGC *layout_gc; clip = fgeom.title_rect; - clip.x += frames->props->text_border.left; - clip.width -= frames->props->text_border.left + - frames->props->text_border.right; + clip.x += frames->layout->text_border.left; + clip.width -= frames->layout->text_border.left + + frames->layout->text_border.right; layout_gc = widget->style->fg_gc[GTK_STATE_NORMAL]; if (flags & META_FRAME_HAS_FOCUS) @@ -1826,16 +1593,16 @@ meta_frames_expose_event (GtkWidget *widget, &layout_rect); /* corner of whole title area */ - x = fgeom.title_rect.x + frames->props->text_border.left; - y = fgeom.title_rect.y + frames->props->text_border.top; + x = fgeom.title_rect.x + frames->layout->text_border.left; + y = fgeom.title_rect.y + frames->layout->text_border.top; area_w = fgeom.title_rect.width - - frames->props->text_border.left - - frames->props->text_border.right; + frames->layout->text_border.left - + frames->layout->text_border.right; area_h = fgeom.title_rect.height - - frames->props->text_border.top - - frames->props->text_border.bottom; + frames->layout->text_border.top - + frames->layout->text_border.bottom; /* center icon vertically */ icon_y = y + MAX ((area_h - icon_h) / 2, 0); @@ -1884,7 +1651,7 @@ meta_frames_expose_event (GtkWidget *widget, } } - inner = frames->props->inner_button_border; + inner = frames->layout->inner_button_border; if (fgeom.close_rect.width > 0 && fgeom.close_rect.height > 0) { diff --git a/src/frames.h b/src/frames.h index 8cfa31017..5cc70ea94 100644 --- a/src/frames.h +++ b/src/frames.h @@ -25,6 +25,7 @@ #include #include #include "common.h" +#include "theme.h" typedef enum { @@ -61,7 +62,6 @@ typedef struct _MetaFrames MetaFrames; typedef struct _MetaFramesClass MetaFramesClass; typedef struct _MetaUIFrame MetaUIFrame; -typedef struct _MetaFrameProperties MetaFrameProperties; struct _MetaUIFrame { @@ -76,7 +76,7 @@ struct _MetaFrames GtkWindow parent_instance; /* If we did a widget per frame, we wouldn't want to cache this. */ - MetaFrameProperties *props; + MetaFrameLayout *layout; int text_height; diff --git a/src/theme.c b/src/theme.c index 1629c5cec..70a353815 100644 --- a/src/theme.c +++ b/src/theme.c @@ -22,8 +22,250 @@ #include "theme.h" #include "util.h" #include "gradient.h" +#include #include +MetaFrameLayout* +meta_frame_layout_new (void) +{ + MetaFrameLayout *layout; + + layout = g_new0 (MetaFrameLayout, 1); + + return layout; +} + +void +meta_frame_layout_free (MetaFrameLayout *layout) +{ + g_return_if_fail (layout != NULL); + + g_free (layout); +} + +void +meta_frame_layout_get_borders (const MetaFrameLayout *layout, + GtkWidget *widget, + int text_height, + MetaFrameFlags flags, + int *top_height, + int *bottom_height, + int *left_width, + int *right_width) +{ + int buttons_height, title_height, spacer_height; + + g_return_if_fail (top_height != NULL); + g_return_if_fail (bottom_height != NULL); + g_return_if_fail (left_width != NULL); + g_return_if_fail (right_width != NULL); + + buttons_height = layout->button_height + + layout->button_border.top + layout->button_border.bottom; + title_height = text_height + + layout->text_border.top + layout->text_border.bottom + + layout->title_border.top + layout->title_border.bottom; + spacer_height = layout->spacer_height; + + if (top_height) + { + *top_height = MAX (buttons_height, title_height); + *top_height = MAX (*top_height, spacer_height); + } + + if (left_width) + *left_width = layout->left_width; + if (right_width) + *right_width = layout->right_width; + + if (bottom_height) + { + if (flags & META_FRAME_SHADED) + *bottom_height = 0; + else + *bottom_height = layout->bottom_height; + } +} + +void +meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, + GtkWidget *widget, + int text_height, + MetaFrameFlags flags, + int client_width, + int client_height, + MetaFrameGeometry *fgeom) +{ + int x; + int button_y; + int title_right_edge; + int width, height; + + meta_frame_layout_get_borders (layout, widget, text_height, + flags, + &fgeom->top_height, + &fgeom->bottom_height, + &fgeom->left_width, + &fgeom->right_width); + + width = client_width + fgeom->left_width + fgeom->right_width; + height = client_height + fgeom->top_height + fgeom->bottom_height; + + fgeom->width = width; + fgeom->height = height; + + x = width - layout->right_inset; + + /* center buttons */ + button_y = (fgeom->top_height - + (layout->button_height + layout->button_border.top + layout->button_border.bottom)) / 2 + layout->button_border.top; + + if ((flags & META_FRAME_ALLOWS_DELETE) && + x >= 0) + { + fgeom->close_rect.x = x - layout->button_border.right - layout->button_width; + fgeom->close_rect.y = button_y; + fgeom->close_rect.width = layout->button_width; + fgeom->close_rect.height = layout->button_height; + + x = fgeom->close_rect.x - layout->button_border.left; + } + else + { + fgeom->close_rect.x = 0; + fgeom->close_rect.y = 0; + fgeom->close_rect.width = 0; + fgeom->close_rect.height = 0; + } + + if ((flags & META_FRAME_ALLOWS_MAXIMIZE) && + x >= 0) + { + fgeom->max_rect.x = x - layout->button_border.right - layout->button_width; + fgeom->max_rect.y = button_y; + fgeom->max_rect.width = layout->button_width; + fgeom->max_rect.height = layout->button_height; + + x = fgeom->max_rect.x - layout->button_border.left; + } + else + { + fgeom->max_rect.x = 0; + fgeom->max_rect.y = 0; + fgeom->max_rect.width = 0; + fgeom->max_rect.height = 0; + } + + if ((flags & META_FRAME_ALLOWS_MINIMIZE) && + x >= 0) + { + fgeom->min_rect.x = x - layout->button_border.right - layout->button_width; + fgeom->min_rect.y = button_y; + fgeom->min_rect.width = layout->button_width; + fgeom->min_rect.height = layout->button_height; + + x = fgeom->min_rect.x - layout->button_border.left; + } + else + { + fgeom->min_rect.x = 0; + fgeom->min_rect.y = 0; + fgeom->min_rect.width = 0; + fgeom->min_rect.height = 0; + } + + if ((fgeom->close_rect.width > 0 || + fgeom->max_rect.width > 0 || + fgeom->min_rect.width > 0) && + x >= 0) + { + fgeom->spacer_rect.x = x - layout->spacer_padding - layout->spacer_width; + fgeom->spacer_rect.y = (fgeom->top_height - layout->spacer_height) / 2; + fgeom->spacer_rect.width = layout->spacer_width; + fgeom->spacer_rect.height = layout->spacer_height; + + x = fgeom->spacer_rect.x - layout->spacer_padding; + } + else + { + fgeom->spacer_rect.x = 0; + fgeom->spacer_rect.y = 0; + fgeom->spacer_rect.width = 0; + fgeom->spacer_rect.height = 0; + } + + title_right_edge = x - layout->title_border.right; + + /* Now x changes to be position from the left */ + x = layout->left_inset; + + if (flags & META_FRAME_ALLOWS_MENU) + { + fgeom->menu_rect.x = x + layout->button_border.left; + fgeom->menu_rect.y = button_y; + fgeom->menu_rect.width = layout->button_width; + fgeom->menu_rect.height = layout->button_height; + + x = fgeom->menu_rect.x + fgeom->menu_rect.width + layout->button_border.right; + } + else + { + fgeom->menu_rect.x = 0; + fgeom->menu_rect.y = 0; + fgeom->menu_rect.width = 0; + fgeom->menu_rect.height = 0; + } + + /* If menu overlaps close button, then the menu wins since it + * lets you perform any operation including close + */ + if (fgeom->close_rect.width > 0 && + fgeom->close_rect.x < (fgeom->menu_rect.x + fgeom->menu_rect.height)) + { + fgeom->close_rect.width = 0; + fgeom->close_rect.height = 0; + } + + /* Check for maximize overlap */ + if (fgeom->max_rect.width > 0 && + fgeom->max_rect.x < (fgeom->menu_rect.x + fgeom->menu_rect.height)) + { + fgeom->max_rect.width = 0; + fgeom->max_rect.height = 0; + } + + /* Check for minimize overlap */ + if (fgeom->min_rect.width > 0 && + fgeom->min_rect.x < (fgeom->menu_rect.x + fgeom->menu_rect.height)) + { + fgeom->min_rect.width = 0; + fgeom->min_rect.height = 0; + } + + /* Check for spacer overlap */ + if (fgeom->spacer_rect.width > 0 && + fgeom->spacer_rect.x < (fgeom->menu_rect.x + fgeom->menu_rect.height)) + { + fgeom->spacer_rect.width = 0; + fgeom->spacer_rect.height = 0; + } + + /* We always fill as much vertical space as possible with title rect, + * rather than centering it like the buttons and spacer + */ + fgeom->title_rect.x = x + layout->title_border.left; + fgeom->title_rect.y = layout->title_border.top; + fgeom->title_rect.width = title_right_edge - fgeom->title_rect.x; + fgeom->title_rect.height = fgeom->top_height - layout->title_border.top - layout->title_border.bottom; + + /* Nuke title if it won't fit */ + if (fgeom->title_rect.width < 0 || + fgeom->title_rect.height < 0) + { + fgeom->title_rect.width = 0; + fgeom->title_rect.height = 0; + } +} MetaGradientSpec* meta_gradient_spec_new (MetaGradientType type) diff --git a/src/theme.h b/src/theme.h index b14142a08..1cb73f06f 100644 --- a/src/theme.h +++ b/src/theme.h @@ -22,8 +22,8 @@ #ifndef META_THEME_H #define META_THEME_H -#include "frames.h" #include "gradient.h" +#include "common.h" #include typedef struct _MetaFrameStyle MetaFrameStyle; @@ -31,6 +31,67 @@ typedef struct _MetaFrameStyleSet MetaFrameStyleSet; typedef struct _MetaTextureSpec MetaTextureSpec; typedef struct _MetaGradientSpec MetaGradientSpec; typedef struct _MetaColorSpec MetaColorSpec; +typedef struct _MetaFrameLayout MetaFrameLayout; +typedef struct _MetaFrameGeometry MetaFrameGeometry; + +/* Parameters used to calculate the geometry of the frame */ +struct _MetaFrameLayout +{ + /* Size of left/right/bottom sides */ + int left_width; + int right_width; + int bottom_height; + + /* Border of blue title region */ + GtkBorder title_border; + + /* Border inside title region, around title */ + GtkBorder text_border; + + /* padding on either side of spacer */ + int spacer_padding; + + /* Size of spacer */ + int spacer_width; + int spacer_height; + + /* indent of buttons from edges of frame */ + int right_inset; + int left_inset; + + /* Size of buttons */ + int button_width; + int button_height; + + /* Space around buttons */ + GtkBorder button_border; + + /* Space inside button which is clickable but doesn't draw the + * button icon + */ + GtkBorder inner_button_border; +}; + + +/* Calculated actual geometry of the frame */ +struct _MetaFrameGeometry +{ + int left_width; + int right_width; + int top_height; + int bottom_height; + + int width; + int height; + + GdkRectangle close_rect; + GdkRectangle max_rect; + GdkRectangle min_rect; + GdkRectangle spacer_rect; + GdkRectangle menu_rect; + GdkRectangle title_rect; +}; + typedef enum { @@ -184,7 +245,8 @@ struct _MetaFrameStyle { MetaTextureSpec *button_icons[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST]; MetaTextureSpec *button_backgrounds[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST]; - MetaTextureSpec *pieces[META_FRAME_PIECE_LAST]; + MetaTextureSpec *pieces[META_FRAME_PIECE_LAST]; + MetaFrameLayout *layout; }; typedef enum @@ -195,6 +257,16 @@ typedef enum META_TEXTURE_DRAW_SCALED_BOTH } MetaTextureDrawMode; +MetaFrameLayout* meta_frame_layout_new (void); +void meta_frame_layout_free (MetaFrameLayout *layout); +void meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, + GtkWidget *widget, + int text_height, + MetaFrameFlags flags, + int client_width, + int client_height, + MetaFrameGeometry *fgeom); + MetaColorSpec* meta_color_spec_new (MetaColorSpecType type); void meta_color_spec_free (MetaColorSpec *spec); void meta_color_spec_render (MetaColorSpec *spec, diff --git a/src/window.c b/src/window.c index 712499a27..252e5337d 100644 --- a/src/window.c +++ b/src/window.c @@ -53,6 +53,8 @@ typedef enum WIN_HINTS_DO_NOT_COVER = (1<<5) /* attempt to not cover this window */ } GnomeWinHints; +static int destroying_windows_disallowed = 0; + static void constrain_size (MetaWindow *window, MetaFrameGeometry *fgeom, int width, @@ -127,6 +129,9 @@ static char* get_text_property (MetaDisplay *display, void meta_window_unqueue_calc_showing (MetaWindow *window); void meta_window_flush_calc_showing (MetaWindow *window); +void meta_window_unqueue_move_resize (MetaWindow *window); +void meta_window_flush_move_resize (MetaWindow *window); + static void meta_window_apply_session_info (MetaWindow *window, const MetaWindowSessionInfo *info); @@ -316,6 +321,7 @@ meta_window_new (MetaDisplay *display, Window xwindow, xwindow); window->unmanaging = FALSE; window->calc_showing_queued = FALSE; + window->move_resize_queued = FALSE; window->keys_grabbed = FALSE; window->grab_on_frame = FALSE; window->all_keys_grabbed = FALSE; @@ -677,6 +683,10 @@ meta_window_free (MetaWindow *window) meta_verbose ("Unmanaging 0x%lx\n", window->xwindow); + if (destroying_windows_disallowed > 0) + meta_bug ("Tried to destroy window %s while destruction was not allowed\n", + window->desc); + window->unmanaging = TRUE; /* If we have the focus, focus some other window. @@ -713,6 +723,7 @@ meta_window_free (MetaWindow *window) window->display->prev_focus_window = NULL; meta_window_unqueue_calc_showing (window); + meta_window_unqueue_move_resize (window); tmp = window->workspaces; while (tmp != NULL) @@ -969,6 +980,8 @@ idle_calc_showing (gpointer data) g_slist_free (calc_showing_pending); calc_showing_pending = NULL; calc_showing_idle = 0; + + destroying_windows_disallowed += 1; /* sort them from bottom to top, so we map the * bottom windows first, so that placement (e.g. cascading) @@ -984,12 +997,20 @@ idle_calc_showing (gpointer data) window = tmp->data; meta_window_calc_showing (window); + + /* important to set this here for reentrancy - + * if we queue a window again while it's in "copy", + * then queue_calc_showing will just return since + * calc_showing_queued = TRUE still + */ window->calc_showing_queued = FALSE; tmp = tmp->next; } g_slist_free (copy); + + destroying_windows_disallowed -= 1; return FALSE; } @@ -1003,6 +1024,9 @@ meta_window_unqueue_calc_showing (MetaWindow *window) meta_verbose ("Removing %s from the calc_showing queue\n", window->desc); + /* Note that window may not actually be in move_resize_pending + * because it may have been in "copy" inside the idle handler + */ calc_showing_pending = g_slist_remove (calc_showing_pending, window); window->calc_showing_queued = FALSE; @@ -1520,6 +1544,9 @@ meta_window_move_resize_internal (MetaWindow *window, gboolean is_configure_request; gboolean do_gravity_adjust; gboolean is_user_action; + + /* We don't need it in the idle queue anymore. */ + meta_window_unqueue_move_resize (window); is_configure_request = (flags & META_IS_CONFIGURE_REQUEST) != 0; do_gravity_adjust = (flags & META_DO_GRAVITY_ADJUST) != 0; @@ -1801,7 +1828,7 @@ meta_window_move_resize_internal (MetaWindow *window, { meta_verbose ("Size/position not modified\n"); } - + /* Update struts for new window size */ if (window->do_not_cover && (need_resize_client || need_move_client)) { @@ -1809,7 +1836,8 @@ meta_window_move_resize_internal (MetaWindow *window, /* Does a resize on all windows on entire current workspace, * would be an infinite loop except for need_resize_client - * above. + * above. We rely on reaching an equilibrium state, which + * is somewhat fragile, though. */ invalidate_work_areas (window); @@ -1901,11 +1929,125 @@ meta_window_move_resize_now (MetaWindow *window) window->user_rect.height : window->rect.height); } + +static guint move_resize_idle = 0; +static GSList *move_resize_pending = NULL; + +/* We want to put windows whose size/pos affects other + * windows earlier in the queue, for efficiency. + */ +static int +move_resize_cmp (gconstpointer a, gconstpointer b) +{ + MetaWindow *aw = (gpointer) a; + MetaWindow *bw = (gpointer) b; + + if (aw->do_not_cover && !bw->do_not_cover) + return -1; /* aw before bw */ + else if (!aw->do_not_cover && bw->do_not_cover) + return 1; + else + return 0; +} + +static gboolean +idle_move_resize (gpointer data) +{ + GSList *tmp; + GSList *copy; + + meta_verbose ("Clearing the move_resize queue\n"); + + /* Work with a copy, for reentrancy. The allowed reentrancy isn't + * complete; destroying a window while we're in here would result in + * badness. But it's OK to queue/unqueue move_resizes. + */ + copy = g_slist_copy (move_resize_pending); + g_slist_free (move_resize_pending); + move_resize_pending = NULL; + move_resize_idle = 0; + + destroying_windows_disallowed += 1; + + copy = g_slist_sort (copy, move_resize_cmp); + + tmp = copy; + while (tmp != NULL) + { + MetaWindow *window; + + window = tmp->data; + + /* As a side effect, sets window->move_resize_queued = FALSE */ + meta_window_move_resize_now (window); + + tmp = tmp->next; + } + + g_slist_free (copy); + + destroying_windows_disallowed -= 1; + + return FALSE; +} + +void +meta_window_unqueue_move_resize (MetaWindow *window) +{ + if (!window->move_resize_queued) + return; + + meta_verbose ("Removing %s from the move_resize queue\n", + window->desc); + + /* Note that window may not actually be in move_resize_pending + * because it may have been in "copy" inside the idle handler + */ + move_resize_pending = g_slist_remove (move_resize_pending, window); + window->move_resize_queued = FALSE; + + if (move_resize_pending == NULL && + move_resize_idle != 0) + { + g_source_remove (move_resize_idle); + move_resize_idle = 0; + } +} + +void +meta_window_flush_move_resize (MetaWindow *window) +{ + if (window->move_resize_queued) + { + meta_window_unqueue_move_resize (window); + meta_window_move_resize_now (window); + } +} + +/* The move/resize queue is only used when we need to + * recheck the constraints on the window, e.g. when + * maximizing or when changing struts. Configure requests + * and such always have to be handled synchronously, + * they can't be done via a queue. + */ void meta_window_queue_move_resize (MetaWindow *window) { - /* FIXME actually queue, don't do it immediately */ - meta_window_move_resize_now (window); + if (window->unmanaging) + return; + + if (window->move_resize_queued) + return; + + meta_verbose ("Putting %s in the move_resize queue\n", + window->desc); + + window->move_resize_queued = TRUE; + + if (move_resize_idle == 0) + move_resize_idle = g_idle_add (idle_move_resize, NULL); + + move_resize_pending = g_slist_prepend (move_resize_pending, window); } void @@ -5014,16 +5156,20 @@ constrain_position (MetaWindow *window, se_y = tmp; } - /* Clamp window to the given positions */ - if (x < nw_x) - x = nw_x; - if (y < nw_y) - y = nw_y; - + /* Clamp window to the given positions. + * Do the SE clamp first, so that the NW clamp has precedence + * and we don't tend to lose the titlebar for too-large + * windows. + */ if (x > se_x) x = se_x; if (y > se_y) y = se_y; + + if (x < nw_x) + x = nw_x; + if (y < nw_y) + y = nw_y; /* If maximized, force the exact position */ if (window->maximized) diff --git a/src/window.h b/src/window.h index 4fbea570c..bd24faf7e 100644 --- a/src/window.h +++ b/src/window.h @@ -169,6 +169,9 @@ struct _MetaWindow /* Are we in the calc_showing queue? */ guint calc_showing_queued : 1; + /* Are we in the move_resize queue? */ + guint move_resize_queued : 1; + /* Used by keybindings.c */ guint keys_grabbed : 1; /* normal keybindings grabbed */ guint grab_on_frame : 1; /* grabs are on the frame */ diff --git a/src/workspace.c b/src/workspace.c index 77c02e5eb..70bf08036 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -327,6 +327,9 @@ meta_workspace_invalidate_work_area (MetaWorkspace *workspace) if (workspace->work_area_invalid) return; + + meta_verbose ("Invalidating work area for workspace %d\n", + meta_workspace_index (workspace)); workspace->work_area_invalid = TRUE;