Add toolbar toggle buttons for scale-to-fit/filter

This commit is contained in:
Přemysl Eric Janouch 2021-12-17 07:39:53 +01:00
parent b78010ccb1
commit e23ed245db
Signed by: p
GPG Key ID: A0420B94F92B9493
3 changed files with 105 additions and 26 deletions

View File

@ -57,6 +57,7 @@ exit_fatal(const gchar *format, ...)
// TODO(p): Add a toggle for a checkerboard background. // TODO(p): Add a toggle for a checkerboard background.
// TODO(p): Implement commented-out actions. // TODO(p): Implement commented-out actions.
#define B make_toolbar_button #define B make_toolbar_button
#define T make_toolbar_toggle
#define TOOLBAR(XX) \ #define TOOLBAR(XX) \
XX(BROWSE, B("view-grid-symbolic", "Browse")) \ XX(BROWSE, B("view-grid-symbolic", "Browse")) \
XX(FILE_PREVIOUS, B("go-previous-symbolic", "Previous file")) \ 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(SCALE, gtk_label_new("100%")) \
XX(MINUS, B("zoom-out-symbolic", "Zoom out")) \ XX(MINUS, B("zoom-out-symbolic", "Zoom out")) \
XX(ONE, B("zoom-original-symbolic", "Original size")) \ 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(S4, make_separator()) \
/* XX(PIN, B("view-pin-symbolic", "Keep view configuration")) */ \ /* XX(PIN, B("view-pin-symbolic", "Keep view configuration")) */ \
/* Or perhaps "blur-symbolic", also in the extended set. */ \ /* 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(COLOR, B("preferences-color-symbolic", "Color management")) */ \
XX(SAVE, B("document-save-as-symbolic", "Save as...")) \ XX(SAVE, B("document-save-as-symbolic", "Save as...")) \
XX(PRINT, B("document-print-symbolic", "Print...")) \ XX(PRINT, B("document-print-symbolic", "Print...")) \
@ -608,6 +609,21 @@ make_toolbar_button(const gchar *symbolic, const gchar *tooltip)
return button; 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 * static GtkWidget *
make_separator(void) make_separator(void)
{ {
@ -620,7 +636,7 @@ make_separator(void)
} }
static void static void
on_notify_scale( on_notify_view_scale(
GObject *object, GParamSpec *param_spec, G_GNUC_UNUSED gpointer user_data) GObject *object, GParamSpec *param_spec, G_GNUC_UNUSED gpointer user_data)
{ {
double scale = 0; double scale = 0;
@ -629,16 +645,38 @@ on_notify_scale(
gchar *scale_str = g_strdup_printf("%.0f%%", round(scale * 100)); gchar *scale_str = g_strdup_printf("%.0f%%", round(scale * 100));
gtk_label_set_text(GTK_LABEL(g.toolbar[TOOLBAR_SCALE]), scale_str); gtk_label_set_text(GTK_LABEL(g.toolbar[TOOLBAR_SCALE]), scale_str);
g_free(scale_str); g_free(scale_str);
// FIXME: The label doesn't immediately assume its new width.
} }
static void 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 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); fiv_view_command(FIV_VIEW(g.view), command);
} }
@ -647,7 +685,15 @@ static void
toolbar_command(int index, FivViewCommand command) toolbar_command(int index, FivViewCommand command)
{ {
g_signal_connect_swapped(g.toolbar[index], "clicked", 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, // 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_PLUS, FIV_VIEW_COMMAND_ZOOM_IN);
toolbar_command(TOOLBAR_MINUS, FIV_VIEW_COMMAND_ZOOM_OUT); toolbar_command(TOOLBAR_MINUS, FIV_VIEW_COMMAND_ZOOM_OUT);
toolbar_command(TOOLBAR_ONE, FIV_VIEW_COMMAND_ZOOM_1); 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_PRINT, FIV_VIEW_COMMAND_PRINT);
toolbar_command(TOOLBAR_SAVE, FIV_VIEW_COMMAND_SAVE_PAGE); toolbar_command(TOOLBAR_SAVE, FIV_VIEW_COMMAND_SAVE_PAGE);
toolbar_command(TOOLBAR_LEFT, FIV_VIEW_COMMAND_ROTATE_LEFT); toolbar_command(TOOLBAR_LEFT, FIV_VIEW_COMMAND_ROTATE_LEFT);
toolbar_command(TOOLBAR_MIRROR, FIV_VIEW_COMMAND_MIRROR); toolbar_command(TOOLBAR_MIRROR, FIV_VIEW_COMMAND_MIRROR);
toolbar_command(TOOLBAR_RIGHT, FIV_VIEW_COMMAND_ROTATE_RIGHT); toolbar_command(TOOLBAR_RIGHT, FIV_VIEW_COMMAND_ROTATE_RIGHT);
toolbar_connect(TOOLBAR_FULLSCREEN, G_CALLBACK(toggle_fullscreen)); 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; return view_toolbar;
} }
@ -786,7 +841,7 @@ main(int argc, char *argv[])
g_signal_connect(g.view, "button-press-event", g_signal_connect(g.view, "button-press-event",
G_CALLBACK(on_button_press_view), NULL); G_CALLBACK(on_button_press_view), NULL);
g_signal_connect(g.view, "notify::scale", 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); gtk_container_add(GTK_CONTAINER(view_scroller), g.view);
// Maybe our custom widgets should derive colours from the theme instead. // Maybe our custom widgets should derive colours from the theme instead.

