From 7641c6f95200fdd23b75e85481b23219d950b337 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Fri, 4 Oct 2002 02:28:57 +0000 Subject: [PATCH] Button-reordering patch. Has all the code except actually installing a 2002-10-03 Havoc Pennington Button-reordering patch. Has all the code except actually installing a gconf schema and reading the gconf key in prefs.c. metacity-theme-viewer displays the button layouts for testing themes. * src/preview-widget.c (meta_preview_size_request): make up a width/height if no child widget * src/prefs.c (meta_prefs_get_button_layout): new function * src/frames.c: get the button layout from prefs and use it when drawing * src/theme.c (meta_frame_layout_calc_geometry): enhance to be able to lay out buttons in different arrangements (button_rect): draw the new button background rectangles (meta_theme_draw_frame): require a button layout argument (meta_theme_calc_geometry): pass in the button layout * src/preview-widget.h: mod to handle button layouts * src/theme-viewer.c: mod to handle button layouts --- ChangeLog | 25 +++ src/common.h | 27 ++- src/frames.c | 56 +++++- src/prefs.c | 25 +++ src/prefs.h | 5 +- src/preview-widget.c | 37 +++- src/preview-widget.h | 20 +- src/theme-viewer.c | 223 ++++++++++++++++++++- src/theme.c | 456 ++++++++++++++++++++++++++++++------------- src/theme.h | 88 +++++---- 10 files changed, 772 insertions(+), 190 deletions(-) diff --git a/ChangeLog b/ChangeLog index 895fe6ffc..0fef9bba1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2002-10-03 Havoc Pennington + + Button-reordering patch. Has all the code except actually + installing a gconf schema and reading the gconf key in prefs.c. + metacity-theme-viewer displays the button layouts for testing + themes. + + * src/preview-widget.c (meta_preview_size_request): make up a + width/height if no child widget + + * src/prefs.c (meta_prefs_get_button_layout): new function + + * src/frames.c: get the button layout from prefs and + use it when drawing + + * src/theme.c (meta_frame_layout_calc_geometry): enhance to be + able to lay out buttons in different arrangements + (button_rect): draw the new button background rectangles + (meta_theme_draw_frame): require a button layout argument + (meta_theme_calc_geometry): pass in the button layout + + * src/preview-widget.h: mod to handle button layouts + + * src/theme-viewer.c: mod to handle button layouts + 2002-10-03 Havoc Pennington * configure.in: 2.4.2 diff --git a/src/common.h b/src/common.h index 9e59fa8cf..0aee2f052 100644 --- a/src/common.h +++ b/src/common.h @@ -117,7 +117,6 @@ typedef enum META_GRAB_OP_CLICKING_MENU } MetaGrabOp; - typedef enum { META_CURSOR_DEFAULT, @@ -169,6 +168,32 @@ typedef enum META_VIRTUAL_MOD5_MASK = 1 << 14 } MetaVirtualModifier; + +/* Function a window button can have. Note, you can't add stuff here + * without extending the theme format to draw a new function and + * breaking all existing themes. + */ +typedef enum +{ + META_BUTTON_FUNCTION_MENU, + META_BUTTON_FUNCTION_MINIMIZE, + META_BUTTON_FUNCTION_MAXIMIZE, + META_BUTTON_FUNCTION_CLOSE, + META_BUTTON_FUNCTION_LAST +} MetaButtonFunction; + +#define MAX_BUTTONS_PER_CORNER META_BUTTON_FUNCTION_LAST + +typedef struct _MetaButtonLayout MetaButtonLayout; +struct _MetaButtonLayout +{ + /* buttons in the group on the left side */ + MetaButtonFunction left_buttons[MAX_BUTTONS_PER_CORNER]; + + /* buttons in the group on the right side */ + MetaButtonFunction right_buttons[MAX_BUTTONS_PER_CORNER]; +}; + /* should investigate changing these to whatever most apps use */ #define META_ICON_WIDTH 32 #define META_ICON_HEIGHT 32 diff --git a/src/frames.c b/src/frames.c index 9736976eb..936c75358 100644 --- a/src/frames.c +++ b/src/frames.c @@ -73,7 +73,9 @@ static void meta_frames_ensure_layout (MetaFrames *frames, static MetaUIFrame* meta_frames_lookup_window (MetaFrames *frames, Window xwindow); -static void meta_frames_font_changed (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); @@ -161,12 +163,19 @@ unsigned_long_hash (gconstpointer v) } static void -font_changed_callback (MetaPreference pref, - void *data) +prefs_changed_callback (MetaPreference pref, + void *data) { - if (pref == META_PREF_TITLEBAR_FONT) + switch (pref) { + case META_PREF_TITLEBAR_FONT: meta_frames_font_changed (META_FRAMES (data)); + break; + case META_PREF_BUTTON_LAYOUT: + meta_frames_button_layout_changed (META_FRAMES (data)); + break; + default: + break; } } @@ -185,7 +194,7 @@ meta_frames_init (MetaFrames *frames) gtk_widget_set_double_buffered (GTK_WIDGET (frames), FALSE); - meta_prefs_add_listener (font_changed_callback, frames); + meta_prefs_add_listener (prefs_changed_callback, frames); } static void @@ -237,7 +246,7 @@ meta_frames_finalize (GObject *object) frames = META_FRAMES (object); - meta_prefs_remove_listener (font_changed_callback, frames); + meta_prefs_remove_listener (prefs_changed_callback, frames); g_hash_table_destroy (frames->text_heights); @@ -292,6 +301,31 @@ meta_frames_font_changed (MetaFrames *frames) } +static void +queue_draw_func (gpointer key, gpointer value, gpointer data) +{ + MetaUIFrame *frame; + MetaFrames *frames; + + frames = META_FRAMES (data); + frame = value; + + /* If a resize occurs it will cause a redraw, but the + * resize may not actually be needed so we always redraw + * in case of color change. + */ + gtk_style_set_background (GTK_WIDGET (frames)->style, + frame->window, GTK_STATE_NORMAL); + gdk_window_invalidate_rect (frame->window, NULL, FALSE); +} + +static void +meta_frames_button_layout_changed (MetaFrames *frames) +{ + g_hash_table_foreach (frames->frames, + queue_draw_func, frames); +} + static void meta_frames_style_set (GtkWidget *widget, GtkStyle *prev_style) @@ -394,6 +428,7 @@ meta_frames_calc_geometry (MetaFrames *frames, int width, height; MetaFrameFlags flags; MetaFrameType type; + MetaButtonLayout button_layout; meta_core_get_client_size (gdk_display, frame->xwindow, &width, &height); @@ -402,12 +437,15 @@ meta_frames_calc_geometry (MetaFrames *frames, type = meta_core_get_frame_type (gdk_display, frame->xwindow); meta_frames_ensure_layout (frames, frame); + + meta_prefs_get_button_layout (&button_layout); meta_theme_calc_geometry (meta_theme_get_current (), type, frame->text_height, flags, width, height, + &button_layout, fgeom); } @@ -1524,6 +1562,7 @@ meta_frames_paint_to_drawable (MetaFrames *frames, GdkRectangle *areas; int n_areas; int screen_width, screen_height; + MetaButtonLayout button_layout; widget = GTK_WIDGET (frames); @@ -1656,10 +1695,12 @@ meta_frames_paint_to_drawable (MetaFrames *frames, /* Now draw remaining portion of region */ gdk_region_get_rectangles (edges, &areas, &n_areas); + + meta_prefs_get_button_layout (&button_layout); i = 0; while (i < n_areas) - { + { if (GDK_IS_WINDOW (drawable)) gdk_window_begin_paint_rect (drawable, &areas[i]); @@ -1673,6 +1714,7 @@ meta_frames_paint_to_drawable (MetaFrames *frames, w, h, frame->layout, frame->text_height, + &button_layout, button_states, mini_icon, icon); diff --git a/src/prefs.c b/src/prefs.c index afabef650..aa66a43cd 100644 --- a/src/prefs.c +++ b/src/prefs.c @@ -799,6 +799,10 @@ meta_preference_to_string (MetaPreference pref) case META_PREF_COMMANDS: return "COMMANDS"; + + case META_PREF_BUTTON_LAYOUT: + return "BUTTON_LAYOUT"; + break; } return "(unknown)"; @@ -1142,6 +1146,27 @@ meta_prefs_get_gconf_key_for_command (int i) return key; } +void +meta_prefs_get_button_layout (MetaButtonLayout *button_layout) +{ + /* FIXME */ + int i; + + i = 0; + while (i < MAX_BUTTONS_PER_CORNER) + { + button_layout->left_buttons[i] = META_BUTTON_FUNCTION_LAST; + button_layout->right_buttons[i] = META_BUTTON_FUNCTION_LAST; + ++i; + } + + button_layout->left_buttons[0] = META_BUTTON_FUNCTION_MENU; + + button_layout->right_buttons[0] = META_BUTTON_FUNCTION_MINIMIZE; + button_layout->right_buttons[1] = META_BUTTON_FUNCTION_MAXIMIZE; + button_layout->right_buttons[2] = META_BUTTON_FUNCTION_CLOSE; +} + void meta_prefs_get_screen_bindings (const MetaKeyPref **bindings, int *n_bindings) diff --git a/src/prefs.h b/src/prefs.h index 8769bceea..b10432c1e 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -38,7 +38,8 @@ typedef enum META_PREF_WINDOW_KEYBINDINGS, META_PREF_SCREEN_KEYBINDINGS, META_PREF_DISABLE_WORKAROUNDS, - META_PREF_COMMANDS + META_PREF_COMMANDS, + META_PREF_BUTTON_LAYOUT } MetaPreference; typedef void (* MetaPrefsChangedFunc) (MetaPreference pref, @@ -66,6 +67,8 @@ const char* meta_prefs_get_command (int i); char* meta_prefs_get_gconf_key_for_command (int i); +void meta_prefs_get_button_layout (MetaButtonLayout *button_layout); + void meta_prefs_set_num_workspaces (int n_workspaces); /* Screen bindings */ diff --git a/src/preview-widget.c b/src/preview-widget.c index 6acac0bba..30ef28623 100644 --- a/src/preview-widget.c +++ b/src/preview-widget.c @@ -79,8 +79,24 @@ meta_preview_class_init (MetaPreviewClass *class) static void meta_preview_init (MetaPreview *preview) { + int i; + GTK_WIDGET_SET_FLAGS (preview, GTK_NO_WINDOW); + i = 0; + while (i < MAX_BUTTONS_PER_CORNER) + { + preview->button_layout.left_buttons[i] = META_BUTTON_FUNCTION_LAST; + preview->button_layout.right_buttons[i] = META_BUTTON_FUNCTION_LAST; + ++i; + } + + preview->button_layout.left_buttons[0] = META_BUTTON_FUNCTION_MENU; + + preview->button_layout.right_buttons[0] = META_BUTTON_FUNCTION_MINIMIZE; + preview->button_layout.right_buttons[1] = META_BUTTON_FUNCTION_MAXIMIZE; + preview->button_layout.right_buttons[2] = META_BUTTON_FUNCTION_CLOSE; + preview->type = META_FRAME_TYPE_NORMAL; preview->flags = META_FRAME_ALLOWS_DELETE | @@ -218,7 +234,7 @@ meta_preview_expose (GtkWidget *widget, if (client_width < 0) client_width = 1; if (client_height < 0) - client_height = 1; + client_height = 1; if (preview->theme) { @@ -235,6 +251,7 @@ meta_preview_expose (GtkWidget *widget, client_width, client_height, preview->layout, preview->text_height, + &preview->button_layout, button_states, meta_preview_get_mini_icon (), meta_preview_get_icon ()); @@ -267,6 +284,13 @@ meta_preview_size_request (GtkWidget *widget, req->width += child_requisition.width; req->height += child_requisition.height; } + else + { +#define NO_CHILD_WIDTH 80 +#define NO_CHILD_HEIGHT 20 + req->width += NO_CHILD_WIDTH; + req->height += NO_CHILD_HEIGHT; + } req->width += GTK_CONTAINER (widget)->border_width * 2; req->height += GTK_CONTAINER (widget)->border_width * 2; @@ -369,6 +393,17 @@ meta_preview_set_frame_flags (MetaPreview *preview, gtk_widget_queue_resize (GTK_WIDGET (preview)); } +void +meta_preview_set_button_layout (MetaPreview *preview, + const MetaButtonLayout *button_layout) +{ + g_return_if_fail (META_IS_PREVIEW (preview)); + + preview->button_layout = *button_layout; + + gtk_widget_queue_draw (GTK_WIDGET (preview)); +} + #include "inlinepixbufs.h" GdkPixbuf* diff --git a/src/preview-widget.h b/src/preview-widget.h index 5b21fe7e7..f6ae48ab8 100644 --- a/src/preview-widget.h +++ b/src/preview-widget.h @@ -52,6 +52,7 @@ struct _MetaPreview int top_height; int bottom_height; + MetaButtonLayout button_layout; }; struct _MetaPreviewClass @@ -63,14 +64,17 @@ struct _MetaPreviewClass GtkType meta_preview_get_type (void) G_GNUC_CONST; GtkWidget* meta_preview_new (void); -void meta_preview_set_theme (MetaPreview *preview, - MetaTheme *theme); -void meta_preview_set_title (MetaPreview *preview, - const char *title); -void meta_preview_set_frame_type (MetaPreview *preview, - MetaFrameType type); -void meta_preview_set_frame_flags (MetaPreview *preview, - MetaFrameFlags flags); +void meta_preview_set_theme (MetaPreview *preview, + MetaTheme *theme); +void meta_preview_set_title (MetaPreview *preview, + const char *title); +void meta_preview_set_frame_type (MetaPreview *preview, + MetaFrameType type); +void meta_preview_set_frame_flags (MetaPreview *preview, + MetaFrameFlags flags); +void meta_preview_set_button_layout (MetaPreview *preview, + const MetaButtonLayout *button_layout); + GdkPixbuf* meta_preview_get_icon (void); GdkPixbuf* meta_preview_get_mini_icon (void); diff --git a/src/theme-viewer.c b/src/theme-viewer.c index 2334eb84c..92349fbf4 100644 --- a/src/theme-viewer.c +++ b/src/theme-viewer.c @@ -28,6 +28,22 @@ #include #include +/* We need to compute all different button arrangements + * in terms of button location. We don't care about + * different arrangements in terms of button function. + * + * So if dups are allowed, from 0-4 buttons on the left, from 0-4 on + * the right, 5x5=25 combinations. + * + * If no dups, 0-4 on left determines the number on the right plus + * we have a special case for the "no buttons on either side" case. + */ +#ifndef ALLOW_DUPLICATE_BUTTONS +#define BUTTON_LAYOUT_COMBINATIONS (MAX_BUTTONS_PER_CORNER + 1 + 1) +#else +#define BUTTON_LAYOUT_COMBINATIONS ((MAX_BUTTONS_PER_CORNER+1)*(MAX_BUTTONS_PER_CORNER+1)) +#endif + #define CLIENT_WIDTH 200 #define CLIENT_HEIGHT 200 @@ -40,7 +56,7 @@ enum }; static MetaTheme *global_theme = NULL; -static GtkWidget *previews[META_FRAME_TYPE_LAST*FONT_SIZE_LAST] = { NULL, }; +static GtkWidget *previews[META_FRAME_TYPE_LAST*FONT_SIZE_LAST + BUTTON_LAYOUT_COMBINATIONS] = { NULL, }; static void run_position_expression_tests (void); static void run_position_expression_timings (void); @@ -547,6 +563,190 @@ preview_collection (int font_size, return sw; } +static MetaButtonLayout different_layouts[BUTTON_LAYOUT_COMBINATIONS]; + +static void +init_layouts (void) +{ + int i; + + /* Blank out all the layouts */ + i = 0; + while (i < (int) G_N_ELEMENTS (different_layouts)) + { + int j; + + j = 0; + while (j < MAX_BUTTONS_PER_CORNER) + { + different_layouts[i].left_buttons[j] = META_BUTTON_FUNCTION_LAST; + different_layouts[i].right_buttons[j] = META_BUTTON_FUNCTION_LAST; + ++j; + } + ++i; + } + +#ifndef ALLOW_DUPLICATE_BUTTONS + i = 0; + while (i <= MAX_BUTTONS_PER_CORNER) + { + int j; + + j = 0; + while (j < i) + { + different_layouts[i].right_buttons[j] = (MetaButtonFunction) j; + ++j; + } + while (j < MAX_BUTTONS_PER_CORNER) + { + different_layouts[i].left_buttons[j-i] = (MetaButtonFunction) j; + ++j; + } + + ++i; + } + + /* Special extra case for no buttons on either side */ + different_layouts[i].left_buttons[0] = META_BUTTON_FUNCTION_LAST; + different_layouts[i].right_buttons[0] = META_BUTTON_FUNCTION_LAST; + +#else + /* FIXME this code is if we allow duplicate buttons, + * which we currently do not + */ + int left; + int i; + + left = 0; + i = 0; + + while (left < MAX_BUTTONS_PER_CORNER) + { + int right; + + right = 0; + + while (right < MAX_BUTTONS_PER_CORNER) + { + int j; + + static MetaButtonFunction left_functions[MAX_BUTTONS_PER_CORNER] = { + META_BUTTON_FUNCTION_MENU, + META_BUTTON_FUNCTION_MINIMIZE, + META_BUTTON_FUNCTION_MAXIMIZE, + META_BUTTON_FUNCTION_CLOSE + }; + static MetaButtonFunction right_functions[MAX_BUTTONS_PER_CORNER] = { + META_BUTTON_FUNCTION_MINIMIZE, + META_BUTTON_FUNCTION_MAXIMIZE, + META_BUTTON_FUNCTION_CLOSE, + META_BUTTON_FUNCTION_MENU + }; + + g_assert (i < BUTTON_LAYOUT_COMBINATIONS); + + j = 0; + while (j <= left) + { + different_layouts[i].left_buttons[j] = left_functions[j]; + ++j; + } + + j = 0; + while (j <= right) + { + different_layouts[i].right_buttons[j] = right_functions[j]; + ++j; + } + + ++i; + + ++right; + } + + ++left; + } +#endif +} + + +static GtkWidget* +previews_of_button_layouts (void) +{ + static gboolean initted = FALSE; + GtkWidget *box; + GtkWidget *sw; + GdkColor desktop_color; + int i; + GtkWidget *eventbox; + + if (!initted) + { + init_layouts (); + initted = TRUE; + } + + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + box = gtk_vbox_new (FALSE, 0); + gtk_box_set_spacing (GTK_BOX (box), 20); + gtk_container_set_border_width (GTK_CONTAINER (box), 20); + + eventbox = gtk_event_box_new (); + gtk_container_add (GTK_CONTAINER (eventbox), box); + + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), eventbox); + + desktop_color.red = 0x5144; + desktop_color.green = 0x75D6; + desktop_color.blue = 0xA699; + + gtk_widget_modify_bg (eventbox, GTK_STATE_NORMAL, &desktop_color); + + i = 0; + while (i < BUTTON_LAYOUT_COMBINATIONS) + { + GtkWidget *align; + double xalign, yalign; + GtkWidget *eventbox2; + GtkWidget *preview; + char *title; + + eventbox2 = gtk_event_box_new (); + + preview = meta_preview_new (); + + gtk_container_add (GTK_CONTAINER (eventbox2), preview); + + meta_preview_set_theme (META_PREVIEW (preview), global_theme); + + title = g_strdup_printf ("Button layout test %d", i+1); + meta_preview_set_title (META_PREVIEW (preview), title); + g_free (title); + + meta_preview_set_button_layout (META_PREVIEW (preview), + &different_layouts[i]); + + xalign = 0.5; + yalign = 0.5; + + align = gtk_alignment_new (0.0, 0.0, xalign, yalign); + gtk_container_add (GTK_CONTAINER (align), eventbox2); + + gtk_box_pack_start (GTK_BOX (box), align, TRUE, TRUE, 0); + + previews[META_FRAME_TYPE_LAST*FONT_SIZE_LAST + i] = preview; + + ++i; + } + + return sw; +} + int main (int argc, char **argv) { @@ -630,6 +830,11 @@ main (int argc, char **argv) collection, gtk_label_new ("Large Title Font")); + collection = previews_of_button_layouts (); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), + collection, + gtk_label_new ("Button Layouts")); + i = 0; while (i < (int) G_N_ELEMENTS (previews)) { @@ -698,6 +903,7 @@ run_theme_benchmark (int client_width, clock_t start; clock_t end; int i; + MetaButtonLayout button_layout; #define ITERATIONS 100 widget = gtk_window_new (GTK_WINDOW_TOPLEVEL); @@ -718,7 +924,21 @@ run_theme_benchmark (int client_width, -1); layout = create_title_layout (widget); + + i = 0; + while (i < MAX_BUTTONS_PER_CORNER) + { + button_layout.left_buttons[i] = META_BUTTON_FUNCTION_LAST; + button_layout.right_buttons[i] = META_BUTTON_FUNCTION_LAST; + ++i; + } + + button_layout.left_buttons[0] = META_BUTTON_FUNCTION_MENU; + button_layout.right_buttons[0] = META_BUTTON_FUNCTION_MINIMIZE; + button_layout.right_buttons[1] = META_BUTTON_FUNCTION_MAXIMIZE; + button_layout.right_buttons[2] = META_BUTTON_FUNCTION_CLOSE; + start = clock (); i = 0; @@ -734,6 +954,7 @@ run_theme_benchmark (int client_width, client_width, client_height, layout, get_text_height (widget), + &button_layout, button_states, meta_preview_get_mini_icon (), meta_preview_get_icon ()); diff --git a/src/theme.c b/src/theme.c index 56a559421..dc060735c 100644 --- a/src/theme.c +++ b/src/theme.c @@ -390,14 +390,86 @@ meta_frame_layout_get_borders (const MetaFrameLayout *layout, } } -void -meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, - int text_height, - MetaFrameFlags flags, - int client_width, - int client_height, - MetaFrameGeometry *fgeom) +static GdkRectangle* +rect_for_function (MetaFrameGeometry *fgeom, + MetaFrameFlags flags, + MetaButtonFunction function) { + switch (function) + { + case META_BUTTON_FUNCTION_MENU: + if (flags & META_FRAME_ALLOWS_MENU) + return &fgeom->menu_rect; + else + return NULL; + case META_BUTTON_FUNCTION_MINIMIZE: + if (flags & META_FRAME_ALLOWS_MINIMIZE) + return &fgeom->min_rect; + else + return NULL; + case META_BUTTON_FUNCTION_MAXIMIZE: + if (flags & META_FRAME_ALLOWS_MAXIMIZE) + return &fgeom->max_rect; + else + return NULL; + case META_BUTTON_FUNCTION_CLOSE: + if (flags & META_FRAME_ALLOWS_DELETE) + return &fgeom->close_rect; + else + return NULL; + case META_BUTTON_FUNCTION_LAST: + return NULL; + } + + return NULL; +} + +static gboolean +strip_button (GdkRectangle *func_rects[MAX_BUTTONS_PER_CORNER], + GdkRectangle *bg_rects[MAX_BUTTONS_PER_CORNER], + int *n_rects, + GdkRectangle *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 */ +} + +void +meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, + int text_height, + MetaFrameFlags flags, + int client_width, + int client_height, + const MetaButtonLayout *button_layout, + MetaFrameGeometry *fgeom) +{ + int i, n_left, n_right; int x; int button_y; int title_right_edge; @@ -405,6 +477,14 @@ meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, 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 + */ + GdkRectangle *left_func_rects[MAX_BUTTONS_PER_CORNER]; + GdkRectangle *right_func_rects[MAX_BUTTONS_PER_CORNER]; + GdkRectangle *left_bg_rects[MAX_BUTTONS_PER_CORNER]; + GdkRectangle *right_bg_rects[MAX_BUTTONS_PER_CORNER]; + meta_frame_layout_get_borders (layout, text_height, flags, &fgeom->top_height, @@ -425,8 +505,6 @@ meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, fgeom->left_titlebar_edge = layout->left_titlebar_edge; fgeom->right_titlebar_edge = layout->right_titlebar_edge; - x = width - layout->right_titlebar_edge; - /* gcc warnings */ button_width = -1; button_height = -1; @@ -446,110 +524,202 @@ meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, 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; + i = 0; + while (i < MAX_BUTTONS_PER_CORNER) + { + /* NULL all unused */ + left_func_rects[i] = NULL; + right_func_rects[i] = NULL; + + /* Try to fill in rects */ + if (button_layout->left_buttons[i] != META_BUTTON_FUNCTION_LAST) + { + left_func_rects[n_left] = rect_for_function (fgeom, flags, + button_layout->left_buttons[i]); + if (left_func_rects[n_left] != NULL) + ++n_left; + } + + if (button_layout->right_buttons[i] != META_BUTTON_FUNCTION_LAST) + { + right_func_rects[n_right] = rect_for_function (fgeom, flags, + button_layout->right_buttons[i]); + if (right_func_rects[n_right] != NULL) + ++n_right; + } + + ++i; + } + + i = 0; + while (i < MAX_BUTTONS_PER_CORNER) + { + left_bg_rects[i] = NULL; + right_bg_rects[i] = NULL; + + ++i; + } + + i = 0; + while (i < n_left) + { + 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]; + + ++i; + } + + i = 0; + while (i < n_right) + { + if (i == 0) + right_bg_rects[i] = &fgeom->right_left_background; + else if (i == (n_right - 1)) + right_bg_rects[i] = &fgeom->right_right_background; + else + right_bg_rects[i] = &fgeom->right_middle_backgrounds[i-1]; + + ++i; + } + + /* Be sure buttons fit */ + while (n_left > 0 || n_right > 0) + { + int space_used_by_buttons; + int space_available; + + space_available = fgeom->width - layout->left_titlebar_edge - layout->right_titlebar_edge; + + space_used_by_buttons = 0; + + i = 0; + while (i < n_left) + { + space_used_by_buttons += button_width; + + if (i != n_left) + space_used_by_buttons += layout->button_border.left + layout->button_border.right; + + ++i; + } + + i = 0; + while (i < n_right) + { + space_used_by_buttons += button_width; + + if (i != n_right) + space_used_by_buttons += layout->button_border.left + layout->button_border.right; + + ++i; + } + + if (space_used_by_buttons <= space_available) + break; /* Everything fits, bail out */ + + /* Otherwise we need to shave out a button. Shave + * 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->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 + { + meta_bug ("Could not find a button to strip. n_left = %d n_right = %d\n", + n_left, n_right); + } + } + /* center buttons vertically */ button_y = (fgeom->top_height - (button_height + layout->button_border.top + layout->button_border.bottom)) / 2 + layout->button_border.top; + + /* right edge of farthest-right button */ + x = width - layout->right_titlebar_edge; - if ((flags & META_FRAME_ALLOWS_DELETE) && - x >= 0) + i = n_right - 1; + while (i >= 0) { - fgeom->close_rect.x = x - layout->button_border.right - button_width; - fgeom->close_rect.y = button_y; - fgeom->close_rect.width = button_width; - fgeom->close_rect.height = button_height; + GdkRectangle *rect; - 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 - button_width; - fgeom->max_rect.y = button_y; - fgeom->max_rect.width = button_width; - fgeom->max_rect.height = 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 - button_width; - fgeom->min_rect.y = button_y; - fgeom->min_rect.width = button_width; - fgeom->min_rect.height = 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 (x < 0) /* if we go negative, leave the buttons we don't get to as 0-width */ + break; + + rect = right_func_rects[i]; + + rect->x = x - layout->button_border.right - button_width; + rect->y = button_y; + rect->width = button_width; + rect->height = button_height; + + *(right_bg_rects[i]) = *rect; + + x = rect->x - layout->button_border.left; + + --i; } + /* save right edge of titlebar for later use */ title_right_edge = x - layout->title_border.right; - /* Now x changes to be position from the left */ + /* Now x changes to be position from the left and we go through + * the left-side buttons + */ x = layout->left_titlebar_edge; - if (flags & META_FRAME_ALLOWS_MENU) + i = 0; + while (i < n_left) { - fgeom->menu_rect.x = x + layout->button_border.left; - fgeom->menu_rect.y = button_y; - fgeom->menu_rect.width = button_width; - fgeom->menu_rect.height = 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; + GdkRectangle *rect; + + rect = left_func_rects[i]; + + rect->x = x + layout->button_border.left; + rect->y = button_y; + rect->width = button_width; + rect->height = button_height; + + x = rect->x + rect->width + layout->button_border.right; + + ++i; } /* We always fill as much vertical space as possible with title rect, @@ -3594,39 +3764,51 @@ meta_frame_style_validate (MetaFrameStyle *style, } static void -button_rect (MetaButtonType type, +button_rect (MetaButtonType type, const MetaFrameGeometry *fgeom, - GdkRectangle *rect) + GdkRectangle *rect) { switch (type) { + case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND: + *rect = fgeom->left_left_background; + break; + + case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND: + /* FIXME */ + break; + + case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND: + *rect = fgeom->left_right_background; + break; + + case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND: + *rect = fgeom->right_left_background; + break; + + case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND: + /* FIXME */ + break; + case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND: + *rect = fgeom->right_right_background; + break; + case META_BUTTON_TYPE_CLOSE: *rect = fgeom->close_rect; break; - case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND: case META_BUTTON_TYPE_MAXIMIZE: *rect = fgeom->max_rect; break; - case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND: case META_BUTTON_TYPE_MINIMIZE: *rect = fgeom->min_rect; break; - case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND: case META_BUTTON_TYPE_MENU: *rect = fgeom->menu_rect; break; - - case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND: - case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND: - rect->x = 0; - rect->y = 0; - rect->width = 0; - rect->height = 0; - break; case META_BUTTON_TYPE_LAST: g_assert_not_reached (); @@ -4445,21 +4627,22 @@ meta_theme_get_title_scale (MetaTheme *theme, } void -meta_theme_draw_frame (MetaTheme *theme, - GtkWidget *widget, - GdkDrawable *drawable, - const GdkRectangle *clip, - int x_offset, - int y_offset, - MetaFrameType type, - MetaFrameFlags flags, - int client_width, - int client_height, - PangoLayout *title_layout, - int text_height, - MetaButtonState button_states[META_BUTTON_TYPE_LAST], - GdkPixbuf *mini_icon, - GdkPixbuf *icon) +meta_theme_draw_frame (MetaTheme *theme, + GtkWidget *widget, + GdkDrawable *drawable, + const GdkRectangle *clip, + int x_offset, + int y_offset, + MetaFrameType type, + MetaFrameFlags flags, + int client_width, + int client_height, + PangoLayout *title_layout, + int text_height, + const MetaButtonLayout *button_layout, + MetaButtonState button_states[META_BUTTON_TYPE_LAST], + GdkPixbuf *mini_icon, + GdkPixbuf *icon) { MetaFrameGeometry fgeom; MetaFrameStyle *style; @@ -4476,6 +4659,7 @@ meta_theme_draw_frame (MetaTheme *theme, text_height, flags, client_width, client_height, + button_layout, &fgeom); meta_frame_style_draw (style, @@ -4562,13 +4746,14 @@ meta_theme_get_frame_borders (MetaTheme *theme, } void -meta_theme_calc_geometry (MetaTheme *theme, - MetaFrameType type, - int text_height, - MetaFrameFlags flags, - int client_width, - int client_height, - MetaFrameGeometry *fgeom) +meta_theme_calc_geometry (MetaTheme *theme, + MetaFrameType type, + int text_height, + MetaFrameFlags flags, + int client_width, + int client_height, + const MetaButtonLayout *button_layout, + MetaFrameGeometry *fgeom) { MetaFrameStyle *style; @@ -4584,6 +4769,7 @@ meta_theme_calc_geometry (MetaTheme *theme, text_height, flags, client_width, client_height, + button_layout, fgeom); } diff --git a/src/theme.h b/src/theme.h index 2e4151eb6..806edc373 100644 --- a/src/theme.h +++ b/src/theme.h @@ -103,7 +103,6 @@ struct _MetaFrameLayout guint bottom_right_corner_rounded : 1; }; - /* Calculated actual geometry of the frame */ struct _MetaFrameGeometry { @@ -113,12 +112,8 @@ struct _MetaFrameGeometry int bottom_height; int width; - int height; - - GdkRectangle close_rect; - GdkRectangle max_rect; - GdkRectangle min_rect; - GdkRectangle menu_rect; + int height; + GdkRectangle title_rect; int left_titlebar_edge; @@ -126,6 +121,25 @@ struct _MetaFrameGeometry int top_titlebar_edge; int bottom_titlebar_edge; + /* 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_right_background) + sizeof (GdkRectangle) - G_STRUCT_OFFSET (MetaFrameGeometry, close_rect)) + + /* The button rects (if changed adjust memset hack) */ + GdkRectangle close_rect; + GdkRectangle max_rect; + GdkRectangle min_rect; + GdkRectangle menu_rect; + +#define MAX_MIDDLE_BACKGROUNDS (MAX_BUTTONS_PER_CORNER - 2) + GdkRectangle left_left_background; + GdkRectangle left_middle_backgrounds[MAX_MIDDLE_BACKGROUNDS]; + GdkRectangle left_right_background; + GdkRectangle right_left_background; + GdkRectangle right_middle_backgrounds[MAX_MIDDLE_BACKGROUNDS]; + GdkRectangle right_right_background; + /* End of button rects (if changed adjust memset hack) */ + /* Round corners */ guint top_left_corner_rounded : 1; guint top_right_corner_rounded : 1; @@ -582,12 +596,13 @@ void meta_frame_layout_get_borders (const MetaFrameLayout *layout, int *bottom_height, int *left_width, int *right_width); -void meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, - int text_height, - MetaFrameFlags flags, - int client_width, - int client_height, - MetaFrameGeometry *fgeom); +void meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, + int text_height, + MetaFrameFlags flags, + int client_width, + int client_height, + const MetaButtonLayout *button_layout, + MetaFrameGeometry *fgeom); gboolean meta_frame_layout_validate (const MetaFrameLayout *layout, GError **error); @@ -710,21 +725,22 @@ double meta_theme_get_title_scale (MetaTheme *theme, MetaFrameType type, MetaFrameFlags flags); -void meta_theme_draw_frame (MetaTheme *theme, - GtkWidget *widget, - GdkDrawable *drawable, - const GdkRectangle *clip, - int x_offset, - int y_offset, - MetaFrameType type, - MetaFrameFlags flags, - int client_width, - int client_height, - PangoLayout *title_layout, - int text_height, - MetaButtonState button_states[META_BUTTON_TYPE_LAST], - GdkPixbuf *mini_icon, - GdkPixbuf *icon); +void meta_theme_draw_frame (MetaTheme *theme, + GtkWidget *widget, + GdkDrawable *drawable, + const GdkRectangle *clip, + int x_offset, + int y_offset, + MetaFrameType type, + MetaFrameFlags flags, + int client_width, + int client_height, + PangoLayout *title_layout, + int text_height, + const MetaButtonLayout *button_layout, + MetaButtonState button_states[META_BUTTON_TYPE_LAST], + GdkPixbuf *mini_icon, + GdkPixbuf *icon); void meta_theme_draw_menu_icon (MetaTheme *theme, GtkWidget *widget, @@ -744,14 +760,14 @@ void meta_theme_get_frame_borders (MetaTheme *theme, int *bottom_height, int *left_width, int *right_width); -void meta_theme_calc_geometry (MetaTheme *theme, - MetaFrameType type, - int text_height, - MetaFrameFlags flags, - int client_width, - int client_height, - MetaFrameGeometry *fgeom); - +void meta_theme_calc_geometry (MetaTheme *theme, + MetaFrameType type, + int text_height, + MetaFrameFlags flags, + int client_width, + int client_height, + const MetaButtonLayout *button_layout, + MetaFrameGeometry *fgeom); MetaFrameLayout* meta_theme_lookup_layout (MetaTheme *theme, const char *name);