From f4352524926eda5e69631311d42139796f8edb8d Mon Sep 17 00:00:00 2001 From: Přemysl Eric Janouch
Date: Mon, 20 Dec 2021 11:08:36 +0100
Subject: Add a checkerboard toggle
---
 fastiv.c                            |  13 +++-
 fiv-view.c                          |  38 ++++++++++--
 fiv-view.h                          |   1 +
 resources/checkerboard-symbolic.svg | 115 ++++++++++++++++++++++++++++++++++++
 resources/resources.gresource.xml   |   1 +
 5 files changed, 160 insertions(+), 8 deletions(-)
 create mode 100644 resources/checkerboard-symbolic.svg
diff --git a/fastiv.c b/fastiv.c
index 4013a91..71960dc 100644
--- a/fastiv.c
+++ b/fastiv.c
@@ -56,7 +56,6 @@ exit_fatal(const gchar *format, ...)
 
 // TODO(p): See if it's possible to give separators room to shrink
 // by some minor amount of pixels, margin-wise.
-// 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
@@ -84,6 +83,7 @@ exit_fatal(const gchar *format, ...)
 	/* XX(PIN,        B("view-pin-symbolic", "Keep view configuration")) */ \
 	/* Or perhaps "blur-symbolic", also in the extended set. */ \
 	XX(SMOOTH,        T("blend-tool-symbolic", "Smooth scaling")) \
+	XX(CHECKERBOARD,  T("checkerboard-symbolic", "Highlight transparency")) \
 	/* XX(COLOR,      B("preferences-color-symbolic", "Color management")) */ \
 	XX(SAVE,          B("document-save-as-symbolic", "Save as...")) \
 	XX(PRINT,         B("document-print-symbolic", "Print...")) \
@@ -759,6 +759,7 @@ on_view_actions_changed(void)
 	gtk_widget_set_sensitive(g.toolbar[TOOLBAR_FIT], has_image);
 
 	gtk_widget_set_sensitive(g.toolbar[TOOLBAR_SMOOTH], has_image);
+	gtk_widget_set_sensitive(g.toolbar[TOOLBAR_CHECKERBOARD], has_image);
 	gtk_widget_set_sensitive(g.toolbar[TOOLBAR_SAVE], has_image);
 	gtk_widget_set_sensitive(g.toolbar[TOOLBAR_PRINT], has_image);
 
@@ -902,6 +903,7 @@ make_view_toolbar(void)
 	toolbar_command(TOOLBAR_ONE,           FIV_VIEW_COMMAND_ZOOM_1);
 	toolbar_toggler(TOOLBAR_FIT,           "scale-to-fit");
 	toolbar_toggler(TOOLBAR_SMOOTH,        "filter");
+	toolbar_toggler(TOOLBAR_CHECKERBOARD,  "checkerboard");
 	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);
@@ -917,11 +919,14 @@ make_view_toolbar(void)
 		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_signal_connect(g.view, "notify::checkerboard",
+		G_CALLBACK(on_notify_view_boolean), g.toolbar[TOOLBAR_CHECKERBOARD]);
 
 	g_object_notify(G_OBJECT(g.view), "scale");
 	g_object_notify(G_OBJECT(g.view), "playing");
 	g_object_notify(G_OBJECT(g.view), "scale-to-fit");
 	g_object_notify(G_OBJECT(g.view), "filter");
+	g_object_notify(G_OBJECT(g.view), "checkerboard");
 
 	GCallback callback = G_CALLBACK(on_view_actions_changed);
 	g_signal_connect(g.view, "notify::has-image", callback, NULL);
