diff options
-rw-r--r-- | libmetacity/Makefile.am | 4 | ||||
-rw-r--r-- | libmetacity/meta-button-layout-private.h | 16 | ||||
-rw-r--r-- | libmetacity/meta-button-layout.c | 184 | ||||
-rw-r--r-- | libmetacity/meta-button.h (renamed from libmetacity/meta-button-enums.h) | 27 | ||||
-rw-r--r-- | libmetacity/meta-frame-style-private.h | 2 | ||||
-rw-r--r-- | libmetacity/meta-theme-gtk.c | 503 | ||||
-rw-r--r-- | libmetacity/meta-theme-impl-private.h | 19 | ||||
-rw-r--r-- | libmetacity/meta-theme-impl.c | 195 | ||||
-rw-r--r-- | libmetacity/meta-theme-metacity.c | 750 | ||||
-rw-r--r-- | libmetacity/meta-theme.c | 109 | ||||
-rw-r--r-- | libmetacity/meta-theme.h | 50 | ||||
-rw-r--r-- | src/ui/frames.c | 859 | ||||
-rw-r--r-- | src/ui/frames.h | 2 |
13 files changed, 1323 insertions, 1397 deletions
diff --git a/libmetacity/Makefile.am b/libmetacity/Makefile.am index 426cb8c3..785e3e30 100644 --- a/libmetacity/Makefile.am +++ b/libmetacity/Makefile.am @@ -3,7 +3,7 @@ NULL = lib_LTLIBRARIES = libmetacity.la libmetacity_la_SOURCES = \ - meta-button-enums.h \ + meta-button.h \ meta-button-layout.c \ meta-button-layout-private.h \ meta-button-private.h \ @@ -69,7 +69,7 @@ libmetacity_la_LIBADD = \ libmetacity_includedir = $(includedir)/metacity/libmetacity libmetacity_include_HEADERS = \ - meta-button-enums.h \ + meta-button.h \ meta-color.h \ meta-enum-types.h \ meta-frame-borders.h \ diff --git a/libmetacity/meta-button-layout-private.h b/libmetacity/meta-button-layout-private.h index 62c62df1..4926b1dc 100644 --- a/libmetacity/meta-button-layout-private.h +++ b/libmetacity/meta-button-layout-private.h @@ -19,23 +19,17 @@ #ifndef META_BUTTON_LAYOUT_PRIVATE_H #define META_BUTTON_LAYOUT_PRIVATE_H -#include "meta-button-enums.h" +#include "meta-button.h" G_BEGIN_DECLS typedef struct { - /* buttons in the group on the left side */ - MetaButtonType left_buttons[META_BUTTON_TYPE_LAST]; - gboolean left_buttons_has_spacer[META_BUTTON_TYPE_LAST]; - gint n_left_buttons; + MetaButton *left_buttons; + gint n_left_buttons; - /* buttons in the group on the right side */ - MetaButtonType right_buttons[META_BUTTON_TYPE_LAST]; - gboolean right_buttons_has_spacer[META_BUTTON_TYPE_LAST]; - gint n_right_buttons; - - MetaButtonState button_states[META_BUTTON_TYPE_LAST]; + MetaButton *right_buttons; + gint n_right_buttons; } MetaButtonLayout; MetaButtonLayout *meta_button_layout_new (const gchar *str, diff --git a/libmetacity/meta-button-layout.c b/libmetacity/meta-button-layout.c index fb92712d..4679cbf8 100644 --- a/libmetacity/meta-button-layout.c +++ b/libmetacity/meta-button-layout.c @@ -21,7 +21,7 @@ #include "meta-button-layout-private.h" static MetaButtonType -meta_button_type_from_string (const gchar *str) +type_from_string (const gchar *str) { if (g_strcmp0 (str, "menu") == 0) return META_BUTTON_TYPE_MENU; @@ -45,12 +45,14 @@ meta_button_type_from_string (const gchar *str) return META_BUTTON_TYPE_STICK; else if (g_strcmp0 (str, "unstick") == 0) return META_BUTTON_TYPE_UNSTICK; + else if (g_strcmp0 (str, "spacer") == 0) + return META_BUTTON_TYPE_SPACER; return META_BUTTON_TYPE_LAST; } static MetaButtonType -meta_button_type_get_opposite (MetaButtonType type) +get_opposite_type (MetaButtonType type) { switch (type) { @@ -74,155 +76,141 @@ meta_button_type_get_opposite (MetaButtonType type) case META_BUTTON_TYPE_MINIMIZE: case META_BUTTON_TYPE_MAXIMIZE: case META_BUTTON_TYPE_CLOSE: + case META_BUTTON_TYPE_SPACER: case META_BUTTON_TYPE_LAST: - return META_BUTTON_TYPE_LAST; - default: - return META_BUTTON_TYPE_LAST; + break; } -} - -static void -meta_button_layout_init (MetaButtonLayout *layout) -{ - gint i; - - for (i = 0; i < META_BUTTON_TYPE_LAST; i++) - { - layout->left_buttons[i] = META_BUTTON_TYPE_LAST; - layout->left_buttons_has_spacer[i] = FALSE; - layout->right_buttons[i] = META_BUTTON_TYPE_LAST; - layout->right_buttons_has_spacer[i] = FALSE; - } + return META_BUTTON_TYPE_LAST; } -static void -string_to_buttons (const gchar *str, - MetaButtonType side_buttons[META_BUTTON_TYPE_LAST], - gboolean side_has_spacer[META_BUTTON_TYPE_LAST]) +static MetaButton * +string_to_buttons (const gchar *str, + gint *n_buttons) { - gint i; - gint b; - gboolean used[META_BUTTON_TYPE_LAST]; gchar **buttons; + MetaButton *retval; + gint index; + gint i; + + *n_buttons = 0; - i = 0; - while (i < META_BUTTON_TYPE_LAST) - used[i++] = FALSE; + if (str == NULL) + return NULL; buttons = g_strsplit (str, ",", -1); - i = b = 0; - while (buttons[b] != NULL) + for (i = 0; buttons[i] != NULL; i++) { MetaButtonType type; - type = meta_button_type_from_string (buttons[b]); + type = type_from_string (buttons[i]); - if (i > 0 && g_strcmp0 ("spacer", buttons[b]) == 0) + if (type != META_BUTTON_TYPE_LAST) { - side_has_spacer[i - 1] = TRUE; - - type = meta_button_type_get_opposite (type); - if (type != META_BUTTON_TYPE_LAST) - side_has_spacer[i - 2] = TRUE; + if (get_opposite_type (type) != META_BUTTON_TYPE_LAST) + *n_buttons += 2; + else + *n_buttons += 1; } else { - if (type != META_BUTTON_TYPE_LAST && !used[type]) - { - side_buttons[i] = type; - used[type] = TRUE; - i++; + g_debug ("Ignoring unknown button name - '%s'", buttons[i]); + } + } - type = meta_button_type_get_opposite (type); - if (type != META_BUTTON_TYPE_LAST) - side_buttons[i++] = type; - } - else + retval = g_new0 (MetaButton, *n_buttons); + index = 0; + + for (i = 0; buttons[i] != NULL; i++) + { + MetaButtonType type; + + type = type_from_string (buttons[i]); + + if (type != META_BUTTON_TYPE_LAST) + { + GdkRectangle empty; + MetaButton tmp; + + empty.x = 0; + empty.y = 0; + empty.width = 0; + empty.height = 0; + + tmp.type = type; + tmp.state = META_BUTTON_STATE_NORMAL; + tmp.rect.visible = empty; + tmp.rect.clickable = empty; + tmp.visible = TRUE; + + retval[index++] = tmp; + + type = get_opposite_type (type); + if (type != META_BUTTON_TYPE_LAST) { - g_debug ("Ignoring unknown or already-used button name - '%s'", - buttons[b]); + tmp.type = type; + retval[index++] = tmp; } } - - b++; } g_strfreev (buttons); + + return retval; } MetaButtonLayout * meta_button_layout_new (const gchar *str, gboolean invert) { - gchar **sides; MetaButtonLayout *layout; - MetaButtonLayout *rtl_layout; - gint i; - gint j; + gchar **sides; + const gchar *buttons; + gint n_buttons; layout = g_new0 (MetaButtonLayout, 1); - meta_button_layout_init (layout); - sides = g_strsplit (str, ":", 2); - if (sides[0] != NULL) - { - string_to_buttons (sides[0], layout->left_buttons, - layout->left_buttons_has_spacer); - } + buttons = sides[0]; + layout->left_buttons = string_to_buttons (buttons, &n_buttons); + layout->n_left_buttons = n_buttons; - if (sides[0] != NULL && sides[1] != NULL) - { - string_to_buttons (sides[1], layout->right_buttons, - layout->right_buttons_has_spacer); - } + buttons = sides[0] != NULL ? sides[1] : NULL; + layout->right_buttons = string_to_buttons (buttons, &n_buttons); + layout->n_right_buttons = n_buttons; g_strfreev (sides); - if (!invert) - return layout; - - rtl_layout = g_new0 (MetaButtonLayout, 1); - meta_button_layout_init (rtl_layout); - - i = 0; - while (rtl_layout->left_buttons[i] != META_BUTTON_TYPE_LAST) - i++; - - for (j = 0; j < i; j++) + if (invert) { - rtl_layout->right_buttons[j] = layout->left_buttons[i - j - 1]; + MetaButtonLayout *rtl_layout; + gint i; - if (j == 0) - rtl_layout->right_buttons_has_spacer[i - 1] = layout->left_buttons_has_spacer[i - j - 1]; - else - rtl_layout->right_buttons_has_spacer[j - 1] = layout->left_buttons_has_spacer[i - j - 1]; - } + rtl_layout = g_new0 (MetaButtonLayout, 1); + rtl_layout->left_buttons = g_new0 (MetaButton, layout->n_right_buttons); + rtl_layout->right_buttons = g_new0 (MetaButton, layout->n_left_buttons); - i = 0; - while (rtl_layout->left_buttons[i] != META_BUTTON_TYPE_LAST) - i++; + for (i = 0; i < layout->n_left_buttons; i++) + rtl_layout->right_buttons[i] = rtl_layout->left_buttons[layout->n_left_buttons - i]; - for (j = 0; j < i; j++) - { - rtl_layout->left_buttons[j] = layout->right_buttons[i - j - 1]; + for (i = 0; i < layout->n_right_buttons; i++) + rtl_layout->left_buttons[i] = rtl_layout->right_buttons[layout->n_right_buttons - i]; - if (j == 0) - rtl_layout->left_buttons_has_spacer[i - 1] = layout->right_buttons_has_spacer[i - j - 1]; - else - rtl_layout->left_buttons_has_spacer[j - 1] = layout->right_buttons_has_spacer[i - j - 1]; - } + meta_button_layout_free (layout); - meta_button_layout_free (layout); + return rtl_layout; + } - return rtl_layout; + return layout; } void meta_button_layout_free (MetaButtonLayout *layout) { + g_free (layout->left_buttons); + g_free (layout->right_buttons); + g_free (layout); } diff --git a/libmetacity/meta-button-enums.h b/libmetacity/meta-button.h index 4ef4505e..a087e147 100644 --- a/libmetacity/meta-button-enums.h +++ b/libmetacity/meta-button.h @@ -16,10 +16,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef META_BUTTON_ENUMS_H -#define META_BUTTON_ENUMS_H +#ifndef META_BUTTON_H +#define META_BUTTON_H -#include <glib.h> +#include <gdk/gdk.h> G_BEGIN_DECLS @@ -36,6 +36,7 @@ typedef enum META_BUTTON_TYPE_UNABOVE, META_BUTTON_TYPE_STICK, META_BUTTON_TYPE_UNSTICK, + META_BUTTON_TYPE_SPACER, META_BUTTON_TYPE_LAST } MetaButtonType; @@ -47,6 +48,26 @@ typedef enum META_BUTTON_STATE_LAST } MetaButtonState; +typedef struct +{ + MetaButtonType type; + MetaButtonState state; + + /* The computed size of a button (really just a way of tying its visible + * and clickable areas together). The reason for two different rectangles + * here is Fitts' law & maximized windows; See bug #97703 for more details. + */ + struct { + /* The area where the button's image is drawn. */ + GdkRectangle visible; + + /* The area where the button can be activated by clicking */ + GdkRectangle clickable; + } rect; + + gboolean visible; +} MetaButton; + G_END_DECLS #endif diff --git a/libmetacity/meta-frame-style-private.h b/libmetacity/meta-frame-style-private.h index f3351b34..380ac188 100644 --- a/libmetacity/meta-frame-style-private.h +++ b/libmetacity/meta-frame-style-private.h @@ -19,7 +19,7 @@ #ifndef META_FRAME_STYLE_PRIVATE_H #define META_FRAME_STYLE_PRIVATE_H -#include "meta-button-enums.h" +#include "meta-button.h" #include "meta-button-private.h" #include "meta-color-spec-private.h" #include "meta-draw-op-private.h" diff --git a/libmetacity/meta-theme-gtk.c b/libmetacity/meta-theme-gtk.c index 94fba18d..22a8f67f 100644 --- a/libmetacity/meta-theme-gtk.c +++ b/libmetacity/meta-theme-gtk.c @@ -288,120 +288,6 @@ meta_theme_gtk_get_frame_borders (MetaThemeImpl *impl, scale_border (&borders->total, scale); } -static MetaButtonSpace * -rect_for_type (MetaFrameGeometry *fgeom, - MetaFrameFlags flags, - MetaButtonType type) -{ - switch (type) - { - case META_BUTTON_TYPE_MENU: - if (flags & META_FRAME_ALLOWS_MENU) - return &fgeom->menu_rect; - else - return NULL; - - case META_BUTTON_TYPE_APPMENU: - if (flags & META_FRAME_ALLOWS_APPMENU) - return &fgeom->appmenu_rect; - else - return NULL; - - case META_BUTTON_TYPE_MINIMIZE: - if (flags & META_FRAME_ALLOWS_MINIMIZE) - return &fgeom->min_rect; - else - return NULL; - - case META_BUTTON_TYPE_MAXIMIZE: - if (flags & META_FRAME_ALLOWS_MAXIMIZE) - return &fgeom->max_rect; - else - return NULL; - - case META_BUTTON_TYPE_CLOSE: - if (flags & META_FRAME_ALLOWS_DELETE) - return &fgeom->close_rect; - else - return NULL; - - case META_BUTTON_TYPE_SHADE: - if ((flags & META_FRAME_ALLOWS_SHADE) && !(flags & META_FRAME_SHADED)) - return &fgeom->shade_rect; - else - return NULL; - - case META_BUTTON_TYPE_ABOVE: - if (!(flags & META_FRAME_ABOVE)) - return &fgeom->above_rect; - else - return NULL; - - case META_BUTTON_TYPE_STICK: - if (!(flags & META_FRAME_STUCK)) - return &fgeom->stick_rect; - else - return NULL; - - case META_BUTTON_TYPE_UNSHADE: - if ((flags & META_FRAME_ALLOWS_SHADE) && (flags & META_FRAME_SHADED)) - return &fgeom->unshade_rect; - else - return NULL; - - case META_BUTTON_TYPE_UNABOVE: - if (flags & META_FRAME_ABOVE) - return &fgeom->unabove_rect; - else - return NULL; - - case META_BUTTON_TYPE_UNSTICK: - if (flags & META_FRAME_STUCK) - return &fgeom->unstick_rect; - else - return NULL; - - case META_BUTTON_TYPE_LAST: - default: - break; - } - - return NULL; -} - -static gboolean -strip_button (MetaButtonSpace *func_rects[META_BUTTON_TYPE_LAST], - int *n_rects, - MetaButtonSpace *to_strip) -{ - int i; - - i = 0; - while (i < *n_rects) - { - if (func_rects[i] == to_strip) - { - *n_rects -= 1; - - /* shift the other rects back in the array */ - while (i < *n_rects) - { - func_rects[i] = func_rects[i+1]; - - ++i; - } - - func_rects[i] = NULL; - - return TRUE; - } - - ++i; - } - - return FALSE; /* did not strip anything */ -} - static void meta_theme_gtk_calc_geometry (MetaThemeImpl *impl, MetaFrameLayout *layout, @@ -425,14 +311,6 @@ meta_theme_gtk_calc_geometry (MetaThemeImpl *impl, int button_width, button_height; int min_size_for_rounding; - /* the left/right rects in order; the max # of rects - * is the number of button functions - */ - MetaButtonSpace *left_func_rects[META_BUTTON_TYPE_LAST]; - MetaButtonSpace *right_func_rects[META_BUTTON_TYPE_LAST]; - gboolean left_buttons_has_spacer[META_BUTTON_TYPE_LAST]; - gboolean right_buttons_has_spacer[META_BUTTON_TYPE_LAST]; - META_THEME_IMPL_GET_CLASS (impl)->get_frame_borders (impl, layout, style_info, text_height, flags, type, &borders); @@ -464,16 +342,6 @@ meta_theme_gtk_calc_geometry (MetaThemeImpl *impl, button_width *= scale; button_height *= scale; - /* FIXME all this code sort of pretends that duplicate buttons - * with the same function are allowed, but that breaks the - * code in frames.c, so isn't really allowed right now. - * Would need left_close_rect, right_close_rect, etc. - */ - - /* Init all button rects to 0, lame hack */ - memset (ADDRESS_OF_BUTTON_RECTS (fgeom), '\0', - LENGTH_OF_BUTTON_RECTS); - n_left = 0; n_right = 0; n_left_spacers = 0; @@ -481,35 +349,44 @@ meta_theme_gtk_calc_geometry (MetaThemeImpl *impl, if (!layout->hide_buttons) { - /* Try to fill in rects */ - for (i = 0; i < META_BUTTON_TYPE_LAST && button_layout->left_buttons[i] != META_BUTTON_TYPE_LAST; i++) + MetaButton *button; + + for (i = 0; i < button_layout->n_left_buttons; i++) { - left_func_rects[n_left] = rect_for_type (fgeom, flags, - button_layout->left_buttons[i]); - if (left_func_rects[n_left] != NULL) - { - left_buttons_has_spacer[n_left] = button_layout->left_buttons_has_spacer[i]; - if (button_layout->left_buttons_has_spacer[i]) - ++n_left_spacers; + button = &button_layout->left_buttons[i]; + button->visible = is_button_visible (button, flags); - ++n_left; + if (button->visible) + { + if (button->type != META_BUTTON_TYPE_SPACER) + n_left++; + else + n_left_spacers++; } } - for (i = 0; i < META_BUTTON_TYPE_LAST && button_layout->right_buttons[i] != META_BUTTON_TYPE_LAST; i++) + for (i = 0; i < button_layout->n_right_buttons; i++) { - right_func_rects[n_right] = rect_for_type (fgeom, flags, - button_layout->right_buttons[i]); - if (right_func_rects[n_right] != NULL) - { - right_buttons_has_spacer[n_right] = button_layout->right_buttons_has_spacer[i]; - if (button_layout->right_buttons_has_spacer[i]) - ++n_right_spacers; + button = &button_layout->right_buttons[i]; + button->visible = is_button_visible (button, flags); - ++n_right; + if (button->visible) + { + if (button->type != META_BUTTON_TYPE_SPACER) + n_left++; + else + n_left_spacers++; } } } + else + { + for (i = 0; i < button_layout->n_left_buttons; i++) + button_layout->left_buttons[i].visible = FALSE; + + for (i = 0; i < button_layout->n_right_buttons; i++) + button_layout->right_buttons[i].visible = FALSE; + } /* Be sure buttons fit */ while (n_left > 0 || n_right > 0) @@ -536,51 +413,41 @@ meta_theme_gtk_calc_geometry (MetaThemeImpl *impl, /* First try to remove separators */ if (n_left_spacers > 0) { - left_buttons_has_spacer[--n_left_spacers] = FALSE; - continue; + if (strip_button (button_layout->left_buttons, + button_layout->n_left_buttons, + META_BUTTON_TYPE_SPACER)) + { + n_left_spacers--; + continue; + } + else + { + g_assert_not_reached (); + } } else if (n_right_spacers > 0) { - right_buttons_has_spacer[--n_right_spacers] = FALSE; - continue; + if (strip_button (button_layout->right_buttons, + button_layout->n_right_buttons, + META_BUTTON_TYPE_SPACER)) + { + n_right_spacers--; + continue; + } + else + { + g_assert_not_reached (); + } } /* Otherwise we need to shave out a button. Shave * above, stick, shade, min, max, close, then menu (menu is most useful); * prefer the default button locations. */ - if (strip_button (left_func_rects, &n_left, &fgeom->above_rect)) - continue; - else if (strip_button (right_func_rects, &n_right, &fgeom->above_rect)) - continue; - else if (strip_button (left_func_rects, &n_left, &fgeom->stick_rect)) - continue; - else if (strip_button (right_func_rects, &n_right, &fgeom->stick_rect)) - continue; - else if (strip_button (left_func_rects, &n_left, &fgeom->shade_rect)) - continue; - else if (strip_button (right_func_rects, &n_right, &fgeom->shade_rect)) - continue; - else if (strip_button (left_func_rects, &n_left, &fgeom->min_rect)) - continue; - else if (strip_button (right_func_rects, &n_right, &fgeom->min_rect)) - continue; - else if (strip_button (left_func_rects, &n_left, &fgeom->max_rect)) - continue; - else if (strip_button (right_func_rects, &n_right, &fgeom->max_rect)) - continue; - else if (strip_button (left_func_rects, &n_left, &fgeom->close_rect)) - continue; - else if (strip_button (right_func_rects, &n_right, &fgeom->close_rect)) - continue; - else if (strip_button (right_func_rects, &n_right, &fgeom->menu_rect)) - continue; - else if (strip_button (left_func_rects, &n_left, &fgeom->menu_rect)) - continue; - else if (strip_button (right_func_rects, &n_right, &fgeom->appmenu_rect)) - continue; - else if (strip_button (left_func_rects, &n_left, &fgeom->appmenu_rect)) - continue; + if (strip_buttons (button_layout, &n_left, &n_right)) + { + continue; + } else { g_error ("Could not find a button to strip. n_left = %d n_right = %d", @@ -588,10 +455,6 @@ meta_theme_gtk_calc_geometry (MetaThemeImpl *impl, } } - /* Save the button layout */ - button_layout->n_left_buttons = n_left; - button_layout->n_right_buttons = n_right; - /* center buttons vertically */ button_y = borders.invisible.top + layout->gtk.frame_border.top * scale + (content_height - button_height) / 2; @@ -600,48 +463,61 @@ meta_theme_gtk_calc_geometry (MetaThemeImpl *impl, x = width - borders.invisible.right - layout->gtk.frame_border.right * scale - layout->gtk.titlebar_border.right * scale; - i = n_right - 1; - while (i >= 0) + for (i = button_layout->n_right_buttons - 1; i >= 0; i--) { - MetaButtonSpace *rect; + MetaButton *button; + GdkRectangle rect; + + button = &button_layout->right_buttons[i]; + + if (button->visible == FALSE) + continue; - if (x < 0) /* if we go negative, leave the buttons we don't get to as 0-width */ + /* if we go negative, leave the buttons we don't get to as 0 - width */ + if (x < 0) break; x -= layout->gtk.button_margin.right * scale; - rect = right_func_rects[i]; + rect.y = button_y; + rect.width = button_width; + rect.height = button_height; - rect->visible.x = x - button_width; - if (right_buttons_has_spacer[i]) - rect->visible.x -= (button_width * 0.75); + if (button->type == META_BUTTON_TYPE_SPACER) + { + rect.x = x - button_width * 0.75; + rect.width *= 0.75; + } + else + { + rect.x = x - button_width; + } - rect->visible.y = button_y; - rect->visible.width = button_width; - rect->visible.height = button_height; + button->rect.visible = rect; + button->rect.clickable = rect; - if (flags & META_FRAME_MAXIMIZED || - flags & META_FRAME_TILED_LEFT || - flags & META_FRAME_TILED_RIGHT) + if ((flags & META_FRAME_MAXIMIZED || flags & META_FRAME_TILED_RIGHT) && + i == button_layout->n_right_buttons - 1) { - rect->clickable.x = rect->visible.x; - rect->clickable.y = rect->visible.y; - rect->clickable.width = button_width; - rect->clickable.height = button_height; - - if (i == n_right - 1) - rect->clickable.width += layout->gtk.frame_border.right * scale + - layout->gtk.titlebar_border.right * scale; + gint extra_width; + gint extra_height; + + extra_width = layout->gtk.button_margin.right * scale + + layout->gtk.frame_border.right * scale + + layout->gtk.titlebar_border.right * scale; + + /* FIXME: */ + extra_height = 0; + + button->rect.clickable.y -= extra_height; + button->rect.clickable.width += extra_width; + button->rect.clickable.height += extra_height; } - else - g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable)); - x = rect->visible.x - layout->gtk.button_margin.left * scale; + x = rect.x - layout->gtk.button_margin.left * scale; if (i > 0) x -= layout->gtk.titlebar_spacing * scale; - - --i; } /* save right edge of titlebar for later use */ @@ -653,34 +529,50 @@ meta_theme_gtk_calc_geometry (MetaThemeImpl *impl, x = borders.invisible.left + layout->gtk.frame_border.left * scale + layout->gtk.titlebar_border.left * scale; - for (i = 0; i < n_left; i++) + for (i = 0; i < button_layout->n_left_buttons; i++) { - MetaButtonSpace *rect; + MetaButton *button; + GdkRectangle rect; + + button = &button_layout->left_buttons[i]; + + if (button->visible == FALSE) + continue; - x += layout->gtk.button_margin.left * scale; + rect.x = x + layout->gtk.button_margin.left * scale; + rect.y = button_y; + rect.width = button_width; + rect.height = button_height; - rect = left_func_rects[i]; + if (button->type == META_BUTTON_TYPE_SPACER) + rect.width *= 0.75; - rect->visible.x = x; - rect->visible.y = button_y; - rect->visible.width = button_width; - rect->visible.height = button_height; + button->rect.visible = rect; + button->rect.clickable = rect; - if (flags & META_FRAME_MAXIMIZED) + if ((flags & META_FRAME_MAXIMIZED || flags & META_FRAME_TILED_LEFT) && + i == 0) { - rect->clickable.x = rect->visible.x; - rect->clickable.y = rect->visible.y; - rect->clickable.width = button_width; - rect->clickable.height = button_height; + gint extra_width; + gint extra_height; + + extra_width = layout->gtk.button_margin.left * scale + + layout->gtk.frame_border.left * scale + + layout->gtk.titlebar_border.left * scale; + + /* FIXME: */ + extra_height = 0; + + button->rect.clickable.x -= extra_width; + button->rect.clickable.y -= extra_height; + button->rect.clickable.width += extra_width; + button->rect.clickable.height += extra_height; } - else - g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable)); - x = rect->visible.x + rect->visible.width + layout->gtk.button_margin.right * scale; - if (i < n_left - 1) + x = rect.x + rect.width + layout->gtk.button_margin.right * scale; + + if (i < button_layout->n_left_buttons - 1) x += layout->gtk.titlebar_spacing * scale; - if (left_buttons_has_spacer[i]) - x += (button_width * 0.75); } /* Center vertically in the available content area */ @@ -747,10 +639,10 @@ meta_theme_gtk_draw_frame (MetaThemeImpl *impl, gdouble scale; GtkStyleContext *context; GtkStateFlags state; - MetaButtonType button_type; MetaRectangleDouble visible_rect; MetaRectangleDouble titlebar_rect; const MetaFrameBorders *borders; + gint side; /* We opt out of GTK+ HiDPI handling, so we have to do the scaling * ourselves; the nitty-gritty is a bit confusing, so here is an overview: @@ -827,49 +719,73 @@ meta_theme_gtk_draw_frame (MetaThemeImpl *impl, context = meta_style_info_get_style (style_info, META_STYLE_ELEMENT_BUTTON); state = gtk_style_context_get_state (context); - for (button_type = 0; button_type < META_BUTTON_TYPE_LAST; button_type++) - { - MetaButtonState button_state; - const char *button_class; - GdkRectangle tmp_rect; - MetaRectangleDouble button_rect; - button_class = get_class_from_button_type (button_type); + for (side = 0; side < 2; side++) + { + MetaButton *buttons; + gint n_buttons; + gint i; - if (button_class) - gtk_style_context_add_class (context, button_class); + if (side == 0) + { + buttons = button_layout->left_buttons; + n_buttons = button_layout->n_left_buttons; + } + else if (side == 1) + { + buttons = button_layout->right_buttons; + n_buttons = button_layout->n_right_buttons; + } + else + { + g_assert_not_reached (); + } - button_state = button_layout->button_states[button_type]; + for (i = 0; i < n_buttons; i++) + { + MetaButton *button; + gdouble x; + gdouble y; + gdouble width; + gdouble height; + const gchar *button_class; + const gchar *icon_name; + GdkPixbuf *pixbuf; + + button = &buttons[i]; + + x = button->rect.visible.x / scale; + y = button->rect.visible.y / scale; + width = button->rect.visible.width / scale; + height = button->rect.visible.height / scale; + + if (!button->visible || button->type == META_BUTTON_TYPE_SPACER || + width <= 0 || height <= 0) + { + continue; + } - if (button_state == META_BUTTON_STATE_PRELIGHT) - gtk_style_context_set_state (context, state | GTK_STATE_PRELIGHT); - else if (button_state == META_BUTTON_STATE_PRESSED) - gtk_style_context_set_state (context, state | GTK_STATE_ACTIVE); - else - gtk_style_context_set_state (context, state); + button_class = get_class_from_button_type (button->type); - cairo_save (cr); + if (button_class) + gtk_style_context_add_class (context, button_class); - get_button_rect_for_type (button_type, fgeom, &tmp_rect); + if (button->state == META_BUTTON_STATE_PRELIGHT) + gtk_style_context_set_state (context, state | GTK_STATE_PRELIGHT); + else if (button->state == META_BUTTON_STATE_PRESSED) + gtk_style_context_set_state (context, state | GTK_STATE_ACTIVE); + else + gtk_style_context_set_state (context, state); - button_rect.x = tmp_rect.x / scale; - button_rect.y = tmp_rect.y / scale; - button_rect.width = tmp_rect.width / scale; - button_rect.height = tmp_rect.height / scale; + cairo_save (cr); - if (button_rect.width > 0 && button_rect.height > 0) - { - GdkPixbuf *pixbuf = NULL; - const char *icon_name = NULL; + gtk_render_background (context, cr, x, y, width, height); + gtk_render_frame (context, cr, x, y, width, height); - gtk_render_background (context, cr, - button_rect.x, button_rect.y, - button_rect.width, button_rect.height); - gtk_render_frame (context, cr, - button_rect.x, button_rect.y, - button_rect.width, button_rect.height); + icon_name = NULL; + pixbuf = NULL; - switch (button_type) + switch (button->type) { case META_BUTTON_TYPE_CLOSE: icon_name = "window-close-symbolic"; @@ -895,49 +811,58 @@ meta_theme_gtk_draw_frame (MetaThemeImpl *impl, case META_BUTTON_TYPE_UNSHADE: case META_BUTTON_TYPE_UNABOVE: case META_BUTTON_TYPE_UNSTICK: + case META_BUTTON_TYPE_SPACER: case META_BUTTON_TYPE_LAST: default: - icon_name = NULL; break; } if (icon_name) { - GtkIconTheme *theme = gtk_icon_theme_get_default (); + GtkIconTheme *theme; GtkIconInfo *info; + theme = gtk_icon_theme_get_default (); info = gtk_icon_theme_lookup_icon_for_scale (theme, icon_name, style->layout->gtk.icon_size, scale, 0); + g_assert (pixbuf == NULL); pixbuf = gtk_icon_info_load_symbolic_for_context (info, context, NULL, NULL); } if (pixbuf) { - float width, height; - gdouble x, y; - - width = gdk_pixbuf_get_width (pixbuf) / scale; - height = gdk_pixbuf_get_height (pixbuf) / scale; - x = button_rect.x + (button_rect.width - width) / 2; - y = button_rect.y + (button_rect.height - height) / 2; - - cairo_translate (cr, x, y); - cairo_scale (cr, - width / style->layout->gtk.icon_size / scale, - height / style->layout->gtk.icon_size / scale); + gdouble pwidth; + gdouble pheight; + gdouble px; + gdouble py; + gdouble scale_x; + gdouble scale_y; + + pwidth = gdk_pixbuf_get_width (pixbuf) / scale; + pheight = gdk_pixbuf_get_height (pixbuf) / scale; + px = x + (width - pwidth) / 2; + py = y + (height - pheight) / 2; + + scale_x = pwidth / style->layout->gtk.icon_size / scale; + scale_y = pheight / style->layout->gtk.icon_size / scale; + + cairo_translate (cr, px, py); + cairo_scale (cr, scale_x, scale_y); gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0); cairo_paint (cr); g_object_unref (pixbuf); } - } - cairo_restore (cr); + cairo_restore (cr); + + if (button_class) + gtk_style_context_remove_class (context, button_class); - if (button_class) - gtk_style_context_remove_class (context, button_class); + gtk_style_context_set_state (context, state); + } } } diff --git a/libmetacity/meta-theme-impl-private.h b/libmetacity/meta-theme-impl-private.h index 77cba2c5..1112fc3b 100644 --- a/libmetacity/meta-theme-impl-private.h +++ b/libmetacity/meta-theme-impl-private.h @@ -89,17 +89,26 @@ MetaFrameStyleSet *meta_theme_impl_get_style_set (MetaThemeImpl *impl MetaFrameType type); G_GNUC_INTERNAL -void get_button_rect_for_type (MetaButtonType type, - const MetaFrameGeometry *fgeom, - GdkRectangle *rect); - -G_GNUC_INTERNAL void scale_border (GtkBorder *border, double factor); G_GNUC_INTERNAL int get_window_scaling_factor (void); +G_GNUC_INTERNAL +gboolean is_button_visible (MetaButton *button, + MetaFrameFlags flags); + +G_GNUC_INTERNAL +gboolean strip_button (MetaButton *buttons, + gint n_buttons, + MetaButtonType type); + +G_GNUC_INTERNAL +gboolean strip_buttons (MetaButtonLayout *layout, + gint *n_left, + gint *n_right); + G_END_DECLS #endif diff --git a/libmetacity/meta-theme-impl.c b/libmetacity/meta-theme-impl.c index ac2735c5..1a1da9e9 100644 --- a/libmetacity/meta-theme-impl.c +++ b/libmetacity/meta-theme-impl.c @@ -132,64 +132,6 @@ meta_theme_impl_get_style_set (MetaThemeImpl *impl, } void -get_button_rect_for_type (MetaButtonType type, - const MetaFrameGeometry *fgeom, - GdkRectangle *rect) -{ - switch (type) - { - case META_BUTTON_TYPE_CLOSE: - *rect = fgeom->close_rect.visible; - break; - - case META_BUTTON_TYPE_SHADE: - *rect = fgeom->shade_rect.visible; - break; - - case META_BUTTON_TYPE_UNSHADE: - *rect = fgeom->unshade_rect.visible; - break; - - case META_BUTTON_TYPE_ABOVE: - *rect = fgeom->above_rect.visible; - break; - - case META_BUTTON_TYPE_UNABOVE: - *rect = fgeom->unabove_rect.visible; - break; - - case META_BUTTON_TYPE_STICK: - *rect = fgeom->stick_rect.visible; - break; - - case META_BUTTON_TYPE_UNSTICK: - *rect = fgeom->unstick_rect.visible; - break; - - case META_BUTTON_TYPE_MAXIMIZE: - *rect = fgeom->max_rect.visible; - break; - - case META_BUTTON_TYPE_MINIMIZE: - *rect = fgeom->min_rect.visible; - break; - - case META_BUTTON_TYPE_MENU: - *rect = fgeom->menu_rect.visible; - break; - - case META_BUTTON_TYPE_APPMENU: - *rect = fgeom->appmenu_rect.visible; - break; - - case META_BUTTON_TYPE_LAST: - default: - g_assert_not_reached (); - break; - } -} - -void scale_border (GtkBorder *border, double factor) { @@ -214,3 +156,140 @@ get_window_scaling_factor (void) else return 1; } + +gboolean +is_button_visible (MetaButton *button, + MetaFrameFlags flags) +{ + gboolean visible; + + visible = FALSE; + + switch (button->type) + { + case META_BUTTON_TYPE_MENU: + if (flags & META_FRAME_ALLOWS_MENU) + visible = TRUE; + break; + + case META_BUTTON_TYPE_APPMENU: + if (flags & META_FRAME_ALLOWS_APPMENU) + visible = TRUE; + break; + + case META_BUTTON_TYPE_MINIMIZE: + if (flags & META_FRAME_ALLOWS_MINIMIZE) + visible = TRUE; + break; + + case META_BUTTON_TYPE_MAXIMIZE: + if (flags & META_FRAME_ALLOWS_MAXIMIZE) + visible = TRUE; + break; + + case META_BUTTON_TYPE_CLOSE: + if (flags & META_FRAME_ALLOWS_DELETE) + visible = TRUE; + break; + + case META_BUTTON_TYPE_SHADE: + if ((flags & META_FRAME_ALLOWS_SHADE) && !(flags & META_FRAME_SHADED)) + visible = TRUE; + break; + + case META_BUTTON_TYPE_ABOVE: + if (!(flags & META_FRAME_ABOVE)) + visible = TRUE; + break; + + case META_BUTTON_TYPE_STICK: + if (!(flags & META_FRAME_STUCK)) + visible = TRUE; + break; + + case META_BUTTON_TYPE_UNSHADE: + if ((flags & META_FRAME_ALLOWS_SHADE) && (flags & META_FRAME_SHADED)) + visible = TRUE; + break; + + case META_BUTTON_TYPE_UNABOVE: + if (flags & META_FRAME_ABOVE) + visible = TRUE; + break; + + case META_BUTTON_TYPE_UNSTICK: + if (flags & META_FRAME_STUCK) + visible = TRUE; + break; + + case META_BUTTON_TYPE_SPACER: + visible = TRUE; + break; + + case META_BUTTON_TYPE_LAST: + default: + break; + } + + return visible; +} + +gboolean +strip_button (MetaButton *buttons, + gint n_buttons, + MetaButtonType type) +{ + gint i; + + for (i = 0; i < n_buttons; i++) + { + if (buttons[i].type == type && buttons[i].visible) + { + buttons[i].visible = FALSE; + return TRUE; + } + } + + return FALSE; +} + +gboolean +strip_buttons (MetaButtonLayout *layout, + gint *n_left, + gint *n_right) +{ + gint count; + MetaButtonType types[META_BUTTON_TYPE_LAST]; + gint i; + + count = 0; + types[count++] = META_BUTTON_TYPE_ABOVE; + types[count++] = META_BUTTON_TYPE_UNABOVE; + types[count++] = META_BUTTON_TYPE_STICK; + types[count++] = META_BUTTON_TYPE_UNSTICK; + types[count++] = META_BUTTON_TYPE_SHADE; + types[count++] = META_BUTTON_TYPE_UNSHADE; + types[count++] = META_BUTTON_TYPE_MINIMIZE; + types[count++] = META_BUTTON_TYPE_MAXIMIZE; + types[count++] = META_BUTTON_TYPE_CLOSE; + types[count++] = META_BUTTON_TYPE_MENU; + types[count++] = META_BUTTON_TYPE_APPMENU; + + for (i = 0; i < count; i++) + { + if (strip_button (layout->left_buttons, layout->n_left_buttons, + types[i])) + { + *n_left -= 1; + return TRUE; + } + else if (strip_button (layout->right_buttons, layout->n_right_buttons, + types[i])) + { + *n_left -= 1; + return TRUE; + } + } + + return FALSE; +} diff --git a/libmetacity/meta-theme-metacity.c b/libmetacity/meta-theme-metacity.c index 7d41ec20..d39a2ddb 100644 --- a/libmetacity/meta-theme-metacity.c +++ b/libmetacity/meta-theme-metacity.c @@ -4697,57 +4697,28 @@ meta_theme_metacity_get_frame_borders (MetaThemeImpl *impl, scale_border (&borders->total, scale); } -static MetaButtonSpace * -rect_for_type (MetaThemeMetacity *metacity, - MetaFrameGeometry *fgeom, - MetaFrameFlags flags, - MetaButtonType type) +static gboolean +is_button_allowed (MetaThemeMetacity *metacity, + MetaButtonType type) { if (theme_allows (metacity, META_THEME_SHADE_STICK_ABOVE_BUTTONS)) { switch (type) { case META_BUTTON_TYPE_SHADE: - if ((flags & META_FRAME_ALLOWS_SHADE) && !(flags & META_FRAME_SHADED)) - return &fgeom->shade_rect; - else - return NULL; - case META_BUTTON_TYPE_ABOVE: - if (!(flags & META_FRAME_ABOVE)) - return &fgeom->above_rect; - else - return NULL; - case META_BUTTON_TYPE_STICK: - if (!(flags & META_FRAME_STUCK)) - return &fgeom->stick_rect; - else - return NULL; - case META_BUTTON_TYPE_UNSHADE: - if ((flags & META_FRAME_ALLOWS_SHADE) && (flags & META_FRAME_SHADED)) - return &fgeom->unshade_rect; - else - return NULL; - case META_BUTTON_TYPE_UNABOVE: - if (flags & META_FRAME_ABOVE) - return &fgeom->unabove_rect; - else - return NULL; - case META_BUTTON_TYPE_UNSTICK: - if (flags & META_FRAME_STUCK) - return &fgeom->unstick_rect; - else - return NULL; + return TRUE; case META_BUTTON_TYPE_MENU: case META_BUTTON_TYPE_APPMENU: case META_BUTTON_TYPE_MINIMIZE: case META_BUTTON_TYPE_MAXIMIZE: case META_BUTTON_TYPE_CLOSE: + case META_BUTTON_TYPE_SPACER: case META_BUTTON_TYPE_LAST: default: break; @@ -4758,34 +4729,12 @@ rect_for_type (MetaThemeMetacity *metacity, switch (type) { case META_BUTTON_TYPE_MENU: - if (flags & META_FRAME_ALLOWS_MENU) - return &fgeom->menu_rect; - else - return NULL; - case META_BUTTON_TYPE_APPMENU: - if (flags & META_FRAME_ALLOWS_APPMENU) - return &fgeom->appmenu_rect; - else - return NULL; - case META_BUTTON_TYPE_MINIMIZE: - if (flags & META_FRAME_ALLOWS_MINIMIZE) - return &fgeom->min_rect; - else - return NULL; - case META_BUTTON_TYPE_MAXIMIZE: - if (flags & META_FRAME_ALLOWS_MAXIMIZE) - return &fgeom->max_rect; - else - return NULL; - case META_BUTTON_TYPE_CLOSE: - if (flags & META_FRAME_ALLOWS_DELETE) - return &fgeom->close_rect; - else - return NULL; + case META_BUTTON_TYPE_SPACER: + return TRUE; case META_BUTTON_TYPE_STICK: case META_BUTTON_TYPE_SHADE: @@ -4798,50 +4747,14 @@ rect_for_type (MetaThemeMetacity *metacity, * therefore, we don't show the button. return NULL and all will * be well. */ - return NULL; + break; case META_BUTTON_TYPE_LAST: default: break; } - return NULL; -} - -static gboolean -strip_button (MetaButtonSpace *func_rects[META_BUTTON_TYPE_LAST], - GdkRectangle *bg_rects[META_BUTTON_TYPE_LAST], - int *n_rects, - MetaButtonSpace *to_strip) -{ - int i; - - i = 0; - while (i < *n_rects) - { - if (func_rects[i] == to_strip) - { - *n_rects -= 1; - - /* shift the other rects back in the array */ - while (i < *n_rects) - { - func_rects[i] = func_rects[i+1]; - bg_rects[i] = bg_rects[i+1]; - - ++i; - } - - func_rects[i] = NULL; - bg_rects[i] = NULL; - - return TRUE; - } - - ++i; - } - - return FALSE; /* did not strip anything */ + return FALSE; } static void @@ -4867,16 +4780,6 @@ meta_theme_metacity_calc_geometry (MetaThemeImpl *impl, int min_size_for_rounding; int scale; - /* the left/right rects in order; the max # of rects - * is the number of button functions - */ - MetaButtonSpace *left_func_rects[META_BUTTON_TYPE_LAST]; - MetaButtonSpace *right_func_rects[META_BUTTON_TYPE_LAST]; - GdkRectangle *left_bg_rects[META_BUTTON_TYPE_LAST]; - gboolean left_buttons_has_spacer[META_BUTTON_TYPE_LAST]; - GdkRectangle *right_bg_rects[META_BUTTON_TYPE_LAST]; - gboolean right_buttons_has_spacer[META_BUTTON_TYPE_LAST]; - META_THEME_IMPL_GET_CLASS (impl)->get_frame_borders (impl, layout, style_info, text_height, flags, type, &borders); @@ -4914,16 +4817,6 @@ meta_theme_metacity_calc_geometry (MetaThemeImpl *impl, break; } - /* FIXME all this code sort of pretends that duplicate buttons - * with the same function are allowed, but that breaks the - * code in frames.c, so isn't really allowed right now. - * Would need left_close_rect, right_close_rect, etc. - */ - - /* Init all button rects to 0, lame hack */ - memset (ADDRESS_OF_BUTTON_RECTS (fgeom), '\0', - LENGTH_OF_BUTTON_RECTS); - n_left = 0; n_right = 0; n_left_spacers = 0; @@ -4933,64 +4826,45 @@ meta_theme_metacity_calc_geometry (MetaThemeImpl *impl, if (!layout->hide_buttons) { - /* Try to fill in rects */ - for (i = 0; i < META_BUTTON_TYPE_LAST && button_layout->left_buttons[i] != META_BUTTON_TYPE_LAST; i++) + MetaButton *button; + + for (i = 0; i < button_layout->n_left_buttons; i++) { - left_func_rects[n_left] = rect_for_type (metacity, fgeom, flags, - button_layout->left_buttons[i]); - if (left_func_rects[n_left] != NULL) - { - left_buttons_has_spacer[n_left] = button_layout->left_buttons_has_spacer[i]; - if (button_layout->left_buttons_has_spacer[i]) - ++n_left_spacers; + button = &button_layout->left_buttons[i]; + button->visible = is_button_visible (button, flags) && + is_button_allowed (metacity, button->type); - ++n_left; + if (button->visible) + { + if (button->type != META_BUTTON_TYPE_SPACER) + n_left++; + else + n_left_spacers++; } } - for (i = 0; i < META_BUTTON_TYPE_LAST && button_layout->right_buttons[i] != META_BUTTON_TYPE_LAST; i++) + for (i = 0; i < button_layout->n_right_buttons; i++) { - right_func_rects[n_right] = rect_for_type (metacity, fgeom, flags, - button_layout->right_buttons[i]); - if (right_func_rects[n_right] != NULL) - { - right_buttons_has_spacer[n_right] = button_layout->right_buttons_has_spacer[i]; - if (button_layout->right_buttons_has_spacer[i]) - ++n_right_spacers; + button = &button_layout->right_buttons[i]; + button->visible = is_button_visible (button, flags) && + is_button_allowed (metacity, button->type); - ++n_right; + if (button->visible) + { + if (button->type != META_BUTTON_TYPE_SPACER) + n_left++; + else + n_left_spacers++; } } } - - for (i = 0; i < META_BUTTON_TYPE_LAST; i++) + else { - left_bg_rects[i] = NULL; - right_bg_rects[i] = NULL; - } + for (i = 0; i < button_layout->n_left_buttons; i++) + button_layout->left_buttons[i].visible = FALSE; - for (i = 0; i < n_left; i++) - { - if (n_left == 1) - left_bg_rects[i] = &fgeom->left_single_background; - else if (i == 0) - left_bg_rects[i] = &fgeom->left_left_background; - else if (i == (n_left - 1)) - left_bg_rects[i] = &fgeom->left_right_background; - else - left_bg_rects[i] = &fgeom->left_middle_backgrounds[i - 1]; - } - - for (i = 0; i < n_right; i++) - { - if (n_right == 1) - right_bg_rects[i] = &fgeom->right_single_background; - else if (i == (n_right - 1)) - right_bg_rects[i] = &fgeom->right_right_background; - else if (i == 0) - right_bg_rects[i] = &fgeom->right_left_background; - else - right_bg_rects[i] = &fgeom->right_middle_backgrounds[i - 1]; + for (i = 0; i < button_layout->n_right_buttons; i++) + button_layout->right_buttons[i].visible = FALSE; } /* Be sure buttons fit */ @@ -5021,67 +4895,41 @@ meta_theme_metacity_calc_geometry (MetaThemeImpl *impl, /* First try to remove separators */ if (n_left_spacers > 0) { - left_buttons_has_spacer[--n_left_spacers] = FALSE; - continue; + if (strip_button (button_layout->left_buttons, + button_layout->n_left_buttons, + META_BUTTON_TYPE_SPACER)) + { + n_left_spacers--; + continue; + } + else + { + g_assert_not_reached (); + } } else if (n_right_spacers > 0) { - right_buttons_has_spacer[--n_right_spacers] = FALSE; - continue; + if (strip_button (button_layout->right_buttons, + button_layout->n_right_buttons, + META_BUTTON_TYPE_SPACER)) + { + n_right_spacers--; + continue; + } + else + { + g_assert_not_reached (); + } } /* Otherwise we need to shave out a button. Shave * above, stick, shade, min, max, close, then menu (menu is most useful); * prefer the default button locations. */ - if (strip_button (left_func_rects, left_bg_rects, - &n_left, &fgeom->above_rect)) - continue; - else if (strip_button (right_func_rects, right_bg_rects, - &n_right, &fgeom->above_rect)) - continue; - else if (strip_button (left_func_rects, left_bg_rects, - &n_left, &fgeom->stick_rect)) - continue; - else if (strip_button (right_func_rects, right_bg_rects, - &n_right, &fgeom->stick_rect)) - continue; - else if (strip_button (left_func_rects, left_bg_rects, - &n_left, &fgeom->shade_rect)) - continue; - else if (strip_button (right_func_rects, right_bg_rects, - &n_right, &fgeom->shade_rect)) - continue; - else if (strip_button (left_func_rects, left_bg_rects, - &n_left, &fgeom->min_rect)) - continue; - else if (strip_button (right_func_rects, right_bg_rects, - &n_right, &fgeom->min_rect)) - continue; - else if (strip_button (left_func_rects, left_bg_rects, - &n_left, &fgeom->max_rect)) - continue; - else if (strip_button (right_func_rects, right_bg_rects, - &n_right, &fgeom->max_rect)) - continue; - else if (strip_button (left_func_rects, left_bg_rects, - &n_left, &fgeom->close_rect)) - continue; - else if (strip_button (right_func_rects, right_bg_rects, - &n_right, &fgeom->close_rect)) - continue; - else if (strip_button (right_func_rects, right_bg_rects, - &n_right, &fgeom->menu_rect)) - continue; - else if (strip_button (left_func_rects, left_bg_rects, - &n_left, &fgeom->menu_rect)) - continue; - else if (strip_button (right_func_rects, right_bg_rects, - &n_right, &fgeom->appmenu_rect)) - continue; - else if (strip_button (left_func_rects, left_bg_rects, - &n_left, &fgeom->appmenu_rect)) - continue; + if (strip_buttons (button_layout, &n_left, &n_right)) + { + continue; + } else { g_error ("Could not find a button to strip. n_left = %d n_right = %d", @@ -5089,10 +4937,6 @@ meta_theme_metacity_calc_geometry (MetaThemeImpl *impl, } } - /* Save the button layout */ - button_layout->n_left_buttons = n_left; - button_layout->n_right_buttons = n_right; - /* center buttons vertically */ button_y = (borders.visible.top - (button_height + layout->button_border.top * scale + layout->button_border.bottom * scale)) / 2 + layout->button_border.top * scale + borders.invisible.top; @@ -5100,45 +4944,56 @@ meta_theme_metacity_calc_geometry (MetaThemeImpl *impl, /* right edge of farthest-right button */ x = width - layout->metacity.right_titlebar_edge * scale - borders.invisible.right; - i = n_right - 1; - while (i >= 0) + for (i = button_layout->n_right_buttons - 1; i >= 0; i--) { - MetaButtonSpace *rect; + MetaButton *button; + GdkRectangle rect; - if (x < 0) /* if we go negative, leave the buttons we don't get to as 0-width */ - break; + button = &button_layout->right_buttons[i]; + + if (button->visible == FALSE) + continue; - rect = right_func_rects[i]; - rect->visible.x = x - layout->button_border.right * scale - button_width; - if (right_buttons_has_spacer[i]) - rect->visible.x -= (button_width * 0.75); + /* if we go negative, leave the buttons we don't get to as 0 - width */ + if (x < 0) + break; - rect->visible.y = button_y; - rect->visible.width = button_width; - rect->visible.height = button_height; + rect.y = button_y; + rect.width = button_width; + rect.height = button_height; - if (flags & META_FRAME_MAXIMIZED || - flags & META_FRAME_TILED_LEFT || - flags & META_FRAME_TILED_RIGHT) + if (button->type == META_BUTTON_TYPE_SPACER) { - rect->clickable.x = rect->visible.x; - rect->clickable.y = rect->visible.y; - rect->clickable.width = button_width; - rect->clickable.height = button_height; - - if (i == n_right - 1) - rect->clickable.width += layout->metacity.right_titlebar_edge * scale + - layout->metacity.right_width * scale + - layout->button_border.right * scale; + rect.x = x - button_width * 0.75; + rect.width *= 0.75; } else - g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable)); + { + rect.x = x - layout->button_border.right * scale - button_width; + } + + button->rect.visible = rect; + button->rect.clickable = rect; - *(right_bg_rects[i]) = rect->visible; + if ((flags & META_FRAME_MAXIMIZED || flags & META_FRAME_TILED_RIGHT) && + i == button_layout->n_right_buttons - 1) + { + gint extra_width; + gint extra_height; + + extra_width = layout->metacity.right_titlebar_edge * scale + + layout->metacity.right_width * scale + + layout->button_border.right * scale; - x = rect->visible.x - layout->button_border.left * scale; + /* FIXME: */ + extra_height = 0; + + button->rect.clickable.y -= extra_height; + button->rect.clickable.width += extra_width; + button->rect.clickable.height += extra_height; + } - --i; + x = rect.x - layout->button_border.left * scale; } /* save right edge of titlebar for later use */ @@ -5148,32 +5003,48 @@ meta_theme_metacity_calc_geometry (MetaThemeImpl *impl, * the left-side buttons */ x = layout->metacity.left_titlebar_edge * scale + borders.invisible.left; - for (i = 0; i < n_left; i++) + + for (i = 0; i < button_layout->n_left_buttons; i++) { - MetaButtonSpace *rect; + MetaButton *button; + GdkRectangle rect; + + button = &button_layout->left_buttons[i]; + + if (button->visible == FALSE) + continue; - rect = left_func_rects[i]; + rect.x = x + layout->button_border.left * scale;; + rect.y = button_y; + rect.width = button_width; + rect.height = button_height; - rect->visible.x = x + layout->button_border.left * scale; - rect->visible.y = button_y; - rect->visible.width = button_width; - rect->visible.height = button_height; + if (button->type == META_BUTTON_TYPE_SPACER) + rect.width *= 0.75; - if (flags & META_FRAME_MAXIMIZED) + button->rect.visible = rect; + button->rect.clickable = rect; + + if ((flags & META_FRAME_MAXIMIZED || flags & META_FRAME_TILED_LEFT) && + i == 0) { - rect->clickable.x = rect->visible.x; - rect->clickable.y = rect->visible.y; - rect->clickable.width = button_width; - rect->clickable.height = button_height; - } - else - g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable)); + gint extra_width; + gint extra_height; + + extra_width = layout->metacity.left_titlebar_edge * scale + + layout->metacity.left_width * scale + + layout->button_border.left * scale; - x = rect->visible.x + rect->visible.width + layout->button_border.right * scale; - if (left_buttons_has_spacer[i]) - x += (button_width * 0.75); + /* FIXME: */ + extra_height = 0; - *(left_bg_rects[i]) = rect->visible; + button->rect.clickable.x -= extra_width; + button->rect.clickable.y -= extra_height; + button->rect.clickable.width += extra_width; + button->rect.clickable.height += extra_height; + } + + x = rect.x + rect.width + layout->button_border.right * scale; } /* We always fill as much vertical space as possible with title rect, @@ -5286,179 +5157,111 @@ clip_to_rounded_corners (cairo_t *cr, cairo_clip (cr); } -static void -get_button_rect (MetaButtonFunction function, - const MetaFrameGeometry *fgeom, - gint middle_background_offset, - GdkRectangle *rect) +static MetaButtonFunction +get_button_function (MetaButtonType type, + gboolean background, + gint button, + gint n_buttons, + gint side) { - switch (function) + if (background) { - case META_BUTTON_FUNCTION_LEFT_LEFT_BACKGROUND: - *rect = fgeom->left_left_background; - break; - - case META_BUTTON_FUNCTION_LEFT_MIDDLE_BACKGROUND: - *rect = fgeom->left_middle_backgrounds[middle_background_offset]; - break; - - case META_BUTTON_FUNCTION_LEFT_RIGHT_BACKGROUND: - *rect = fgeom->left_right_background; - break; - - case META_BUTTON_FUNCTION_LEFT_SINGLE_BACKGROUND: - *rect = fgeom->left_single_background; - break; - - case META_BUTTON_FUNCTION_RIGHT_LEFT_BACKGROUND: - *rect = fgeom->right_left_background; - break; - - case META_BUTTON_FUNCTION_RIGHT_MIDDLE_BACKGROUND: - *rect = fgeom->right_middle_backgrounds[middle_background_offset]; - break; - - case META_BUTTON_FUNCTION_RIGHT_RIGHT_BACKGROUND: - *rect = fgeom->right_right_background; - break; - - case META_BUTTON_FUNCTION_RIGHT_SINGLE_BACKGROUND: - *rect = fgeom->right_single_background; - break; - - case META_BUTTON_FUNCTION_CLOSE: - *rect = fgeom->close_rect.visible; - break; - - case META_BUTTON_FUNCTION_SHADE: - *rect = fgeom->shade_rect.visible; - break; - - case META_BUTTON_FUNCTION_UNSHADE: - *rect = fgeom->unshade_rect.visible; - break; - - case META_BUTTON_FUNCTION_ABOVE: - *rect = fgeom->above_rect.visible; - break; + if (side == 0) /* left */ + { + if (n_buttons == 1) + { + return META_BUTTON_FUNCTION_LEFT_SINGLE_BACKGROUND; + } + else if (n_buttons == 2) + { + if (button == 0) + return META_BUTTON_FUNCTION_LEFT_LEFT_BACKGROUND; + else + return META_BUTTON_FUNCTION_LEFT_RIGHT_BACKGROUND; + } + else if (n_buttons > 2) + { + if (button == 0) + return META_BUTTON_FUNCTION_LEFT_LEFT_BACKGROUND; + else if (button == n_buttons - 1) + return META_BUTTON_FUNCTION_LEFT_RIGHT_BACKGROUND; + else + return META_BUTTON_FUNCTION_LEFT_MIDDLE_BACKGROUND; + } + } + else if (side == 1) /* right */ + { + if (n_buttons == 1) + { + return META_BUTTON_FUNCTION_RIGHT_SINGLE_BACKGROUND; + } + else if (n_buttons == 2) + { + if (button == 0) + return META_BUTTON_FUNCTION_RIGHT_LEFT_BACKGROUND; + else + return META_BUTTON_FUNCTION_RIGHT_RIGHT_BACKGROUND; + } + else if (n_buttons > 2) + { + if (button == 0) + return META_BUTTON_FUNCTION_RIGHT_LEFT_BACKGROUND; + else if (button == n_buttons - 1) + return META_BUTTON_FUNCTION_RIGHT_RIGHT_BACKGROUND; + else + return META_BUTTON_FUNCTION_RIGHT_MIDDLE_BACKGROUND; + } + } + else + { + g_assert_not_reached (); + } + } + else + { + switch (type) + { + case META_BUTTON_TYPE_SHADE: + return META_BUTTON_FUNCTION_SHADE; - case META_BUTTON_FUNCTION_UNABOVE: - *rect = fgeom->unabove_rect.visible; - break; + case META_BUTTON_TYPE_UNSHADE: + return META_BUTTON_FUNCTION_UNSHADE; - case META_BUTTON_FUNCTION_STICK: - *rect = fgeom->stick_rect.visible; - break; + case META_BUTTON_TYPE_ABOVE: + return META_BUTTON_FUNCTION_ABOVE; - case META_BUTTON_FUNCTION_UNSTICK: - *rect = fgeom->unstick_rect.visible; - break; + case META_BUTTON_TYPE_UNABOVE: + return META_BUTTON_FUNCTION_UNABOVE; - case META_BUTTON_FUNCTION_MAXIMIZE: - *rect = fgeom->max_rect.visible; - break; + case META_BUTTON_TYPE_STICK: + return META_BUTTON_FUNCTION_STICK; - case META_BUTTON_FUNCTION_MINIMIZE: - *rect = fgeom->min_rect.visible; - break; + case META_BUTTON_TYPE_UNSTICK: + return META_BUTTON_FUNCTION_UNSTICK; - case META_BUTTON_FUNCTION_MENU: - *rect = fgeom->menu_rect.visible; - break; + case META_BUTTON_TYPE_MENU: + return META_BUTTON_FUNCTION_MENU; - case META_BUTTON_FUNCTION_APPMENU: - *rect = fgeom->appmenu_rect.visible; - break; + case META_BUTTON_TYPE_APPMENU: + return META_BUTTON_FUNCTION_APPMENU; - case META_BUTTON_FUNCTION_LAST: - default: - g_assert_not_reached (); - break; - } -} + case META_BUTTON_TYPE_MINIMIZE: + return META_BUTTON_FUNCTION_MINIMIZE; -static MetaButtonState -map_button_state (MetaButtonFunction button_function, - const MetaFrameGeometry *fgeom, - gint middle_bg_offset, - const MetaButtonLayout *button_layout) -{ - MetaButtonType type = META_BUTTON_TYPE_LAST; + case META_BUTTON_TYPE_MAXIMIZE: + return META_BUTTON_FUNCTION_MAXIMIZE; - switch (button_function) - { - /* First handle types, which map directly */ - case META_BUTTON_FUNCTION_SHADE: - type = META_BUTTON_TYPE_SHADE; - break; - case META_BUTTON_FUNCTION_ABOVE: - type = META_BUTTON_TYPE_ABOVE; - break; - case META_BUTTON_FUNCTION_STICK: - type = META_BUTTON_TYPE_STICK; - break; - case META_BUTTON_FUNCTION_UNSHADE: - type = META_BUTTON_TYPE_UNSHADE; - break; - case META_BUTTON_FUNCTION_UNABOVE: - type = META_BUTTON_TYPE_UNABOVE; - break;; - case META_BUTTON_FUNCTION_UNSTICK: - type = META_BUTTON_TYPE_UNSTICK; - break; - case META_BUTTON_FUNCTION_MENU: - type = META_BUTTON_TYPE_MENU; - break; - case META_BUTTON_FUNCTION_APPMENU: - type = META_BUTTON_TYPE_APPMENU; - break; - case META_BUTTON_FUNCTION_MINIMIZE: - type = META_BUTTON_TYPE_MINIMIZE; - break; - case META_BUTTON_FUNCTION_MAXIMIZE: - type = META_BUTTON_TYPE_MAXIMIZE; - break; - case META_BUTTON_FUNCTION_CLOSE: - type = META_BUTTON_TYPE_CLOSE; - break; + case META_BUTTON_TYPE_CLOSE: + return META_BUTTON_FUNCTION_CLOSE; - /* Map position buttons to the corresponding type */ - case META_BUTTON_FUNCTION_RIGHT_LEFT_BACKGROUND: - case META_BUTTON_FUNCTION_RIGHT_SINGLE_BACKGROUND: - if (button_layout->n_right_buttons > 0) - type = button_layout->right_buttons[0]; - break; - case META_BUTTON_FUNCTION_RIGHT_RIGHT_BACKGROUND: - if (button_layout->n_right_buttons > 0) - type = button_layout->right_buttons[button_layout->n_right_buttons - 1]; - break; - case META_BUTTON_FUNCTION_RIGHT_MIDDLE_BACKGROUND: - if (middle_bg_offset + 1 < button_layout->n_right_buttons) - type = button_layout->right_buttons[middle_bg_offset + 1]; - break; - case META_BUTTON_FUNCTION_LEFT_LEFT_BACKGROUND: - case META_BUTTON_FUNCTION_LEFT_SINGLE_BACKGROUND: - if (button_layout->n_left_buttons > 0) - type = button_layout->left_buttons[0]; - break; - case META_BUTTON_FUNCTION_LEFT_RIGHT_BACKGROUND: - if (button_layout->n_left_buttons > 0) - type = button_layout->left_buttons[button_layout->n_left_buttons - 1]; - break; - case META_BUTTON_FUNCTION_LEFT_MIDDLE_BACKGROUND: - if (middle_bg_offset + 1 < button_layout->n_left_buttons) - type = button_layout->left_buttons[middle_bg_offset + 1]; - break; - case META_BUTTON_FUNCTION_LAST: - break; - default: - break; + case META_BUTTON_TYPE_SPACER: + case META_BUTTON_TYPE_LAST: + default: + break; + } } - if (type != META_BUTTON_TYPE_LAST) - return button_layout->button_states[type]; - - return META_BUTTON_STATE_LAST; + return META_BUTTON_FUNCTION_LAST; } static void @@ -5474,7 +5277,7 @@ meta_theme_metacity_draw_frame (MetaThemeImpl *impl, GdkPixbuf *icon) { gdouble scale; - int i, j; + gint i; MetaRectangleDouble visible_rect; MetaRectangleDouble titlebar_rect; MetaRectangleDouble left_titlebar_edge; @@ -5685,55 +5488,78 @@ meta_theme_metacity_draw_frame (MetaThemeImpl *impl, /* Draw buttons just before overlay */ if ((i + 1) == META_FRAME_PIECE_OVERLAY) { - MetaDrawOpList *op_list; - int middle_bg_offset; + gint side; - middle_bg_offset = 0; - j = 0; - while (j < META_BUTTON_FUNCTION_LAST) + for (side = 0; side < 2; side++) { - GdkRectangle tmp_rect; - MetaButtonState button_state; + MetaButton *buttons; + gint n_buttons; + gint j; - get_button_rect (j, fgeom, middle_bg_offset, &tmp_rect); - - rect.x = tmp_rect.x / scale; - rect.y = tmp_rect.y / scale; - rect.width = tmp_rect.width / scale; - rect.height = tmp_rect.height / scale; - - button_state = map_button_state (j, fgeom, middle_bg_offset, - button_layout); - - op_list = meta_frame_style_get_button (style, j, button_state); + if (side == 0) + { + buttons = button_layout->left_buttons; + n_buttons = button_layout->n_left_buttons; + } + else if (side == 1) + { + buttons = button_layout->right_buttons; + n_buttons = button_layout->n_right_buttons; + } + else + { + g_assert_not_reached (); + } - if (op_list) + for (j = 0; j < n_buttons; j++) { - cairo_save (cr); + MetaButton *button; + gint op; - cairo_rectangle (cr, rect.x, rect.y, rect.width, rect.height); - cairo_clip (cr); + button = &buttons[j]; - if (gdk_cairo_get_clip_rectangle (cr, NULL)) + rect.x = button->rect.visible.x / scale; + rect.y = button->rect.visible.y / scale; + rect.width = button->rect.visible.width / scale; + rect.height = button->rect.visible.height / scale; + + if (!button->visible || + button->type == META_BUTTON_TYPE_SPACER || + rect.width <= 0 || rect.height <= 0) { - meta_draw_op_list_draw_with_style (op_list, context, cr, - &draw_info, rect); + continue; } - cairo_restore (cr); - } + for (op = 0; op < 2; op++) + { + MetaButtonFunction function; + MetaDrawOpList *op_list; - /* MIDDLE_BACKGROUND type may get drawn more than once */ - if ((j == META_BUTTON_FUNCTION_RIGHT_MIDDLE_BACKGROUND || - j == META_BUTTON_FUNCTION_LEFT_MIDDLE_BACKGROUND) && - (middle_bg_offset < (MAX_MIDDLE_BACKGROUNDS - 1))) - { - ++middle_bg_offset; - } - else - { - middle_bg_offset = 0; - ++j; + function = get_button_function (button->type, op == 0, + j, n_buttons, op); + + op_list = meta_frame_style_get_button (style, function, + button->state); + + if (op_list) + { + cairo_save (cr); + + cairo_rectangle (cr, rect.x, rect.y, + rect.width, rect.height); + cairo_clip (cr); + + if (gdk_cairo_get_clip_rectangle (cr, NULL)) + { + meta_draw_op_list_draw_with_style (op_list, + context, cr, + &draw_info, + rect); + } + + cairo_restore (cr); + } + } } } } diff --git a/libmetacity/meta-theme.c b/libmetacity/meta-theme.c index efabb3f5..7084ef49 100644 --- a/libmetacity/meta-theme.c +++ b/libmetacity/meta-theme.c @@ -570,6 +570,63 @@ meta_theme_set_button_layout (MetaTheme *theme, theme->button_layout = meta_button_layout_new (button_layout, invert); } +gboolean +meta_theme_get_button (MetaTheme *theme, + gint x, + gint y, + MetaButton *button) +{ + gint side; + + for (side = 0; side < 2; side++) + { + MetaButton *buttons; + gint n_buttons; + gint i; + + if (side == 0) + { + buttons = theme->button_layout->left_buttons; + n_buttons = theme->button_layout->n_left_buttons; + } + else if (side == 1) + { + buttons = theme->button_layout->right_buttons; + n_buttons = theme->button_layout->n_right_buttons; + } + else + { + g_assert_not_reached (); + } + + for (i = 0; i < n_buttons; i++) + { + MetaButton *btn; + GdkRectangle rect; + + btn = &buttons[i]; + rect = btn->rect.visible; + + if (!btn->visible || btn->type == META_BUTTON_TYPE_SPACER || + rect.width <= 0 || rect.height <= 0) + { + continue; + } + + rect = btn->rect.clickable; + + if (x >= rect.x && x < (rect.x + rect.width) && + y >= rect.y && y < (rect.y + rect.height)) + { + *button = *btn; + return TRUE; + } + } + } + + return FALSE; +} + void meta_theme_set_composited (MetaTheme *theme, gboolean composited) @@ -694,20 +751,50 @@ meta_theme_draw_frame (MetaTheme *theme, title_height, flags, client_width, client_height, theme->button_layout, type, &fgeom); - for (i = 0; i < META_BUTTON_TYPE_LAST; i++) + for (i = 0; i < 2; i++) { - MetaButtonState state; - GdkRectangle rect; + MetaButton *buttons; + gint n_buttons; + gint j; - get_button_rect_for_type (i, &fgeom, &rect); - - state = META_BUTTON_STATE_NORMAL; - if (func != NULL) - state = (* func) (i, rect, user_data); - - g_assert (state >= META_BUTTON_STATE_NORMAL && state < META_BUTTON_STATE_LAST); + if (i == 0) + { + buttons = theme->button_layout->left_buttons; + n_buttons = theme->button_layout->n_left_buttons; + } + else if (i == 1) + { + buttons = theme->button_layout->right_buttons; + n_buttons = theme->button_layout->n_right_buttons; + } + else + { + g_assert_not_reached (); + } - theme->button_layout->button_states[i] = state; + for (j = 0; j < n_buttons; j++) + { + MetaButton *button; + MetaButtonState state; + GdkRectangle rect; + + button = &buttons[j]; + state = META_BUTTON_STATE_NORMAL; + rect = button->rect.visible; + + if (!button->visible || button->type == META_BUTTON_TYPE_SPACER || + rect.width <= 0 || rect.height <= 0) + { + button->state = state; + continue; + } + + if (func != NULL) + state = (* func) (button->type, button->rect.clickable, user_data); + + g_assert (state >= META_BUTTON_STATE_NORMAL && state < META_BUTTON_STATE_LAST); + button->state = state; + } } impl_class->draw_frame (theme->impl, style, style_info, cr, &fgeom, diff --git a/libmetacity/meta-theme.h b/libmetacity/meta-theme.h index 4ee1d7c5..4174ffe6 100644 --- a/libmetacity/meta-theme.h +++ b/libmetacity/meta-theme.h @@ -20,13 +20,12 @@ #define META_THEME_H #include <gtk/gtk.h> -#include <libmetacity/meta-button-enums.h> +#include <libmetacity/meta-button.h> #include <libmetacity/meta-frame-borders.h> #include <libmetacity/meta-frame-enums.h> G_BEGIN_DECLS -typedef struct _MetaButtonSpace MetaButtonSpace; typedef struct _MetaFrameGeometry MetaFrameGeometry; typedef MetaButtonState (* MetaButtonStateFunc) (MetaButtonType type, @@ -82,20 +81,6 @@ typedef enum } MetaThemeType; /** - * The computed size of a button (really just a way of tying its - * visible and clickable areas together). - * The reason for two different rectangles here is Fitts' law & maximized - * windows; see bug #97703 for more details. - */ -struct _MetaButtonSpace -{ - /** The screen area where the button's image is drawn */ - GdkRectangle visible; - /** The screen area where the button can be activated by clicking */ - GdkRectangle clickable; -}; - -/** * Calculated actual geometry of the frame */ struct _MetaFrameGeometry @@ -107,34 +92,6 @@ struct _MetaFrameGeometry GdkRectangle title_rect; - /* used for a memset hack */ -#define ADDRESS_OF_BUTTON_RECTS(fgeom) (((char*)(fgeom)) + G_STRUCT_OFFSET (MetaFrameGeometry, close_rect)) -#define LENGTH_OF_BUTTON_RECTS (G_STRUCT_OFFSET (MetaFrameGeometry, right_single_background) + sizeof (GdkRectangle) - G_STRUCT_OFFSET (MetaFrameGeometry, close_rect)) - - /* The button rects (if changed adjust memset hack) */ - MetaButtonSpace close_rect; - MetaButtonSpace max_rect; - MetaButtonSpace min_rect; - MetaButtonSpace menu_rect; - MetaButtonSpace appmenu_rect; - MetaButtonSpace shade_rect; - MetaButtonSpace above_rect; - MetaButtonSpace stick_rect; - MetaButtonSpace unshade_rect; - MetaButtonSpace unabove_rect; - MetaButtonSpace unstick_rect; - -#define MAX_MIDDLE_BACKGROUNDS (META_BUTTON_TYPE_LAST - 2) - GdkRectangle left_left_background; - GdkRectangle left_middle_backgrounds[MAX_MIDDLE_BACKGROUNDS]; - GdkRectangle left_right_background; - GdkRectangle left_single_background; - GdkRectangle right_left_background; - GdkRectangle right_middle_backgrounds[MAX_MIDDLE_BACKGROUNDS]; - GdkRectangle right_right_background; - GdkRectangle right_single_background; - /* End of button rects (if changed adjust memset hack) */ - /* Round corners */ guint top_left_corner_rounded_radius; guint top_right_corner_rounded_radius; @@ -156,6 +113,11 @@ void meta_theme_set_button_layout (MetaTheme *theme, const gchar *button_layout, gboolean invert); +gboolean meta_theme_get_button (MetaTheme *theme, + gint x, + gint y, + MetaButton *button); + void meta_theme_set_composited (MetaTheme *theme, gboolean composited); diff --git a/src/ui/frames.c b/src/ui/frames.c index e639abbb..7c25b2cc 100644 --- a/src/ui/frames.c +++ b/src/ui/frames.c @@ -48,9 +48,6 @@ static void meta_frames_destroy (GtkWidget *widget); static void meta_frames_finalize (GObject *object); static void meta_frames_style_updated (GtkWidget *widget); -static void meta_frames_update_prelit_control (MetaFrames *frames, - MetaUIFrame *frame, - MetaFrameControl control); static gboolean meta_frames_button_press_event (GtkWidget *widget, GdkEventButton *event); static gboolean meta_frames_button_release_event (GtkWidget *widget, @@ -81,13 +78,6 @@ static MetaUIFrame* meta_frames_lookup_window (MetaFrames *frames, static void meta_frames_font_changed (MetaFrames *frames); static void meta_frames_button_layout_changed (MetaFrames *frames); - -static GdkRectangle* control_rect (MetaFrameControl control, - MetaFrameGeometry *fgeom); -static MetaFrameControl get_control (MetaFrames *frames, - MetaUIFrame *frame, - int x, - int y); static void clear_tip (MetaFrames *frames); static void invalidate_all_caches (MetaFrames *frames); static void invalidate_whole_window (MetaFrames *frames, @@ -114,6 +104,277 @@ struct _MetaFrames G_DEFINE_TYPE (MetaFrames, meta_frames, GTK_TYPE_WINDOW) static void +get_client_rect (MetaFrameGeometry *fgeom, + GdkRectangle *rect) +{ + rect->x = fgeom->borders.total.left; + rect->y = fgeom->borders.total.top; + rect->width = fgeom->width - fgeom->borders.total.right - rect->x; + rect->height = fgeom->height - fgeom->borders.total.bottom - rect->y; +} + +#define RESIZE_EXTENDS 15 +#define TOP_RESIZE_HEIGHT 4 +static MetaFrameControl +get_control (MetaFrames *frames, + MetaUIFrame *frame, + gint x, + gint y) +{ + MetaFrameGeometry fgeom; + MetaFrameFlags flags; + MetaFrameType type; + gboolean has_vert, has_horiz; + gboolean has_north_resize; + GdkRectangle client; + MetaFrameBorders borders; + MetaTheme *theme; + MetaButton button; + + meta_frames_calc_geometry (frames, frame, &fgeom); + get_client_rect (&fgeom, &client); + + borders = fgeom.borders; + theme = meta_ui_get_theme (); + + if (x < borders.invisible.left - borders.resize.left || + y < borders.invisible.top - borders.resize.top || + x > fgeom.width - borders.invisible.right + borders.resize.right || + y > fgeom.height - borders.invisible.bottom + borders.resize.bottom) + return META_FRAME_CONTROL_NONE; + + if (POINT_IN_RECT (x, y, client)) + return META_FRAME_CONTROL_CLIENT_AREA; + + meta_core_get (frames->xdisplay, frame->xwindow, + META_CORE_GET_FRAME_FLAGS, &flags, + META_CORE_GET_FRAME_TYPE, &type, + META_CORE_GET_END); + + if (meta_theme_get_button (theme, x, y, &button)) + { + switch (button.type) + { + case META_BUTTON_TYPE_CLOSE: + return META_FRAME_CONTROL_DELETE; + + case META_BUTTON_TYPE_MINIMIZE: + return META_FRAME_CONTROL_MINIMIZE; + + case META_BUTTON_TYPE_MENU: + return META_FRAME_CONTROL_MENU; + + case META_BUTTON_TYPE_APPMENU: + return META_FRAME_CONTROL_APPMENU; + + case META_BUTTON_TYPE_MAXIMIZE: + if (flags & META_FRAME_MAXIMIZED) + return META_FRAME_CONTROL_UNMAXIMIZE; + else + return META_FRAME_CONTROL_MAXIMIZE; + + case META_BUTTON_TYPE_SHADE: + return META_FRAME_CONTROL_SHADE; + + case META_BUTTON_TYPE_UNSHADE: + return META_FRAME_CONTROL_UNSHADE; + + case META_BUTTON_TYPE_ABOVE: + return META_FRAME_CONTROL_ABOVE; + + case META_BUTTON_TYPE_UNABOVE: + return META_FRAME_CONTROL_UNABOVE; + + case META_BUTTON_TYPE_STICK: + return META_FRAME_CONTROL_STICK; + + case META_BUTTON_TYPE_UNSTICK: + return META_FRAME_CONTROL_UNSTICK; + + case META_BUTTON_TYPE_SPACER: + case META_BUTTON_TYPE_LAST: + default: + break; + } + } + + has_north_resize = (type != META_FRAME_TYPE_ATTACHED); + has_vert = (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE) != 0; + has_horiz = (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE) != 0; + + if (POINT_IN_RECT (x, y, fgeom.title_rect)) + { + if (has_vert && y <= (fgeom.borders.invisible.top + TOP_RESIZE_HEIGHT) && has_north_resize) + return META_FRAME_CONTROL_RESIZE_N; + else + return META_FRAME_CONTROL_TITLE; + } + + /* South resize always has priority over north resize, + * in case of overlap. + */ + + if (y >= (fgeom.height - fgeom.borders.total.bottom - RESIZE_EXTENDS) && + x >= (fgeom.width - fgeom.borders.total.right - RESIZE_EXTENDS)) + { + if (has_vert && has_horiz) + return META_FRAME_CONTROL_RESIZE_SE; + else if (has_vert) + return META_FRAME_CONTROL_RESIZE_S; + else if (has_horiz) + return META_FRAME_CONTROL_RESIZE_E; + } + else if (y >= (fgeom.height - fgeom.borders.total.bottom - RESIZE_EXTENDS) && + x <= (fgeom.borders.total.left + RESIZE_EXTENDS)) + { + if (has_vert && has_horiz) + return META_FRAME_CONTROL_RESIZE_SW; + else if (has_vert) + return META_FRAME_CONTROL_RESIZE_S; + else if (has_horiz) + return META_FRAME_CONTROL_RESIZE_W; + } + else if (y < (fgeom.borders.invisible.top + RESIZE_EXTENDS) && + x <= (fgeom.borders.total.left + RESIZE_EXTENDS) && has_north_resize) + { + if (has_vert && has_horiz) + return META_FRAME_CONTROL_RESIZE_NW; + else if (has_vert) + return META_FRAME_CONTROL_RESIZE_N; + else if (has_horiz) + return META_FRAME_CONTROL_RESIZE_W; + } + else if (y < (fgeom.borders.invisible.top + RESIZE_EXTENDS) && + x >= (fgeom.width - fgeom.borders.total.right - RESIZE_EXTENDS) && has_north_resize) + { + if (has_vert && has_horiz) + return META_FRAME_CONTROL_RESIZE_NE; + else if (has_vert) + return META_FRAME_CONTROL_RESIZE_N; + else if (has_horiz) + return META_FRAME_CONTROL_RESIZE_E; + } + else if (y < (fgeom.borders.invisible.top + TOP_RESIZE_HEIGHT)) + { + if (has_vert && has_north_resize) + return META_FRAME_CONTROL_RESIZE_N; + } + else if (y >= (fgeom.height - fgeom.borders.total.bottom - RESIZE_EXTENDS)) + { + if (has_vert) + return META_FRAME_CONTROL_RESIZE_S; + } + else if (x <= fgeom.borders.total.left + RESIZE_EXTENDS) + { + if (has_horiz) + return META_FRAME_CONTROL_RESIZE_W; + } + else if (x >= (fgeom.width - fgeom.borders.total.right - RESIZE_EXTENDS)) + { + if (has_horiz) + return META_FRAME_CONTROL_RESIZE_E; + } + + if (y >= fgeom.borders.total.top) + return META_FRAME_CONTROL_NONE; + else + return META_FRAME_CONTROL_TITLE; +} + +static gboolean +get_control_rect (MetaFrameControl control, + MetaFrameGeometry *fgeom, + gint x, + gint y, + GdkRectangle *rect) +{ + MetaButtonType type; + MetaTheme *theme; + MetaButton button; + + type = META_BUTTON_TYPE_LAST; + + switch (control) + { + case META_FRAME_CONTROL_TITLE: + *rect = fgeom->title_rect; + return TRUE; + + case META_FRAME_CONTROL_DELETE: + type = META_BUTTON_TYPE_CLOSE; + break; + + case META_FRAME_CONTROL_MENU: + type = META_BUTTON_TYPE_MENU; + break; + + case META_FRAME_CONTROL_APPMENU: + type = META_BUTTON_TYPE_APPMENU; + break; + + case META_FRAME_CONTROL_MINIMIZE: + type = META_BUTTON_TYPE_MINIMIZE; + break; + + case META_FRAME_CONTROL_MAXIMIZE: + case META_FRAME_CONTROL_UNMAXIMIZE: + type = META_BUTTON_TYPE_MAXIMIZE; + break; + + case META_FRAME_CONTROL_SHADE: + type = META_BUTTON_TYPE_SHADE; + break; + + case META_FRAME_CONTROL_UNSHADE: + type = META_BUTTON_TYPE_UNSHADE; + break; + + case META_FRAME_CONTROL_ABOVE: + type = META_BUTTON_TYPE_ABOVE; + break; + + case META_FRAME_CONTROL_UNABOVE: + type = META_BUTTON_TYPE_UNABOVE; + break; + + case META_FRAME_CONTROL_STICK: + type = META_BUTTON_TYPE_STICK; + break; + + case META_FRAME_CONTROL_UNSTICK: + type = META_BUTTON_TYPE_UNSTICK; + break; + + case META_FRAME_CONTROL_CLIENT_AREA: + case META_FRAME_CONTROL_RESIZE_SE: + case META_FRAME_CONTROL_RESIZE_S: + case META_FRAME_CONTROL_RESIZE_SW: + case META_FRAME_CONTROL_RESIZE_N: + case META_FRAME_CONTROL_RESIZE_NE: + case META_FRAME_CONTROL_RESIZE_NW: + case META_FRAME_CONTROL_RESIZE_W: + case META_FRAME_CONTROL_RESIZE_E: + case META_FRAME_CONTROL_NONE: + default: + break; + } + + theme = meta_ui_get_theme (); + + if (type != META_BUTTON_TYPE_LAST && + meta_theme_get_button (theme, x, y, &button)) + { + if (type == button.type) + { + *rect = button.rect.clickable; + return TRUE; + } + } + + return FALSE; +} + +static void meta_frames_class_init (MetaFramesClass *frames_class) { GObjectClass *object_class; @@ -514,6 +775,8 @@ meta_frames_manage_window (MetaFrames *frames, frame->expose_delayed = FALSE; frame->shape_applied = FALSE; frame->prelit_control = META_FRAME_CONTROL_NONE; + frame->prelit_x = 0; + frame->prelit_y = 0; meta_core_grab_buttons (frames->xdisplay, frame->xwindow); @@ -642,21 +905,6 @@ apply_cairo_region_to_window (Display *display, g_free (rects); } -/* The client rectangle surrounds client window; it subtracts both - * the visible and invisible borders from the frame window's size. - */ -static void -get_client_rect (MetaFrameGeometry *fgeom, - int window_width, - int window_height, - cairo_rectangle_int_t *rect) -{ - rect->x = fgeom->borders.total.left; - rect->y = fgeom->borders.total.top; - rect->width = window_width - fgeom->borders.total.right - rect->x; - rect->height = window_height - fgeom->borders.total.bottom - rect->y; -} - /* The visible frame rectangle surrounds the visible portion of the * frame window; it subtracts only the invisible borders from the frame * window's size. @@ -1120,12 +1368,13 @@ show_tip_now (MetaFrames *frames) if (tiptext) { MetaFrameGeometry fgeom; - GdkRectangle *rect; + GdkRectangle rect; int dx, dy; meta_frames_calc_geometry (frames, frame, &fgeom); - rect = control_rect (control, &fgeom); + if (!get_control_rect (control, &fgeom, x, y, &rect)) + return; /* get conversion delta for root-to-frame coords */ dx = root_x - x; @@ -1133,10 +1382,10 @@ show_tip_now (MetaFrames *frames) /* Align the tooltip to the button right end if RTL */ if (meta_ui_get_direction() == META_UI_DIRECTION_RTL) - dx += rect->width; + dx += rect.width; - meta_fixed_tip_show (rect->x + dx, - rect->y + rect->height + 2 + dy, + meta_fixed_tip_show (rect.x + dx, + rect.y + rect.height + 2 + dy, tiptext); } } @@ -1178,21 +1427,146 @@ clear_tip (MetaFrames *frames) } static void -redraw_control (MetaFrames *frames, - MetaUIFrame *frame, - MetaFrameControl control) +redraw_control (MetaFrames *frames, + MetaUIFrame *frame, + MetaFrameControl control, + gint x, + gint y) { MetaFrameGeometry fgeom; - GdkRectangle *rect; + GdkRectangle rect; meta_frames_calc_geometry (frames, frame, &fgeom); - rect = control_rect (control, &fgeom); + if (!get_control_rect (control, &fgeom, x, y, &rect)) + return; - gdk_window_invalidate_rect (frame->window, rect, FALSE); + gdk_window_invalidate_rect (frame->window, &rect, FALSE); invalidate_cache (frames, frame); } +static void +update_prelit_control (MetaFrames *frames, + MetaUIFrame *frame, + MetaFrameControl control, + gint x, + gint y) +{ + MetaCursor cursor; + MetaFrameControl old_control; + gint old_x; + gint old_y; + + meta_verbose ("Updating prelit control from %u to %u\n", + frame->prelit_control, control); + + switch (control) + { + case META_FRAME_CONTROL_RESIZE_SE: + cursor = META_CURSOR_SE_RESIZE; + break; + + case META_FRAME_CONTROL_RESIZE_S: + cursor = META_CURSOR_SOUTH_RESIZE; + break; + + case META_FRAME_CONTROL_RESIZE_SW: + cursor = META_CURSOR_SW_RESIZE; + break; + + case META_FRAME_CONTROL_RESIZE_N: + cursor = META_CURSOR_NORTH_RESIZE; + break; + + case META_FRAME_CONTROL_RESIZE_NE: + cursor = META_CURSOR_NE_RESIZE; + break; + + case META_FRAME_CONTROL_RESIZE_NW: + cursor = META_CURSOR_NW_RESIZE; + break; + + case META_FRAME_CONTROL_RESIZE_W: + cursor = META_CURSOR_WEST_RESIZE; + break; + + case META_FRAME_CONTROL_RESIZE_E: + cursor = META_CURSOR_EAST_RESIZE; + break; + + case META_FRAME_CONTROL_CLIENT_AREA: + case META_FRAME_CONTROL_NONE: + case META_FRAME_CONTROL_TITLE: + case META_FRAME_CONTROL_DELETE: + case META_FRAME_CONTROL_MENU: + case META_FRAME_CONTROL_APPMENU: + case META_FRAME_CONTROL_MINIMIZE: + case META_FRAME_CONTROL_MAXIMIZE: + case META_FRAME_CONTROL_UNMAXIMIZE: + case META_FRAME_CONTROL_SHADE: + case META_FRAME_CONTROL_UNSHADE: + case META_FRAME_CONTROL_ABOVE: + case META_FRAME_CONTROL_UNABOVE: + case META_FRAME_CONTROL_STICK: + case META_FRAME_CONTROL_UNSTICK: + default: + cursor = META_CURSOR_DEFAULT; + break; + } + + /* set/unset the prelight cursor */ + meta_core_set_screen_cursor (frames->xdisplay, frame->xwindow, cursor); + + switch (control) + { + case META_FRAME_CONTROL_MENU: + case META_FRAME_CONTROL_APPMENU: + case META_FRAME_CONTROL_MINIMIZE: + case META_FRAME_CONTROL_MAXIMIZE: + case META_FRAME_CONTROL_DELETE: + case META_FRAME_CONTROL_SHADE: + case META_FRAME_CONTROL_UNSHADE: + case META_FRAME_CONTROL_ABOVE: + case META_FRAME_CONTROL_UNABOVE: + case META_FRAME_CONTROL_STICK: + case META_FRAME_CONTROL_UNSTICK: + case META_FRAME_CONTROL_UNMAXIMIZE: + /* leave control set */ + break; + + case META_FRAME_CONTROL_NONE: + case META_FRAME_CONTROL_TITLE: + case META_FRAME_CONTROL_RESIZE_SE: + case META_FRAME_CONTROL_RESIZE_S: + case META_FRAME_CONTROL_RESIZE_SW: + case META_FRAME_CONTROL_RESIZE_N: + case META_FRAME_CONTROL_RESIZE_NE: + case META_FRAME_CONTROL_RESIZE_NW: + case META_FRAME_CONTROL_RESIZE_W: + case META_FRAME_CONTROL_RESIZE_E: + case META_FRAME_CONTROL_CLIENT_AREA: + default: + /* Only prelight buttons */ + control = META_FRAME_CONTROL_NONE; + break; + } + + if (control == frame->prelit_control) + return; + + /* Save the old control so we can unprelight it */ + old_control = frame->prelit_control; + old_x = frame->prelit_x; + old_y = frame->prelit_y; + + frame->prelit_control = control; + frame->prelit_x = x; + frame->prelit_y = y; + + redraw_control (frames, frame, old_control, old_x, old_y); + redraw_control (frames, frame, control, x, y); +} + static gboolean meta_frame_titlebar_event (MetaFrames *frames, MetaUIFrame *frame, @@ -1428,17 +1802,24 @@ meta_frames_button_press_event (GtkWidget *widget, event->y_root); frame->prelit_control = control; - redraw_control (frames, frame, control); + frame->prelit_x = event->x; + frame->prelit_y = event->y; + + redraw_control (frames, frame, control, event->x, event->y); if (op == META_GRAB_OP_CLICKING_MENU) { MetaFrameGeometry fgeom; - GdkRectangle *rect; + GdkRectangle rect; int dx, dy; meta_frames_calc_geometry (frames, frame, &fgeom); - rect = control_rect (META_FRAME_CONTROL_MENU, &fgeom); + if (!get_control_rect (META_FRAME_CONTROL_MENU, &fgeom, + event->x, event->y, &rect)) + { + return FALSE; + } /* get delta to convert to root coords */ dx = event->x_root - event->x; @@ -1446,12 +1827,12 @@ meta_frames_button_press_event (GtkWidget *widget, /* Align to the right end of the menu rectangle if RTL */ if (meta_ui_get_direction() == META_UI_DIRECTION_RTL) - dx += rect->width; + dx += rect.width; meta_core_show_window_menu (frames->xdisplay, frame->xwindow, - rect->x + dx, - rect->y + rect->height + dy, + rect.x + dx, + rect.y + rect.height + dy, event->button, event->time); } @@ -1552,7 +1933,9 @@ meta_frames_notify_menu_hide (MetaFrames *frames) if (frame) { - redraw_control (frames, frame, META_FRAME_CONTROL_MENU); + redraw_control (frames, frame, META_FRAME_CONTROL_MENU, + frame->prelit_x, frame->prelit_y); + meta_core_end_grab_op (frames->xdisplay, CurrentTime); } } @@ -1709,137 +2092,12 @@ meta_frames_button_release_event (GtkWidget *widget, * prelit so to let the user know that it can now be pressed. * :) */ - meta_frames_update_prelit_control (frames, frame, control); + update_prelit_control (frames, frame, control, event->x, event->y); } return TRUE; } -static void -meta_frames_update_prelit_control (MetaFrames *frames, - MetaUIFrame *frame, - MetaFrameControl control) -{ - MetaFrameControl old_control; - MetaCursor cursor; - - - meta_verbose ("Updating prelit control from %u to %u\n", - frame->prelit_control, control); - - cursor = META_CURSOR_DEFAULT; - - switch (control) - { - case META_FRAME_CONTROL_CLIENT_AREA: - break; - case META_FRAME_CONTROL_NONE: - break; - case META_FRAME_CONTROL_TITLE: - break; - case META_FRAME_CONTROL_DELETE: - break; - case META_FRAME_CONTROL_MENU: - break; - case META_FRAME_CONTROL_APPMENU: - break; - case META_FRAME_CONTROL_MINIMIZE: - break; - case META_FRAME_CONTROL_MAXIMIZE: - break; - case META_FRAME_CONTROL_UNMAXIMIZE: - break; - case META_FRAME_CONTROL_SHADE: - break; - case META_FRAME_CONTROL_UNSHADE: - break; - case META_FRAME_CONTROL_ABOVE: - break; - case META_FRAME_CONTROL_UNABOVE: - break; - case META_FRAME_CONTROL_STICK: - break; - case META_FRAME_CONTROL_UNSTICK: - break; - case META_FRAME_CONTROL_RESIZE_SE: - cursor = META_CURSOR_SE_RESIZE; - break; - case META_FRAME_CONTROL_RESIZE_S: - cursor = META_CURSOR_SOUTH_RESIZE; - break; - case META_FRAME_CONTROL_RESIZE_SW: - cursor = META_CURSOR_SW_RESIZE; - break; - case META_FRAME_CONTROL_RESIZE_N: - cursor = META_CURSOR_NORTH_RESIZE; - break; - case META_FRAME_CONTROL_RESIZE_NE: - cursor = META_CURSOR_NE_RESIZE; - break; - case META_FRAME_CONTROL_RESIZE_NW: - cursor = META_CURSOR_NW_RESIZE; - break; - case META_FRAME_CONTROL_RESIZE_W: - cursor = META_CURSOR_WEST_RESIZE; - break; - case META_FRAME_CONTROL_RESIZE_E: - cursor = META_CURSOR_EAST_RESIZE; - break; - default: - break; - } - - /* set/unset the prelight cursor */ - meta_core_set_screen_cursor (frames->xdisplay, frame->xwindow, cursor); - - switch (control) - { - case META_FRAME_CONTROL_MENU: - case META_FRAME_CONTROL_APPMENU: - case META_FRAME_CONTROL_MINIMIZE: - case META_FRAME_CONTROL_MAXIMIZE: - case META_FRAME_CONTROL_DELETE: - case META_FRAME_CONTROL_SHADE: - case META_FRAME_CONTROL_UNSHADE: - case META_FRAME_CONTROL_ABOVE: - case META_FRAME_CONTROL_UNABOVE: - case META_FRAME_CONTROL_STICK: - case META_FRAME_CONTROL_UNSTICK: - case META_FRAME_CONTROL_UNMAXIMIZE: - /* leave control set */ - break; - case META_FRAME_CONTROL_NONE: - case META_FRAME_CONTROL_TITLE: - case META_FRAME_CONTROL_RESIZE_SE: - case META_FRAME_CONTROL_RESIZE_S: - case META_FRAME_CONTROL_RESIZE_SW: - case META_FRAME_CONTROL_RESIZE_N: - case META_FRAME_CONTROL_RESIZE_NE: - case META_FRAME_CONTROL_RESIZE_NW: - case META_FRAME_CONTROL_RESIZE_W: - case META_FRAME_CONTROL_RESIZE_E: - case META_FRAME_CONTROL_CLIENT_AREA: - /* Only prelight buttons */ - control = META_FRAME_CONTROL_NONE; - break; - default: - /* Only prelight buttons */ - control = META_FRAME_CONTROL_NONE; - break; - } - - if (control == frame->prelit_control) - return; - - /* Save the old control so we can unprelight it */ - old_control = frame->prelit_control; - - frame->prelit_control = control; - - redraw_control (frames, frame, old_control); - redraw_control (frames, frame, control); -} - static gboolean meta_frames_motion_notify_event (GtkWidget *widget, GdkEventMotion *event) @@ -1912,7 +2170,7 @@ meta_frames_motion_notify_event (GtkWidget *widget, control = META_FRAME_CONTROL_NONE; /* Update prelit control and cursor */ - meta_frames_update_prelit_control (frames, frame, control); + update_prelit_control (frames, frame, control, x, y); /* No tooltip while in the process of clicking */ } @@ -1928,7 +2186,7 @@ meta_frames_motion_notify_event (GtkWidget *widget, control = get_control (frames, frame, x, y); /* Update prelit control and cursor */ - meta_frames_update_prelit_control (frames, frame, control); + update_prelit_control (frames, frame, control, x, y); queue_tip (frames); } @@ -2284,6 +2542,11 @@ update_button_state (MetaButtonType type, Window grab_frame; MetaGrabOp grab_op; MetaFrameControl control; + GdkDisplay *display; + GdkSeat *seat; + GdkDevice *device; + gint x; + gint y; data = (ButtonStateData *) user_data; @@ -2296,6 +2559,15 @@ update_button_state (MetaButtonType type, control = data->frame->prelit_control; + display = gdk_display_get_default (); + seat = gdk_display_get_default_seat (display); + device = gdk_seat_get_pointer (seat); + + gdk_window_get_device_position (data->frame->window, device, &x, &y, NULL); + + if (!POINT_IN_RECT (x, y, rect)) + return state; + /* Set prelight state */ if (control == META_FRAME_CONTROL_MENU && type == META_BUTTON_TYPE_MENU) @@ -2441,7 +2713,7 @@ meta_frames_enter_notify_event (GtkWidget *widget, return FALSE; control = get_control (frames, frame, event->x, event->y); - meta_frames_update_prelit_control (frames, frame, control); + update_prelit_control (frames, frame, control, event->x, event->y); return TRUE; } @@ -2459,253 +2731,14 @@ meta_frames_leave_notify_event (GtkWidget *widget, if (frame == NULL) return FALSE; - meta_frames_update_prelit_control (frames, frame, META_FRAME_CONTROL_NONE); + update_prelit_control (frames, frame, META_FRAME_CONTROL_NONE, + event->x, event->y); clear_tip (frames); return TRUE; } -static GdkRectangle* -control_rect (MetaFrameControl control, - MetaFrameGeometry *fgeom) -{ - GdkRectangle *rect; - - rect = NULL; - switch (control) - { - case META_FRAME_CONTROL_TITLE: - rect = &fgeom->title_rect; - break; - case META_FRAME_CONTROL_DELETE: - rect = &fgeom->close_rect.visible; - break; - case META_FRAME_CONTROL_MENU: - rect = &fgeom->menu_rect.visible; - break; - case META_FRAME_CONTROL_APPMENU: - rect = &fgeom->appmenu_rect.visible; - break; - case META_FRAME_CONTROL_MINIMIZE: - rect = &fgeom->min_rect.visible; - break; - case META_FRAME_CONTROL_MAXIMIZE: - case META_FRAME_CONTROL_UNMAXIMIZE: - rect = &fgeom->max_rect.visible; - break; - case META_FRAME_CONTROL_SHADE: - rect = &fgeom->shade_rect.visible; - break; - case META_FRAME_CONTROL_UNSHADE: - rect = &fgeom->unshade_rect.visible; - break; - case META_FRAME_CONTROL_ABOVE: - rect = &fgeom->above_rect.visible; - break; - case META_FRAME_CONTROL_UNABOVE: - rect = &fgeom->unabove_rect.visible; - break; - case META_FRAME_CONTROL_STICK: - rect = &fgeom->stick_rect.visible; - break; - case META_FRAME_CONTROL_UNSTICK: - rect = &fgeom->unstick_rect.visible; - break; - case META_FRAME_CONTROL_RESIZE_SE: - break; - case META_FRAME_CONTROL_RESIZE_S: - break; - case META_FRAME_CONTROL_RESIZE_SW: - break; - case META_FRAME_CONTROL_RESIZE_N: - break; - case META_FRAME_CONTROL_RESIZE_NE: - break; - case META_FRAME_CONTROL_RESIZE_NW: - break; - case META_FRAME_CONTROL_RESIZE_W: - break; - case META_FRAME_CONTROL_RESIZE_E: - break; - case META_FRAME_CONTROL_NONE: - break; - case META_FRAME_CONTROL_CLIENT_AREA: - break; - default: - break; - } - - return rect; -} - -#define RESIZE_EXTENDS 15 -#define TOP_RESIZE_HEIGHT 4 -static MetaFrameControl -get_control (MetaFrames *frames, - MetaUIFrame *frame, - int x, int y) -{ - MetaFrameGeometry fgeom; - MetaFrameFlags flags; - MetaFrameType type; - gboolean has_vert, has_horiz; - gboolean has_north_resize; - GdkRectangle client; - MetaFrameBorders borders; - - meta_frames_calc_geometry (frames, frame, &fgeom); - get_client_rect (&fgeom, fgeom.width, fgeom.height, &client); - - borders = fgeom.borders; - - if (x < borders.invisible.left - borders.resize.left || - y < borders.invisible.top - borders.resize.top || - x > fgeom.width - borders.invisible.right + borders.resize.right || - y > fgeom.height - borders.invisible.bottom + borders.resize.bottom) - return META_FRAME_CONTROL_NONE; - - if (POINT_IN_RECT (x, y, client)) - return META_FRAME_CONTROL_CLIENT_AREA; - - if (POINT_IN_RECT (x, y, fgeom.close_rect.clickable)) - return META_FRAME_CONTROL_DELETE; - - if (POINT_IN_RECT (x, y, fgeom.min_rect.clickable)) - return META_FRAME_CONTROL_MINIMIZE; - - if (POINT_IN_RECT (x, y, fgeom.menu_rect.clickable)) - return META_FRAME_CONTROL_MENU; - - if (POINT_IN_RECT (x, y, fgeom.appmenu_rect.clickable)) - return META_FRAME_CONTROL_APPMENU; - - meta_core_get (frames->xdisplay, frame->xwindow, - META_CORE_GET_FRAME_FLAGS, &flags, - META_CORE_GET_FRAME_TYPE, &type, - META_CORE_GET_END); - - has_north_resize = (type != META_FRAME_TYPE_ATTACHED); - has_vert = (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE) != 0; - has_horiz = (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE) != 0; - - if (POINT_IN_RECT (x, y, fgeom.title_rect)) - { - if (has_vert && y <= (fgeom.borders.invisible.top + TOP_RESIZE_HEIGHT) && has_north_resize) - return META_FRAME_CONTROL_RESIZE_N; - else - return META_FRAME_CONTROL_TITLE; - } - - if (POINT_IN_RECT (x, y, fgeom.max_rect.clickable)) - { - if (flags & META_FRAME_MAXIMIZED) - return META_FRAME_CONTROL_UNMAXIMIZE; - else - return META_FRAME_CONTROL_MAXIMIZE; - } - - if (POINT_IN_RECT (x, y, fgeom.shade_rect.clickable)) - { - return META_FRAME_CONTROL_SHADE; - } - - if (POINT_IN_RECT (x, y, fgeom.unshade_rect.clickable)) - { - return META_FRAME_CONTROL_UNSHADE; - } - - if (POINT_IN_RECT (x, y, fgeom.above_rect.clickable)) - { - return META_FRAME_CONTROL_ABOVE; - } - - if (POINT_IN_RECT (x, y, fgeom.unabove_rect.clickable)) - { - return META_FRAME_CONTROL_UNABOVE; - } - - if (POINT_IN_RECT (x, y, fgeom.stick_rect.clickable)) - { - return META_FRAME_CONTROL_STICK; - } - - if (POINT_IN_RECT (x, y, fgeom.unstick_rect.clickable)) - { - return META_FRAME_CONTROL_UNSTICK; - } - - /* South resize always has priority over north resize, - * in case of overlap. - */ - - if (y >= (fgeom.height - fgeom.borders.total.bottom - RESIZE_EXTENDS) && - x >= (fgeom.width - fgeom.borders.total.right - RESIZE_EXTENDS)) - { - if (has_vert && has_horiz) - return META_FRAME_CONTROL_RESIZE_SE; - else if (has_vert) - return META_FRAME_CONTROL_RESIZE_S; - else if (has_horiz) - return META_FRAME_CONTROL_RESIZE_E; - } - else if (y >= (fgeom.height - fgeom.borders.total.bottom - RESIZE_EXTENDS) && - x <= (fgeom.borders.total.left + RESIZE_EXTENDS)) - { - if (has_vert && has_horiz) - return META_FRAME_CONTROL_RESIZE_SW; - else if (has_vert) - return META_FRAME_CONTROL_RESIZE_S; - else if (has_horiz) - return META_FRAME_CONTROL_RESIZE_W; - } - else if (y < (fgeom.borders.invisible.top + RESIZE_EXTENDS) && - x <= (fgeom.borders.total.left + RESIZE_EXTENDS) && has_north_resize) - { - if (has_vert && has_horiz) - return META_FRAME_CONTROL_RESIZE_NW; - else if (has_vert) - return META_FRAME_CONTROL_RESIZE_N; - else if (has_horiz) - return META_FRAME_CONTROL_RESIZE_W; - } - else if (y < (fgeom.borders.invisible.top + RESIZE_EXTENDS) && - x >= (fgeom.width - fgeom.borders.total.right - RESIZE_EXTENDS) && has_north_resize) - { - if (has_vert && has_horiz) - return META_FRAME_CONTROL_RESIZE_NE; - else if (has_vert) - return META_FRAME_CONTROL_RESIZE_N; - else if (has_horiz) - return META_FRAME_CONTROL_RESIZE_E; - } - else if (y < (fgeom.borders.invisible.top + TOP_RESIZE_HEIGHT)) - { - if (has_vert && has_north_resize) - return META_FRAME_CONTROL_RESIZE_N; - } - else if (y >= (fgeom.height - fgeom.borders.total.bottom - RESIZE_EXTENDS)) - { - if (has_vert) - return META_FRAME_CONTROL_RESIZE_S; - } - else if (x <= fgeom.borders.total.left + RESIZE_EXTENDS) - { - if (has_horiz) - return META_FRAME_CONTROL_RESIZE_W; - } - else if (x >= (fgeom.width - fgeom.borders.total.right - RESIZE_EXTENDS)) - { - if (has_horiz) - return META_FRAME_CONTROL_RESIZE_E; - } - - if (y >= fgeom.borders.total.top) - return META_FRAME_CONTROL_NONE; - else - return META_FRAME_CONTROL_TITLE; -} - void meta_frames_push_delay_exposes (MetaFrames *frames) { diff --git a/src/ui/frames.h b/src/ui/frames.h index 251e6421..5d30c9a7 100644 --- a/src/ui/frames.h +++ b/src/ui/frames.h @@ -74,6 +74,8 @@ struct _MetaUIFrame /* FIXME get rid of this, it can just be in the MetaFrames struct */ MetaFrameControl prelit_control; + gint prelit_x; + gint prelit_y; }; MetaFrames *meta_frames_new (void); |