View File

@ -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 static void
get_surface_dimensions(FivView *self, double *width, double *height) get_surface_dimensions(FivView *self, double *width, double *height)
{ {
@ -396,16 +415,17 @@ fiv_view_button_press_event(GtkWidget *widget, GdkEventButton *event)
return FALSE; return FALSE;
} }
#define SCALE_STEP 1.4 #define SCALE_STEP 1.25
static gboolean static gboolean
set_scale_to_fit(FivView *self, bool scale_to_fit) set_scale_to_fit(FivView *self, bool scale_to_fit)
{ {
if (self->scale_to_fit != scale_to_fit) {
self->scale_to_fit = scale_to_fit; self->scale_to_fit = scale_to_fit;
gtk_widget_queue_resize(GTK_WIDGET(self));
g_object_notify_by_pspec( g_object_notify_by_pspec(
G_OBJECT(self), view_properties[PROP_SCALE_TO_FIT]); G_OBJECT(self), view_properties[PROP_SCALE_TO_FIT]);
gtk_widget_queue_resize(GTK_WIDGET(self));
}
return TRUE; return TRUE;
} }
@ -413,8 +433,8 @@ static gboolean
set_scale(FivView *self, double scale) set_scale(FivView *self, double scale)
{ {
self->scale = scale; self->scale = scale;
g_object_notify_by_pspec( g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_SCALE]);
G_OBJECT(self), view_properties[PROP_SCALE]); gtk_widget_queue_resize(GTK_WIDGET(self));
return set_scale_to_fit(self, false); 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); return command(self, FIV_VIEW_COMMAND_ZOOM_OUT);
case GDK_KEY_x: // Inspired by gThumb. 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: case GDK_KEY_i:
self->filter = !self->filter; return command(self, FIV_VIEW_COMMAND_TOGGLE_FILTER);
g_object_notify_by_pspec(
G_OBJECT(self), view_properties[PROP_FILTER]);
gtk_widget_queue_draw(widget);
return TRUE;
case GDK_KEY_less: case GDK_KEY_less:
return command(self, FIV_VIEW_COMMAND_ROTATE_LEFT); 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); GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->finalize = fiv_view_finalize; object_class->finalize = fiv_view_finalize;
object_class->get_property = fiv_view_get_property; object_class->get_property = fiv_view_get_property;
object_class->set_property = fiv_view_set_property;
view_properties[PROP_SCALE] = g_param_spec_double( view_properties[PROP_SCALE] = g_param_spec_double(
"scale", "Scale", "Zoom level", "scale", "Scale", "Zoom level",
0, G_MAXDOUBLE, 1.0, G_PARAM_READABLE); 0, G_MAXDOUBLE, 1.0, G_PARAM_READABLE);
view_properties[PROP_SCALE_TO_FIT] = g_param_spec_boolean( view_properties[PROP_SCALE_TO_FIT] = g_param_spec_boolean(
"scale-to-fit", "Scale to fit", "Scale images down to fit the window", "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( view_properties[PROP_FILTER] = g_param_spec_boolean(
"filter", "Use filtering", "Scale images smoothly", "filter", "Use filtering", "Scale images smoothly",
TRUE, G_PARAM_READABLE); TRUE, G_PARAM_READWRITE);
g_object_class_install_properties( g_object_class_install_properties(
object_class, N_PROPERTIES, view_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: break; case FIV_VIEW_COMMAND_FRAME_NEXT:
frame_step(self, +1); 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: break; case FIV_VIEW_COMMAND_PRINT:
print(self); print(self);
break; case FIV_VIEW_COMMAND_SAVE_PAGE: 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); set_scale(self, self->scale / SCALE_STEP);
break; case FIV_VIEW_COMMAND_ZOOM_1: break; case FIV_VIEW_COMMAND_ZOOM_1:
set_scale(self, 1.0); set_scale(self, 1.0);
break; case FIV_VIEW_COMMAND_TOGGLE_SCALE_TO_FIT:
set_scale_to_fit(self, !self->scale_to_fit);
} }
} }

View File

@ -40,12 +40,14 @@ typedef enum _FivViewCommand {
FIV_VIEW_COMMAND_FRAME_NEXT, FIV_VIEW_COMMAND_FRAME_NEXT,
// Going to the end frame makes no sense, wrap around if needed. // Going to the end frame makes no sense, wrap around if needed.
FIV_VIEW_COMMAND_TOGGLE_FILTER,
FIV_VIEW_COMMAND_PRINT, FIV_VIEW_COMMAND_PRINT,
FIV_VIEW_COMMAND_SAVE_PAGE, FIV_VIEW_COMMAND_SAVE_PAGE,
FIV_VIEW_COMMAND_ZOOM_IN, FIV_VIEW_COMMAND_ZOOM_IN,
FIV_VIEW_COMMAND_ZOOM_OUT, FIV_VIEW_COMMAND_ZOOM_OUT,
FIV_VIEW_COMMAND_ZOOM_1 FIV_VIEW_COMMAND_ZOOM_1,
FIV_VIEW_COMMAND_TOGGLE_SCALE_TO_FIT,
} FivViewCommand; } FivViewCommand;
/// Execute a user action. /// Execute a user action.