@@ -995,8 +1000,10 @@ main(int argc, char *argv[])
 		fiv-browser { padding: 5px; } \
 		fiv-browser.item { \
 			color: mix(#000, @content_view_bg, 0.625); margin: 8px; \
-			border: 2px solid #fff; background: @theme_bg_color; \
-			background-image: \
+			border: 2px solid #fff; \
+		} \
+		fiv-browser.item, fiv-view.checkerboard { \
+			background: @theme_bg_color; background-image: \
 				linear-gradient(45deg, @fiv-tile 26%, transparent 26%), \
 				linear-gradient(-45deg, @fiv-tile 26%, transparent 26%), \
 				linear-gradient(45deg, transparent 74%, @fiv-tile 74%), \
diff --git a/fiv-view.c b/fiv-view.c
index f67a464..893c327 100644
--- a/fiv-view.c
+++ b/fiv-view.c
@@ -38,6 +38,7 @@ struct _FivView {
 	cairo_surface_t *frame;             ///< Current frame within page, weak
 	FivIoOrientation orientation;       ///< Current page orientation
 	bool filter;                        ///< Smooth scaling toggle
+	bool checkerboard;                  ///< Show checkerboard background
 	bool scale_to_fit;                  ///< Image no larger than the allocation
 	double scale;                       ///< Scaling factor
 
@@ -88,6 +89,7 @@ enum {
 	PROP_SCALE = 1,
 	PROP_SCALE_TO_FIT,
 	PROP_FILTER,
+	PROP_CHECKERBOARD,
 	PROP_PLAYING,
 	PROP_HAS_IMAGE,
 	PROP_CAN_ANIMATE,
@@ -122,6 +124,9 @@ fiv_view_get_property(
 	case PROP_FILTER:
 		g_value_set_boolean(value, self->filter);
 		break;
+	case PROP_CHECKERBOARD:
+		g_value_set_boolean(value, self->checkerboard);
+		break;
 	case PROP_PLAYING:
 		g_value_set_boolean(value, !!self->frame_update_connection);
 		break;
@@ -158,6 +163,10 @@ fiv_view_set_property(
 		if (self->filter != g_value_get_boolean(value))
 			fiv_view_command(self, FIV_VIEW_COMMAND_TOGGLE_FILTER);
 		break;
+	case PROP_CHECKERBOARD:
+		if (self->checkerboard != g_value_get_boolean(value))
+			fiv_view_command(self, FIV_VIEW_COMMAND_TOGGLE_CHECKERBOARD);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
 	}
@@ -356,8 +365,8 @@ fiv_view_draw(GtkWidget *widget, cairo_t *cr)
 	// which makes the widget have no double buffering or default background.
 	GtkAllocation allocation;
 	gtk_widget_get_allocation(widget, &allocation);
-	gtk_render_background(gtk_widget_get_style_context(widget), cr, 0, 0,
-		allocation.width, allocation.height);
+	GtkStyleContext *style = gtk_widget_get_style_context(widget);
+	gtk_render_background(style, cr, 0, 0, allocation.width, allocation.height);
 
 	FivView *self = FIV_VIEW(widget);
 	if (!self->image ||
@@ -376,10 +385,17 @@ fiv_view_draw(GtkWidget *widget, cairo_t *cr)
 	if (h < allocation.height)
 		y = round((allocation.height - h) / 2.);
 
-	// FIXME: Recording surfaces do not work well with CAIRO_SURFACE_TYPE_XLIB,
-	// we always get a shitty pixmap, where transparency contains junk.
 	cairo_matrix_t matrix = get_orientation_matrix(self->orientation, sw, sh);
 	cairo_translate(cr, x, y);
+	if (self->checkerboard) {
+		gtk_style_context_save(style);
+		gtk_style_context_add_class(style, "checkerboard");
+		gtk_render_background(style, cr, 0, 0, w, h);
+		gtk_style_context_restore(style);
+	}
+
+	// FIXME: Recording surfaces do not work well with CAIRO_SURFACE_TYPE_XLIB,
+	// we always get a shitty pixmap, where transparency contains junk.
 	if (cairo_surface_get_type(self->frame) == CAIRO_SURFACE_TYPE_RECORDING) {
 		cairo_surface_t *image =
 			cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
@@ -804,6 +820,8 @@ fiv_view_key_press_event(GtkWidget *widget, GdkEventKey *event)
 		return command(self, FIV_VIEW_COMMAND_TOGGLE_SCALE_TO_FIT);
 	case GDK_KEY_i:
 		return command(self, FIV_VIEW_COMMAND_TOGGLE_FILTER);
+	case GDK_KEY_t:
+		return command(self, FIV_VIEW_COMMAND_TOGGLE_CHECKERBOARD);
 
 	case GDK_KEY_less:
 		return command(self, FIV_VIEW_COMMAND_ROTATE_LEFT);
@@ -844,6 +862,9 @@ fiv_view_class_init(FivViewClass *klass)
 	view_properties[PROP_FILTER] = g_param_spec_boolean(
 		"filter", "Use filtering", "Scale images smoothly",
 		TRUE, G_PARAM_READWRITE);
+	view_properties[PROP_CHECKERBOARD] = g_param_spec_boolean(
+		"checkerboard", "Show checkerboard", "Highlight transparent background",
+		TRUE, G_PARAM_READWRITE);
 	view_properties[PROP_PLAYING] = g_param_spec_boolean(
 		"playing", "Playing animation", "An animation is running",
 		FALSE, G_PARAM_READABLE);
@@ -885,6 +906,7 @@ fiv_view_init(FivView *self)
 	gtk_widget_set_can_focus(GTK_WIDGET(self), TRUE);
 
 	self->filter = true;
+	self->checkerboard = false;
 	self->scale = 1.0;
 }
 
@@ -975,7 +997,13 @@ fiv_view_command(FivView *self, FivViewCommand command)
 
 	break; case FIV_VIEW_COMMAND_TOGGLE_FILTER:
 		self->filter = !self->filter;
-		g_object_notify_by_pspec(G_OBJECT(self), view_properties[PROP_FILTER]);
+		g_object_notify_by_pspec(
+			G_OBJECT(self), view_properties[PROP_FILTER]);
+		gtk_widget_queue_draw(widget);
+	break; case FIV_VIEW_COMMAND_TOGGLE_CHECKERBOARD:
+		self->checkerboard = !self->checkerboard;
+		g_object_notify_by_pspec(
+			G_OBJECT(self), view_properties[PROP_CHECKERBOARD]);
 		gtk_widget_queue_draw(widget);
 	break; case FIV_VIEW_COMMAND_PRINT:
 		print(self);
diff --git a/fiv-view.h b/fiv-view.h
index 88c8445..c8b3a28 100644
--- a/fiv-view.h
+++ b/fiv-view.h
@@ -42,6 +42,7 @@ typedef enum _FivViewCommand {
 	FIV_VIEW_COMMAND_TOGGLE_PLAYBACK,
 
 	FIV_VIEW_COMMAND_TOGGLE_FILTER,
+	FIV_VIEW_COMMAND_TOGGLE_CHECKERBOARD,
 	FIV_VIEW_COMMAND_PRINT,
 	FIV_VIEW_COMMAND_SAVE_PAGE,
 
diff --git a/resources/checkerboard-symbolic.svg b/resources/checkerboard-symbolic.svg
new file mode 100644
index 0000000..b22ee66
--- /dev/null
+++ b/resources/checkerboard-symbolic.svg
@@ -0,0 +1,115 @@
+
+
diff --git a/resources/resources.gresource.xml b/resources/resources.gresource.xml
index ab85308..1a99827 100644
--- a/resources/resources.gresource.xml
+++ b/resources/resources.gresource.xml
@@ -4,5 +4,6 @@