diff --git a/app/actions/dockable-actions.c b/app/actions/dockable-actions.c index 73d0fa58e6..bf0933e39b 100644 --- a/app/actions/dockable-actions.c +++ b/app/actions/dockable-actions.c @@ -111,7 +111,9 @@ static const GimpRadioActionEntry dockable_tab_style_actions[] = TAB_STYLE ("icon-name", NC_("tab-style", "I_con & Text"), GIMP_TAB_STYLE_ICON_NAME), TAB_STYLE ("preview-name", - NC_("tab-style", "St_atus & Text"), GIMP_TAB_STYLE_PREVIEW_NAME) + NC_("tab-style", "St_atus & Text"), GIMP_TAB_STYLE_PREVIEW_NAME), + TAB_STYLE ("automatic", + NC_("tab-style", "Automatic"), GIMP_TAB_STYLE_AUTOMATIC) }; #undef VIEW_SIZE @@ -176,7 +178,7 @@ dockable_actions_setup (GimpActionGroup *group) dockable_tab_style_actions, G_N_ELEMENTS (dockable_tab_style_actions), NULL, - GIMP_TAB_STYLE_PREVIEW, + GIMP_TAB_STYLE_AUTOMATIC, G_CALLBACK (dockable_tab_style_cmd_callback)); gimp_action_group_add_radio_actions (group, "dockable-action", @@ -344,6 +346,8 @@ dockable_actions_update (GimpActionGroup *group, SET_ACTIVE ("dockable-tab-style-icon-name", TRUE); else if (tab_style == GIMP_TAB_STYLE_PREVIEW_NAME) SET_ACTIVE ("dockable-tab-style-preview-name", TRUE); + else if (tab_style == GIMP_TAB_STYLE_AUTOMATIC) + SET_ACTIVE ("dockable-tab-style-automatic", TRUE); SET_SENSITIVE ("dockable-tab-style-preview", docked_iface->get_preview); diff --git a/app/gimp-log.c b/app/gimp-log.c index 96b5c696fa..3b0eb7a3be 100644 --- a/app/gimp-log.c +++ b/app/gimp-log.c @@ -51,7 +51,8 @@ gimp_log_init (void) { "floating-selection", GIMP_LOG_FLOATING_SELECTION }, { "shm", GIMP_LOG_SHM }, { "text-editing", GIMP_LOG_TEXT_EDITING }, - { "key-events", GIMP_LOG_KEY_EVENTS } + { "key-events", GIMP_LOG_KEY_EVENTS }, + { "auto-tab-style", GIMP_LOG_AUTO_TAB_STYLE } }; /* g_parse_debug_string() has special treatment of the string 'help', diff --git a/app/gimp-log.h b/app/gimp-log.h index 8113e32e35..8aa7444d27 100644 --- a/app/gimp-log.h +++ b/app/gimp-log.h @@ -35,7 +35,8 @@ typedef enum GIMP_LOG_FLOATING_SELECTION = 1 << 11, GIMP_LOG_SHM = 1 << 12, GIMP_LOG_TEXT_EDITING = 1 << 13, - GIMP_LOG_KEY_EVENTS = 1 << 14 + GIMP_LOG_KEY_EVENTS = 1 << 14, + GIMP_LOG_AUTO_TAB_STYLE = 1 << 15 } GimpLogFlags; @@ -91,6 +92,7 @@ void gimp_logv (const gchar *function, #define SHM GIMP_LOG_SHM #define TEXT_EDITING GIMP_LOG_TEXT_EDITING #define KEY_EVENTS GIMP_LOG_KEY_EVENTS +#define AUTO_TAB_STYLE GIMP_LOG_AUTO_TAB_STYLE #if 0 /* last resort */ # define GIMP_LOG /* nothing => no varargs, no log */ diff --git a/app/tests/gimpdir/sessionrc b/app/tests/gimpdir/sessionrc index c3942eaeaa..0e023731d2 100644 --- a/app/tests/gimpdir/sessionrc +++ b/app/tests/gimpdir/sessionrc @@ -67,7 +67,7 @@ (book (current-page 0) (dockable "gimp-channel-list" - (tab-style icon) + (tab-style automatic) (preview-size 32))) (book (position 162) diff --git a/app/tests/gimpdir/sessionrc-expected b/app/tests/gimpdir/sessionrc-expected index 52b6f487f8..d2282b6e51 100644 --- a/app/tests/gimpdir/sessionrc-expected +++ b/app/tests/gimpdir/sessionrc-expected @@ -71,7 +71,7 @@ (book (current-page 0) (dockable "gimp-channel-list" - (tab-style icon) + (tab-style automatic) (preview-size 32))) (book (position 162) diff --git a/app/tests/test-ui.c b/app/tests/test-ui.c index fa222ddc0a..594d8fcc65 100644 --- a/app/tests/test-ui.c +++ b/app/tests/test-ui.c @@ -16,6 +16,7 @@ */ #include +#include #include #include @@ -31,6 +32,8 @@ #include "display/gimpimagewindow.h" #include "widgets/gimpdialogfactory.h" +#include "widgets/gimpdockable.h" +#include "widgets/gimpdockbook.h" #include "widgets/gimpdocked.h" #include "widgets/gimpdockwindow.h" #include "widgets/gimphelp-ids.h" @@ -66,6 +69,8 @@ typedef struct static void gimp_ui_tool_options_editor_updates (GimpTestFixture *fixture, gconstpointer data); +static void gimp_ui_automatic_tab_style (GimpTestFixture *fixture, + gconstpointer data); static void gimp_ui_create_new_image_via_dialog (GimpTestFixture *fixture, gconstpointer data); static void gimp_ui_restore_recently_closed_dock (GimpTestFixture *fixture, @@ -112,6 +117,12 @@ int main(int argc, char **argv) NULL, gimp_ui_tool_options_editor_updates, NULL); + g_test_add ("/gimp-ui/automatic-tab-style", + GimpTestFixture, + gimp, + NULL, + gimp_ui_automatic_tab_style, + NULL); g_test_add ("/gimp-ui/create-new-image-via-dialog", GimpTestFixture, gimp, @@ -155,6 +166,7 @@ int main(int argc, char **argv) gimp_ui_switch_back_to_multi_window_mode, NULL); + /* Run the tests and return status */ result = g_test_run (); @@ -217,6 +229,82 @@ gimp_ui_tool_options_editor_updates (GimpTestFixture *fixture, help_id); } +static GtkWidget * +gimp_ui_get_dialog (const gchar *identifier) +{ + GtkWidget *result = NULL; + GList *iter; + + for (iter = gimp_dialog_factory_get_open_dialogs (gimp_dialog_factory_get_singleton ()); + iter; + iter = g_list_next (iter)) + { + GtkWidget *dialog = GTK_WIDGET (iter->data); + GimpDialogFactoryEntry *entry = NULL; + + gimp_dialog_factory_from_widget (dialog, &entry); + + if (strcmp (entry->identifier, identifier) == 0) + { + result = dialog; + break; + } + } + + return result; +} + +static void +gimp_ui_automatic_tab_style (GimpTestFixture *fixture, + gconstpointer data) +{ + GtkWidget *channel_dockable = gimp_ui_get_dialog ("gimp-channel-list"); + GimpDockable *dockable; + GimpUIManager *ui_manager; + g_assert (channel_dockable != NULL); + + dockable = GIMP_DOCKABLE (channel_dockable); + + /* The channel dockable is the only dockable, it has enough space + * for the icon-blurb + */ + g_assert_cmpint (GIMP_TAB_STYLE_ICON_BLURB, + ==, + gimp_dockable_get_actual_tab_style (dockable)); + + /* Add some dockables to the channel dockable dockbook */ + ui_manager = + gimp_dockbook_get_ui_manager (gimp_dockable_get_dockbook (dockable)); + gimp_ui_manager_activate_action (ui_manager, + "dockable", + "dialogs-sample-points"); + gimp_ui_manager_activate_action (ui_manager, + "dockable", + "dialogs-vectors"); + gimp_test_run_mainloop_until_idle (); + + /* Now there is not enough space to have icon-blurb for channel + * dockable, make sure it's just an icon now + */ + g_assert_cmpint (GIMP_TAB_STYLE_ICON, + ==, + gimp_dockable_get_actual_tab_style (dockable)); + + /* Close the two dockables we added */ + gimp_ui_manager_activate_action (ui_manager, + "dockable", + "dockable-close-tab"); + gimp_ui_manager_activate_action (ui_manager, + "dockable", + "dockable-close-tab"); + gimp_test_run_mainloop_until_idle (); + + /* We should now be back on icon-blurb */ + g_assert_cmpint (GIMP_TAB_STYLE_ICON_BLURB, + ==, + gimp_dockable_get_actual_tab_style (dockable)); +} + static void gimp_ui_create_new_image_via_dialog (GimpTestFixture *fixture, gconstpointer data) diff --git a/app/widgets/gimpdockable.c b/app/widgets/gimpdockable.c index 1f276c08f4..f2936b1776 100644 --- a/app/widgets/gimpdockable.c +++ b/app/widgets/gimpdockable.c @@ -62,6 +62,7 @@ struct _GimpDockablePrivate gchar *stock_id; gchar *help_id; GimpTabStyle tab_style; + GimpTabStyle actual_tab_style; gboolean locked; GimpDockbook *dockbook; @@ -215,9 +216,10 @@ gimp_dockable_init (GimpDockable *dockable) dockable->p = G_TYPE_INSTANCE_GET_PRIVATE (dockable, GIMP_TYPE_DOCKABLE, GimpDockablePrivate); - dockable->p->tab_style = GIMP_TAB_STYLE_PREVIEW; - dockable->p->drag_x = GIMP_DOCKABLE_DRAG_OFFSET; - dockable->p->drag_y = GIMP_DOCKABLE_DRAG_OFFSET; + dockable->p->tab_style = GIMP_TAB_STYLE_AUTOMATIC; + dockable->p->actual_tab_style = GIMP_TAB_STYLE_UNDEFINED; + dockable->p->drag_x = GIMP_DOCKABLE_DRAG_OFFSET; + dockable->p->drag_y = GIMP_DOCKABLE_DRAG_OFFSET; gtk_widget_push_composite_child (); dockable->p->menu_button = gtk_button_new (); @@ -884,6 +886,11 @@ gimp_dockable_new_tab_widget_internal (GimpDockable *dockable, gtk_box_pack_start (GTK_BOX (tab_widget), label, FALSE, FALSE, 0); gtk_widget_show (label); break; + + case GIMP_TAB_STYLE_UNDEFINED: + case GIMP_TAB_STYLE_AUTOMATIC: + g_warning ("Tab style error, unexpected code path taken, fix!"); + break; } return tab_widget; @@ -946,6 +953,22 @@ gimp_dockable_get_tab_style (GimpDockable *dockable) return dockable->p->tab_style; } +/** + * gimp_dockable_get_actual_tab_style: + * @dockable: + * + * Get actual tab style, i.e. never "automatic". This state should + * actually be hold on a per-dockbook basis, but at this point that + * feels like over-engineering... + **/ +GimpTabStyle +gimp_dockable_get_actual_tab_style (GimpDockable *dockable) +{ + g_return_val_if_fail (GIMP_IS_DOCKABLE (dockable), -1); + + return dockable->p->actual_tab_style; +} + const gchar * gimp_dockable_get_name (GimpDockable *dockable) { @@ -1072,6 +1095,39 @@ gimp_dockable_set_tab_style (GimpDockable *dockable, g_return_if_fail (GIMP_IS_DOCKABLE (dockable)); dockable->p->tab_style = gimp_dockable_convert_tab_style (dockable, tab_style); + + if (tab_style == GIMP_TAB_STYLE_AUTOMATIC) + gimp_dockable_set_actual_tab_style (dockable, GIMP_TAB_STYLE_UNDEFINED); + else + gimp_dockable_set_actual_tab_style (dockable, tab_style); + + if (dockable->p->dockbook) + gimp_dockbook_update_auto_tab_style (dockable->p->dockbook); +} + +/** + * gimp_dockable_set_actual_tab_style: + * @dockable: + * @tab_style: + * + * Sets actual tab style, meant for those that decides what + * "automatic" tab style means. + * + * Returns: %TRUE if changed, %FALSE otherwise. + **/ +gboolean +gimp_dockable_set_actual_tab_style (GimpDockable *dockable, + GimpTabStyle tab_style) +{ + GimpTabStyle new_tab_style = gimp_dockable_convert_tab_style (dockable, tab_style); + GimpTabStyle old_tab_style = dockable->p->actual_tab_style; + + g_return_val_if_fail (GIMP_IS_DOCKABLE (dockable), FALSE); + g_return_val_if_fail (tab_style != GIMP_TAB_STYLE_AUTOMATIC, FALSE); + + dockable->p->actual_tab_style = new_tab_style; + + return new_tab_style != old_tab_style; } GtkWidget * diff --git a/app/widgets/gimpdockable.h b/app/widgets/gimpdockable.h index a285b9efe2..52ea00ebdf 100644 --- a/app/widgets/gimpdockable.h +++ b/app/widgets/gimpdockable.h @@ -88,6 +88,9 @@ gboolean gimp_dockable_is_locked (GimpDockable *dockable) void gimp_dockable_set_tab_style (GimpDockable *dockable, GimpTabStyle tab_style); +gboolean gimp_dockable_set_actual_tab_style (GimpDockable *dockable, + GimpTabStyle tab_style); +GimpTabStyle gimp_dockable_get_actual_tab_style (GimpDockable *dockable); GtkWidget * gimp_dockable_create_tab_widget (GimpDockable *dockable, GimpContext *context, GimpTabStyle tab_style, diff --git a/app/widgets/gimpdockbook.c b/app/widgets/gimpdockbook.c index 943e1ffa6b..0fce11e99f 100644 --- a/app/widgets/gimpdockbook.c +++ b/app/widgets/gimpdockbook.c @@ -47,12 +47,13 @@ #include "gimp-log.h" -#define DEFAULT_TAB_BORDER 0 -#define DEFAULT_TAB_ICON_SIZE GTK_ICON_SIZE_BUTTON -#define DND_WIDGET_ICON_SIZE GTK_ICON_SIZE_BUTTON -#define MENU_WIDGET_ICON_SIZE GTK_ICON_SIZE_MENU -#define MENU_WIDGET_SPACING 4 -#define TAB_HOVER_TIMEOUT 500 +#define DEFAULT_TAB_BORDER 0 +#define DEFAULT_TAB_ICON_SIZE GTK_ICON_SIZE_BUTTON +#define DND_WIDGET_ICON_SIZE GTK_ICON_SIZE_BUTTON +#define MENU_WIDGET_ICON_SIZE GTK_ICON_SIZE_MENU +#define MENU_WIDGET_SPACING 4 +#define TAB_HOVER_TIMEOUT 500 +#define GIMP_N_TAB_STYLE_CANDIDATES 3 /* G_N_ELEMENTS (gimp_tab_style_candidates); */ enum @@ -65,18 +66,34 @@ enum struct _GimpDockbookPrivate { - GimpDock *dock; - GimpUIManager *ui_manager; + GimpDock *dock; + GimpUIManager *ui_manager; - guint tab_hover_timeout; - GimpDockable *tab_hover_dockable; + guint tab_hover_timeout; + GimpDockable *tab_hover_dockable; - GimpPanedBox *drag_handler; + GimpPanedBox *drag_handler; + + /* Cache for "what actual tab style for automatic styles can we use + * for a given dockbook width + */ + gint min_width_for_style[GIMP_N_TAB_STYLE_CANDIDATES]; + + /* We need a list separate from the GtkContainer children list, + * because we need to do calculations for all dockables before we + * can add a dockable as a child, namely automatic tab style + * calculations + */ + GList *dockables; + + GtkWidget *menu_button; }; static void gimp_dockbook_dispose (GObject *object); static void gimp_dockbook_finalize (GObject *object); +static void gimp_dockbook_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); static void gimp_dockbook_style_set (GtkWidget *widget, GtkStyle *prev_style); static void gimp_dockbook_drag_leave (GtkWidget *widget, @@ -98,7 +115,8 @@ static void gimp_dockbook_dockable_removed (GimpDockbook *d GimpDockable *dockable); static void gimp_dockbook_update_tabs (GimpDockbook *dockbook, gboolean added); -static void gimp_dockbook_recreate_tab_widgets (GimpDockbook *dockbook); +static void gimp_dockbook_recreate_tab_widgets (GimpDockbook *dockbook, + gboolean only_auto); static void gimp_dockbook_tab_drag_source_setup (GtkWidget *widget, GimpDockable *dockable); static void gimp_dockbook_tab_drag_begin (GtkWidget *widget, @@ -122,6 +140,10 @@ static gboolean gimp_dockbook_tab_drag_drop (GtkWidget *w gint x, gint y, guint time); +static GimpTabStyle gimp_dockbook_tab_style_to_prefered (GimpTabStyle tab_style, + GimpDockable *dockable); +static void gimp_dockbook_refresh_tab_layout_lut (GimpDockbook *dockbook); +static void gimp_dockbook_update_automatic_tab_style (GimpDockbook *dockbook); static GtkWidget * gimp_dockable_create_event_box_tab_widget (GimpDockable *dockable, GimpContext *context, GimpTabStyle tab_style, @@ -136,6 +158,7 @@ static void gimp_dockbook_tab_locked_notify (GimpDockable *d GimpDockbook *dockbook); static void gimp_dockbook_help_func (const gchar *help_id, gpointer help_data); +static const gchar *gimp_dockbook_get_tab_style_name (GimpTabStyle tab_style); G_DEFINE_TYPE (GimpDockbook, gimp_dockbook, GTK_TYPE_NOTEBOOK) @@ -146,6 +169,15 @@ static guint dockbook_signals[LAST_SIGNAL] = { 0 }; static const GtkTargetEntry dialog_target_table[] = { GIMP_TARGET_DIALOG }; +/* List of candidates for the automatic style, starting with the + * biggest first + */ +static GimpTabStyle gimp_tab_style_candidates[] = { + GIMP_TAB_STYLE_PREVIEW_BLURB, + GIMP_TAB_STYLE_PREVIEW_NAME, + GIMP_TAB_STYLE_PREVIEW, +}; + static void gimp_dockbook_class_init (GimpDockbookClass *klass) @@ -153,6 +185,9 @@ gimp_dockbook_class_init (GimpDockbookClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + if (G_N_ELEMENTS (gimp_tab_style_candidates) != GIMP_N_TAB_STYLE_CANDIDATES) + g_error ("Update GIMP_N_TAB_STYLE_CANDIDATES"); + dockbook_signals[DOCKABLE_ADDED] = g_signal_new ("dockable-added", G_TYPE_FROM_CLASS (klass), @@ -186,10 +221,11 @@ gimp_dockbook_class_init (GimpDockbookClass *klass) object_class->dispose = gimp_dockbook_dispose; object_class->finalize = gimp_dockbook_finalize; - widget_class->style_set = gimp_dockbook_style_set; - widget_class->drag_leave = gimp_dockbook_drag_leave; - widget_class->drag_motion = gimp_dockbook_drag_motion; - widget_class->drag_drop = gimp_dockbook_drag_drop; + widget_class->size_allocate = gimp_dockbook_size_allocate; + widget_class->style_set = gimp_dockbook_style_set; + widget_class->drag_leave = gimp_dockbook_drag_leave; + widget_class->drag_motion = gimp_dockbook_drag_motion; + widget_class->drag_drop = gimp_dockbook_drag_drop; klass->dockable_added = gimp_dockbook_dockable_added; klass->dockable_removed = gimp_dockbook_dockable_removed; @@ -254,6 +290,18 @@ gimp_dockbook_finalize (GObject *object) G_OBJECT_CLASS (parent_class)->finalize (object); } +static void +gimp_dockbook_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GimpDockbook *dockbook = GIMP_DOCKBOOK (widget); + + GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation); + + /* Update tab styles, also recreates if changed */ + gimp_dockbook_update_automatic_tab_style (dockbook); +} + static void gimp_dockbook_style_set (GtkWidget *widget, GtkStyle *prev_style) @@ -279,7 +327,8 @@ gimp_dockbook_style_set (GtkWidget *widget, "tab-border", tab_border, NULL); - gimp_dockbook_recreate_tab_widgets (GIMP_DOCKBOOK (widget)); + gimp_dockbook_recreate_tab_widgets (GIMP_DOCKBOOK (widget), + FALSE /*only_auto*/); } static void @@ -382,6 +431,241 @@ gimp_dockbook_update_tabs (GimpDockbook *dockbook, } } +/** + * gimp_dockbook_get_dockable_tab_width: + * @dockable: + * @tab_style: + * + * Returns: Width of tab when the dockable is using the specified tab + * style. + **/ +static gint +gimp_dockbook_get_dockable_tab_width (GimpDockbook *dockbook, + GimpDockable *dockable, + GimpTabStyle tab_style) +{ + GtkRequisition dockable_request; + GtkWidget *tab_widget; + + tab_widget = + gimp_dockable_create_event_box_tab_widget (dockable, + gimp_dock_get_context (dockbook->p->dock), + tab_style, + gimp_dockbook_get_tab_icon_size (dockbook)); + + /* So font-scale is applied. We can't apply styles without having a + * GdkScreen :( + */ + gimp_dock_temp_add (dockbook->p->dock, tab_widget); + + gtk_widget_size_request (tab_widget, &dockable_request); + + /* Also destroys the widget */ + gimp_dock_temp_remove (dockbook->p->dock, tab_widget); + + return dockable_request.width; +} + +/** + * gimp_dockbook_tab_style_to_prefered: + * @tab_style: + * @dockable: + * + * The list of tab styles to try in automatic mode only consists of + * preview styles. For some dockables, like the tool options dockable, + * we rather want to use the icon tab styles for the automatic + * mode. This function is used to convert tab styles for such + * dockables. + * + * Returns: An icon tab style if the dockable prefers icon tab styles + * in automatic mode. + **/ +static GimpTabStyle +gimp_dockbook_tab_style_to_prefered (GimpTabStyle tab_style, + GimpDockable *dockable) +{ + GimpDocked *docked = GIMP_DOCKED (gtk_bin_get_child (GTK_BIN (dockable))); + + if (gimp_docked_get_prefer_icon (docked)) + tab_style = gimp_preview_tab_style_to_icon (tab_style); + + return tab_style; +} + +/** + * gimp_dockbook_refresh_tab_layout_lut: + * @dockbook: + * + * For each given set of tab widgets, there is a fixed mapping between + * the width of the dockbook and the actual tab style to use for auto + * tab widgets. This function refreshes that look-up table. + **/ +static void +gimp_dockbook_refresh_tab_layout_lut (GimpDockbook *dockbook) +{ + GList *auto_dockables = NULL; + GList *iter = NULL; + gint fixed_tab_style_space = 0; + int i = 0; + + /* Calculate space taken by dockables with fixed tab styles */ + fixed_tab_style_space = 0; + for (iter = dockbook->p->dockables; iter; iter = g_list_next (iter)) + { + GimpDockable *dockable = GIMP_DOCKABLE (iter->data); + GimpTabStyle tab_style = gimp_dockable_get_tab_style (dockable); + + if (tab_style == GIMP_TAB_STYLE_AUTOMATIC) + auto_dockables = g_list_prepend (auto_dockables, dockable); + else + fixed_tab_style_space += + gimp_dockbook_get_dockable_tab_width (dockbook, + dockable, + tab_style); + } + + /* Calculate space taken with auto tab style for all candidates */ + for (i = 0; i < GIMP_N_TAB_STYLE_CANDIDATES; i++) + { + gint size_with_candidate = 0; + GimpTabStyle candidate = gimp_tab_style_candidates[i]; + + for (iter = auto_dockables; iter; iter = g_list_next (iter)) + { + GimpDockable *dockable = GIMP_DOCKABLE (iter->data); + GimpTabStyle style_to_use; + + style_to_use = gimp_dockbook_tab_style_to_prefered (candidate, + dockable); + size_with_candidate += + gimp_dockbook_get_dockable_tab_width (dockbook, + dockable, + style_to_use); + } + + dockbook->p->min_width_for_style[i] = + fixed_tab_style_space + size_with_candidate; + + GIMP_LOG (AUTO_TAB_STYLE, "Total tab space taken for auto tab style %s = %d", + gimp_dockbook_get_tab_style_name (candidate), + dockbook->p->min_width_for_style[i]); + } + + g_list_free (auto_dockables); +} + +/** + * gimp_dockbook_update_automatic_tab_style: + * @dockbook: + * + * Based on widget allocation, sets actual tab style for dockables + * with automatic tab styles. Takes care of recreating tab widgets if + * necessary. + **/ +static void +gimp_dockbook_update_automatic_tab_style (GimpDockbook *dockbook) +{ + GtkWidget *widget = GTK_WIDGET (dockbook); + gboolean changed = FALSE; + GList *iter = NULL; + GtkAllocation dockbook_allocation = { 0, }; + GtkAllocation button_allocation = { 0, }; + GimpTabStyle tab_style = 0; + int i = 0; + gint available_space = 0; + guint tab_hborder = 0; + gint xthickness = 0; + gint tab_curvature = 0; + gint focus_width = 0; + gint tab_overlap = 0; + gint tab_padding = 0; + gint border_loss = 0; + gint action_widget_size = 0; + + xthickness = gtk_widget_get_style (widget)->xthickness; + g_object_get (widget, + "tab-hborder", &tab_hborder, + NULL); + gtk_widget_style_get (widget, + "tab-curvature", &tab_curvature, + "focus-line-width", &focus_width, + "tab-overlap", &tab_overlap, + NULL); + gtk_widget_get_allocation (dockbook->p->menu_button, + &button_allocation); + + /* Calculate available space. Based on code in GTK+ internal + * functions gtk_notebook_size_request() and + * gtk_notebook_pages_allocate() + */ + gtk_widget_get_allocation (widget, &dockbook_allocation); + + /* Border on both sides */ + border_loss = gtk_container_get_border_width (GTK_CONTAINER (dockbook)) * 2; + + /* Space taken by action widget */ + action_widget_size = button_allocation.width + xthickness; + + /* Space taken by the tabs but not the tab widgets themselves */ + tab_padding = gtk_notebook_get_n_pages (GTK_NOTEBOOK (dockbook)) * + (2 * (xthickness + tab_curvature + focus_width + tab_hborder) - + tab_overlap); + + available_space = dockbook_allocation.width + - border_loss + - action_widget_size + - tab_padding + - tab_overlap; + + GIMP_LOG (AUTO_TAB_STYLE, "\n" + " available_space = %d where\n" + " dockbook_allocation.width = %d\n" + " border_loss = %d\n" + " action_widget_size = %d\n" + " tab_padding = %d\n" + " tab_overlap = %d\n", + available_space, + dockbook_allocation.width, + border_loss, + action_widget_size, + tab_padding, + tab_overlap); + + /* Try all candidates, if we don't get any hit we still end up on + * the smallest style (which we always fall back to if we don't get + * a better match) + */ + for (i = 0; i < GIMP_N_TAB_STYLE_CANDIDATES; i++) + { + tab_style = gimp_tab_style_candidates[i]; + if (available_space > dockbook->p->min_width_for_style[i]) + { + GIMP_LOG (AUTO_TAB_STYLE, "Choosing tab style %s", + gimp_dockbook_get_tab_style_name (tab_style)); + break; + } + } + + for (iter = dockbook->p->dockables; iter; iter = g_list_next (iter)) + { + GimpDockable *dockable = GIMP_DOCKABLE (iter->data); + GimpTabStyle actual_tab_style = tab_style; + + if (gimp_dockable_get_tab_style (dockable) != GIMP_TAB_STYLE_AUTOMATIC) + continue; + + actual_tab_style = gimp_dockbook_tab_style_to_prefered (tab_style, + dockable); + + if (gimp_dockable_set_actual_tab_style (dockable, actual_tab_style)) + changed = TRUE; + } + + if (changed) + gimp_dockbook_recreate_tab_widgets (dockbook, + TRUE /*only_auto*/); +} + GtkWidget * gimp_dockbook_new (GimpMenuFactory *menu_factory) { @@ -443,6 +727,16 @@ gimp_dockbook_add (GimpDockbook *dockbook, GIMP_LOG (DND, "Adding GimpDockable %p to GimpDockbook %p", dockable, dockbook); + /* Add to internal list before doing automatic tab style + * calculations + */ + dockbook->p->dockables = g_list_insert (dockbook->p->dockables, + dockable, + position); + + gimp_dockbook_update_auto_tab_style (dockbook); + + /* Create the new tab widget, it will get the correct tab style now */ tab_widget = gimp_dockbook_create_tab_widget (dockbook, dockable); g_return_if_fail (GTK_IS_WIDGET (tab_widget)); @@ -515,11 +809,15 @@ gimp_dockbook_remove (GimpDockbook *dockbook, gimp_dockable_set_context (dockable, NULL); gtk_container_remove (GTK_CONTAINER (dockbook), GTK_WIDGET (dockable)); + dockbook->p->dockables = g_list_remove (dockbook->p->dockables, + dockable); g_signal_emit (dockbook, dockbook_signals[DOCKABLE_REMOVED], 0, dockable); g_object_unref (dockable); + gimp_dockbook_update_auto_tab_style (dockbook); + children = gtk_container_get_children (GTK_CONTAINER (dockbook)); if (! g_list_length (children)) @@ -565,7 +863,7 @@ gimp_dockbook_create_tab_widget (GimpDockbook *dockbook, tab_widget = gimp_dockable_create_event_box_tab_widget (dockable, gimp_dock_get_context (dockbook->p->dock), - gimp_dockable_get_tab_style (dockable), + gimp_dockable_get_actual_tab_style (dockable), gimp_dockbook_get_tab_icon_size (dockbook)); /* EEK */ @@ -649,6 +947,23 @@ gimp_dockbook_create_tab_widget (GimpDockbook *dockbook, return tab_widget; } +/** + * gimp_dockbook_update_auto_tab_style: + * @dockbook: + * + * Refresh the table that we use to map dockbook width to actual auto + * tab style, then update auto tabs (also recreate tab widgets if + * necessary). + **/ +void +gimp_dockbook_update_auto_tab_style (GimpDockbook *dockbook) +{ + g_return_if_fail (GIMP_IS_DOCKBOOK (dockbook)); + + gimp_dockbook_refresh_tab_layout_lut (dockbook); + gimp_dockbook_update_automatic_tab_style (dockbook); +} + gboolean gimp_dockbook_drop_dockable (GimpDockbook *dockbook, GtkWidget *drag_source) @@ -730,7 +1045,8 @@ gimp_dockbook_drag_source_to_dockable (GtkWidget *drag_source) /* tab DND source side */ static void -gimp_dockbook_recreate_tab_widgets (GimpDockbook *dockbook) +gimp_dockbook_recreate_tab_widgets (GimpDockbook *dockbook, + gboolean only_auto) { GList *dockables = gtk_container_get_children (GTK_CONTAINER (dockbook)); GList *iter = NULL; @@ -740,6 +1056,10 @@ gimp_dockbook_recreate_tab_widgets (GimpDockbook *dockbook) GimpDockable *dockable = GIMP_DOCKABLE (iter->data); GtkWidget *tab_widget; + if (only_auto && + ! gimp_dockable_get_tab_style (dockable) == GIMP_TAB_STYLE_AUTOMATIC) + continue; + tab_widget = gimp_dockbook_create_tab_widget (dockbook, dockable); gtk_notebook_set_tab_label (GTK_NOTEBOOK (dockbook), @@ -1109,3 +1429,10 @@ gimp_dockbook_help_func (const gchar *help_id, else gimp_standard_help_func (GIMP_HELP_DOCK, NULL); } + +static const gchar * +gimp_dockbook_get_tab_style_name (GimpTabStyle tab_style) +{ + return g_enum_get_value (g_type_class_peek (GIMP_TYPE_TAB_STYLE), + tab_style)->value_name; +} diff --git a/app/widgets/gimpdockbook.h b/app/widgets/gimpdockbook.h index f43b6803e0..e3249febbb 100644 --- a/app/widgets/gimpdockbook.h +++ b/app/widgets/gimpdockbook.h @@ -74,6 +74,7 @@ void gimp_dockbook_update_with_context (GimpDockbook *dockbo GimpContext *context); GtkWidget * gimp_dockbook_create_tab_widget (GimpDockbook *dockbook, GimpDockable *dockable); +void gimp_dockbook_update_auto_tab_style (GimpDockbook *dockbook); gboolean gimp_dockbook_drop_dockable (GimpDockbook *dockbook, GtkWidget *drag_source); void gimp_dockbook_set_drag_handler (GimpDockbook *dockbook, diff --git a/app/widgets/gimpdocked.c b/app/widgets/gimpdocked.c index 81e57b1202..14d8246721 100644 --- a/app/widgets/gimpdocked.c +++ b/app/widgets/gimpdocked.c @@ -191,6 +191,21 @@ gimp_docked_get_preview (GimpDocked *docked, return NULL; } +gboolean +gimp_docked_get_prefer_icon (GimpDocked *docked) +{ + GimpDockedInterface *docked_iface; + + g_return_val_if_fail (GIMP_IS_DOCKED (docked), FALSE); + + docked_iface = GIMP_DOCKED_GET_INTERFACE (docked); + + if (docked_iface->get_prefer_icon) + return docked_iface->get_prefer_icon (docked); + + return FALSE; +} + GimpUIManager * gimp_docked_get_menu (GimpDocked *docked, const gchar **ui_path, diff --git a/app/widgets/gimpdocked.h b/app/widgets/gimpdocked.h index 04d73affbd..fe9e269ad9 100644 --- a/app/widgets/gimpdocked.h +++ b/app/widgets/gimpdocked.h @@ -50,6 +50,7 @@ struct _GimpDockedInterface GtkWidget * (* get_preview) (GimpDocked *docked, GimpContext *context, GtkIconSize size); + gboolean (* get_prefer_icon) (GimpDocked *docked); GimpUIManager * (* get_menu) (GimpDocked *docked, const gchar **ui_path, gpointer *popup_data); @@ -76,6 +77,7 @@ GList * gimp_docked_get_aux_info (GimpDocked *docked); GtkWidget * gimp_docked_get_preview (GimpDocked *docked, GimpContext *context, GtkIconSize size); +gboolean gimp_docked_get_prefer_icon (GimpDocked *docked); GimpUIManager * gimp_docked_get_menu (GimpDocked *docked, const gchar **ui_path, gpointer *popup_data); diff --git a/app/widgets/gimptooloptionseditor.c b/app/widgets/gimptooloptionseditor.c index 3fd282d20e..63154bd148 100644 --- a/app/widgets/gimptooloptionseditor.c +++ b/app/widgets/gimptooloptionseditor.c @@ -88,6 +88,7 @@ static GtkWidget * gimp_tool_options_editor_get_preview (GimpDocked GimpContext *context, GtkIconSize size); static gchar * gimp_tool_options_editor_get_title (GimpDocked *docked); +static gboolean gimp_tool_options_editor_get_prefer_icon (GimpDocked *docked); static void gimp_tool_options_editor_save_clicked (GtkWidget *widget, GimpToolOptionsEditor *editor); static void gimp_tool_options_editor_restore_clicked (GtkWidget *widget, @@ -184,8 +185,9 @@ gimp_tool_options_editor_init (GimpToolOptionsEditor *editor) static void gimp_tool_options_editor_docked_iface_init (GimpDockedInterface *docked_iface) { - docked_iface->get_preview = gimp_tool_options_editor_get_preview; - docked_iface->get_title = gimp_tool_options_editor_get_title; + docked_iface->get_preview = gimp_tool_options_editor_get_preview; + docked_iface->get_title = gimp_tool_options_editor_get_title; + docked_iface->get_prefer_icon = gimp_tool_options_editor_get_prefer_icon; } static GObject * @@ -349,6 +351,15 @@ gimp_tool_options_editor_get_title (GimpDocked *docked) return tool_info ? g_strdup (tool_info->blurb) : NULL; } +static gboolean +gimp_tool_options_editor_get_prefer_icon (GimpDocked *docked) +{ + /* We support get_preview() for tab tyles, but we prefer to show our + * icon + */ + return TRUE; +} + /* public functions */ diff --git a/app/widgets/widgets-enums.c b/app/widgets/widgets-enums.c index 826edb1eb4..9f6d07bf72 100644 --- a/app/widgets/widgets-enums.c +++ b/app/widgets/widgets-enums.c @@ -205,6 +205,8 @@ gimp_tab_style_get_type (void) { GIMP_TAB_STYLE_ICON_BLURB, "GIMP_TAB_STYLE_ICON_BLURB", "icon-blurb" }, { GIMP_TAB_STYLE_PREVIEW_NAME, "GIMP_TAB_STYLE_PREVIEW_NAME", "preview-name" }, { GIMP_TAB_STYLE_PREVIEW_BLURB, "GIMP_TAB_STYLE_PREVIEW_BLURB", "preview-blurb" }, + { GIMP_TAB_STYLE_UNDEFINED, "GIMP_TAB_STYLE_UNDEFINED", "undefined" }, + { GIMP_TAB_STYLE_AUTOMATIC, "GIMP_TAB_STYLE_AUTOMATIC", "automatic" }, { 0, NULL, NULL } }; @@ -218,6 +220,8 @@ gimp_tab_style_get_type (void) { GIMP_TAB_STYLE_ICON_BLURB, NC_("tab-style", "Icon & desc"), NULL }, { GIMP_TAB_STYLE_PREVIEW_NAME, NC_("tab-style", "Status & text"), NULL }, { GIMP_TAB_STYLE_PREVIEW_BLURB, NC_("tab-style", "Status & desc"), NULL }, + { GIMP_TAB_STYLE_UNDEFINED, NC_("tab-style", "Undefined"), NULL }, + { GIMP_TAB_STYLE_AUTOMATIC, NC_("tab-style", "Automatic"), NULL }, { 0, NULL, NULL } }; diff --git a/app/widgets/widgets-enums.h b/app/widgets/widgets-enums.h index 56ffb4e97f..894214d55b 100644 --- a/app/widgets/widgets-enums.h +++ b/app/widgets/widgets-enums.h @@ -107,7 +107,9 @@ typedef enum GIMP_TAB_STYLE_ICON_NAME, /*< desc="Icon & text" >*/ GIMP_TAB_STYLE_ICON_BLURB, /*< desc="Icon & desc" >*/ GIMP_TAB_STYLE_PREVIEW_NAME, /*< desc="Status & text" >*/ - GIMP_TAB_STYLE_PREVIEW_BLURB /*< desc="Status & desc" >*/ + GIMP_TAB_STYLE_PREVIEW_BLURB, /*< desc="Status & desc" >*/ + GIMP_TAB_STYLE_UNDEFINED, /*< desc="Undefined" >*/ + GIMP_TAB_STYLE_AUTOMATIC /*< desc="Automatic" >*/ } GimpTabStyle; diff --git a/etc/sessionrc b/etc/sessionrc index f5673165bc..f41c3bf75e 100644 --- a/etc/sessionrc +++ b/etc/sessionrc @@ -12,7 +12,7 @@ (gimp-toolbox (book (dockable "gimp-tool-options" - (tab-style icon))))) + (tab-style automatic))))) (session-info "dock" "gimp-dock-window" (position -0 0) (size 210 820) @@ -20,21 +20,21 @@ (gimp-dock (book (dockable "gimp-layer-list" - (tab-style icon)) + (tab-style automatic)) (dockable "gimp-channel-list" - (tab-style icon)) + (tab-style automatic)) (dockable "gimp-vectors-list" - (tab-style icon)) + (tab-style automatic)) (dockable "gimp-undo-history" - (tab-style icon))) + (tab-style automatic))) (book (position 420) (dockable "gimp-brush-grid" - (tab-style preview)) + (tab-style automatic)) (dockable "gimp-pattern-grid" - (tab-style preview)) + (tab-style automatic)) (dockable "gimp-gradient-list" - (tab-style preview))))) + (tab-style automatic))))) (session-info "toplevel" (factory-entry "gimp-empty-image-window") (position 410 370) diff --git a/menus/dockable-menu.xml.in b/menus/dockable-menu.xml.in index 29301d1fc6..55eff39a02 100644 --- a/menus/dockable-menu.xml.in +++ b/menus/dockable-menu.xml.in @@ -27,6 +27,8 @@ + +