From e23ed245db960b12e0974f5f091c300a2fb8b9ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?=
Date: Fri, 17 Dec 2021 07:39:53 +0100 Subject: [PATCH] Add toolbar toggle buttons for scale-to-fit/filter --- fastiv.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++------ fiv-view.c | 56 +++++++++++++++++++++++++++++------------- fiv-view.h | 4 ++- 3 files changed, 105 insertions(+), 26 deletions(-) diff --git a/fastiv.c b/fastiv.c index 25b8109..80744ea 100644 --- a/fastiv.c +++ b/fastiv.c @@ -57,6 +57,7 @@ exit_fatal(const gchar *format, ...) // TODO(p): Add a toggle for a checkerboard background. // TODO(p): Implement commented-out actions. #define B make_toolbar_button +#define T make_toolbar_toggle #define TOOLBAR(XX) \ XX(BROWSE, B("view-grid-symbolic", "Browse")) \ XX(FILE_PREVIOUS, B("go-previous-symbolic", "Previous file")) \ @@ -77,11 +78,11 @@ exit_fatal(const gchar *format, ...) XX(SCALE, gtk_label_new("100%")) \ XX(MINUS, B("zoom-out-symbolic", "Zoom out")) \ XX(ONE, B("zoom-original-symbolic", "Original size")) \ - /* XX(FIT, B("zoom-fit-best-symbolic", "Scale to fit")) */ \ + XX(FIT, T("zoom-fit-best-symbolic", "Scale to fit")) \ XX(S4, make_separator()) \ /* XX(PIN, B("view-pin-symbolic", "Keep view configuration")) */ \ /* Or perhaps "blur-symbolic", also in the extended set. */ \ - /* XX(SMOOTH, B("blend-tool-symbolic", "Smooth scaling")) */ \ + XX(SMOOTH, T("blend-tool-symbolic", "Smooth scaling")) \ /* XX(COLOR, B("preferences-color-symbolic", "Color management")) */ \ XX(SAVE, B("document-save-as-symbolic", "Save as...")) \ XX(PRINT, B("document-print-symbolic", "Print...")) \ @@ -608,6 +609,21 @@ make_toolbar_button(const gchar *symbolic, const gchar *tooltip) return button; } +static GtkWidget * +make_toolbar_toggle(const gchar *symbolic, const gchar *tooltip) +{ + GtkWidget *button = gtk_toggle_button_new(); + gtk_button_set_image(GTK_BUTTON(button), + gtk_image_new_from_icon_name(symbolic, GTK_ICON_SIZE_BUTTON)); + gtk_widget_set_tooltip_text(button, tooltip); +// gtk_widget_set_sensitive(button, FALSE); + gtk_widget_set_focus_on_click(button, FALSE); + + gtk_style_context_add_class( + gtk_widget_get_style_context(button), GTK_STYLE_CLASS_FLAT); + return button; +} + static GtkWidget * make_separator(void) { @@ -620,7 +636,7 @@ make_separator(void) } static void -on_notify_scale( +on_notify_view_scale( GObject *object, GParamSpec *param_spec, G_GNUC_UNUSED gpointer user_data) { double scale = 0; @@ -629,16 +645,38 @@ on_notify_scale( gchar *scale_str = g_strdup_printf("%.0f%%", round(scale * 100)); gtk_label_set_text(GTK_LABEL(g.toolbar[TOOLBAR_SCALE]), scale_str); g_free(scale_str); + + // FIXME: The label doesn't immediately assume its new width. } static void -toolbar_connect(int index, GCallback callback) +on_notify_view_boolean( + GObject *object, GParamSpec *param_spec, gpointer user_data) { - g_signal_connect_swapped(g.toolbar[index], "clicked", callback, NULL); + gboolean b = FALSE; + g_object_get(object, g_param_spec_get_name(param_spec), &b, NULL); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(user_data), b); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +static void +on_toolbar_view_toggle(GtkToggleButton *button, const char *property) +{ + g_object_set(g.view, property, gtk_toggle_button_get_active(button), NULL); } static void -on_command(intptr_t command) +toolbar_toggler(int index, const char *property) +{ + g_signal_connect(g.toolbar[index], "toggled", + G_CALLBACK(on_toolbar_view_toggle), (gpointer) property); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +static void +on_toolbar_view_command(intptr_t command) { fiv_view_command(FIV_VIEW(g.view), command); } @@ -647,7 +685,15 @@ static void toolbar_command(int index, FivViewCommand command) { g_signal_connect_swapped(g.toolbar[index], "clicked", - G_CALLBACK(on_command), (void *) (intptr_t) command); + G_CALLBACK(on_toolbar_view_command), (void *) (intptr_t) command); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +static void +toolbar_connect(int index, GCallback callback) +{ + g_signal_connect_swapped(g.toolbar[index], "clicked", callback, NULL); } // TODO(p): The toolbar should not be visible in fullscreen, @@ -686,12 +732,21 @@ make_view_toolbar(void) toolbar_command(TOOLBAR_PLUS, FIV_VIEW_COMMAND_ZOOM_IN); toolbar_command(TOOLBAR_MINUS, FIV_VIEW_COMMAND_ZOOM_OUT); toolbar_command(TOOLBAR_ONE, FIV_VIEW_COMMAND_ZOOM_1); + toolbar_toggler(TOOLBAR_FIT, "scale-to-fit"); + toolbar_toggler(TOOLBAR_SMOOTH, "filter"); toolbar_command(TOOLBAR_PRINT, FIV_VIEW_COMMAND_PRINT); toolbar_command(TOOLBAR_SAVE, FIV_VIEW_COMMAND_SAVE_PAGE); toolbar_command(TOOLBAR_LEFT, FIV_VIEW_COMMAND_ROTATE_LEFT); toolbar_command(TOOLBAR_MIRROR, FIV_VIEW_COMMAND_MIRROR); toolbar_command(TOOLBAR_RIGHT, FIV_VIEW_COMMAND_ROTATE_RIGHT); toolbar_connect(TOOLBAR_FULLSCREEN, G_CALLBACK(toggle_fullscreen)); + + g_signal_connect(g.view, "notify::scale-to-fit", + G_CALLBACK(on_notify_view_boolean), g.toolbar[TOOLBAR_FIT]); + g_signal_connect(g.view, "notify::filter", + G_CALLBACK(on_notify_view_boolean), g.toolbar[TOOLBAR_SMOOTH]); + g_object_notify(G_OBJECT(g.view), "scale-to-fit"); + g_object_notify(G_OBJECT(g.view), "filter"); return view_toolbar; } @@ -786,7 +841,7 @@ main(int argc, char *argv[]) g_signal_connect(g.view, "button-press-event", G_CALLBACK(on_button_press_view), NULL); g_signal_connect(g.view, "notify::scale", - G_CALLBACK(on_notify_scale), NULL); + G_CALLBACK(on_notify_view_scale), NULL); gtk_container_add(GTK_CONTAINER(view_scroller), g.view); // Maybe our custom widgets should derive colours from the theme instead. diff --git a/fiv-view.c b/fiv-view.c index e064b56..4593b48 100644 --- a/fiv-view.c +++ b/fiv-view.c @@ -122,6 +122,25 @@ fiv_view_get_property( } } +static void +fiv_view_set_property( + GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ + FivView *self = FIV_VIEW(object); + switch (property_id) { + case PROP_SCALE_TO_FIT: + if (self->scale_to_fit != g_value_get_boolean(value)) + fiv_view_command(self, FIV_VIEW_COMMAND_TOGGLE_SCALE_TO_FIT); + break; + case PROP_FILTER: + if (self->filter != g_value_get_boolean(value)) + fiv_view_command(self, FIV_VIEW_COMMAND_TOGGLE_FILTER); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + static void get_surface_dimensions(FivView *self, double *width, double *height) { @@ -396,16 +415,17 @@ fiv_view_button_press_event(GtkWidget *widget, GdkEventButton *event) return FALSE; } -#define SCALE_STEP 1.4 +#define SCALE_STEP 1.25 static gboolean set_scale_to_fit(FivView *self, bool scale_to_fit) { - self->scale_to_fit = scale_to_fit; - - gtk_widget_queue_resize(GTK_WIDGET(self)); - g_object_notify_by_pspec( - G_OBJECT(self), view_properties[PROP_SCALE_TO_FIT]); + if (self->scale_to_fit != scale_to_fit) { + self->scale_to_fit = scale_to_fit; + g_object_notify_by_pspec( + G_OBJECT(self), view_properties[PROP_SCALE_TO_FIT]); + gtk_widget_queue_resize(GTK_WIDGET(self)); + } return TRUE; } @@ -413,8 +433,8 @@ static gboolean set_scale(FivView *self, double scale) { self->scale = scale; - g_object_notify_by_pspec( - G_OBJECT(self), view_properties[PROP_SCALE]); + g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_SCALE]); + gtk_widget_queue_resize(GTK_WIDGET(self)); return set_scale_to_fit(self, false); } @@ -750,14 +770,9 @@ fiv_view_key_press_event(GtkWidget *widget, GdkEventKey *event) return command(self, FIV_VIEW_COMMAND_ZOOM_OUT); case GDK_KEY_x: // Inspired by gThumb. - return set_scale_to_fit(self, !self->scale_to_fit); - + return command(self, FIV_VIEW_COMMAND_TOGGLE_SCALE_TO_FIT); case GDK_KEY_i: - self->filter = !self->filter; - g_object_notify_by_pspec( - G_OBJECT(self), view_properties[PROP_FILTER]); - gtk_widget_queue_draw(widget); - return TRUE; + return command(self, FIV_VIEW_COMMAND_TOGGLE_FILTER); case GDK_KEY_less: return command(self, FIV_VIEW_COMMAND_ROTATE_LEFT); @@ -785,16 +800,17 @@ fiv_view_class_init(FivViewClass *klass) GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->finalize = fiv_view_finalize; object_class->get_property = fiv_view_get_property; + object_class->set_property = fiv_view_set_property; view_properties[PROP_SCALE] = g_param_spec_double( "scale", "Scale", "Zoom level", 0, G_MAXDOUBLE, 1.0, G_PARAM_READABLE); view_properties[PROP_SCALE_TO_FIT] = g_param_spec_boolean( "scale-to-fit", "Scale to fit", "Scale images down to fit the window", - TRUE, G_PARAM_READABLE); + TRUE, G_PARAM_READWRITE); view_properties[PROP_FILTER] = g_param_spec_boolean( "filter", "Use filtering", "Scale images smoothly", - TRUE, G_PARAM_READABLE); + TRUE, G_PARAM_READWRITE); g_object_class_install_properties( object_class, N_PROPERTIES, view_properties); @@ -903,6 +919,10 @@ fiv_view_command(FivView *self, FivViewCommand command) break; case FIV_VIEW_COMMAND_FRAME_NEXT: frame_step(self, +1); + break; case FIV_VIEW_COMMAND_TOGGLE_FILTER: + self->filter = !self->filter; + g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_FILTER]); + gtk_widget_queue_draw(widget); break; case FIV_VIEW_COMMAND_PRINT: print(self); break; case FIV_VIEW_COMMAND_SAVE_PAGE: @@ -914,5 +934,7 @@ fiv_view_command(FivView *self, FivViewCommand command) set_scale(self, self->scale / SCALE_STEP); break; case FIV_VIEW_COMMAND_ZOOM_1: set_scale(self, 1.0); + break; case FIV_VIEW_COMMAND_TOGGLE_SCALE_TO_FIT: + set_scale_to_fit(self, !self->scale_to_fit); } } diff --git a/fiv-view.h b/fiv-view.h index 92c89e9..579ba4c 100644 --- a/fiv-view.h +++ b/fiv-view.h @@ -40,12 +40,14 @@ typedef enum _FivViewCommand { FIV_VIEW_COMMAND_FRAME_NEXT, // Going to the end frame makes no sense, wrap around if needed. + FIV_VIEW_COMMAND_TOGGLE_FILTER, FIV_VIEW_COMMAND_PRINT, FIV_VIEW_COMMAND_SAVE_PAGE, FIV_VIEW_COMMAND_ZOOM_IN, FIV_VIEW_COMMAND_ZOOM_OUT, - FIV_VIEW_COMMAND_ZOOM_1 + FIV_VIEW_COMMAND_ZOOM_1, + FIV_VIEW_COMMAND_TOGGLE_SCALE_TO_FIT, } FivViewCommand; /// Execute a user action.