From a135d6f332c31b11b8d4c4b3c3cfb0b500beffb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?= Date: Mon, 8 Nov 2021 08:00:18 +0100 Subject: [PATCH] Enable opening images from the browser Also, make it possible to go back, in a roughly implemented manner. --- fastiv-browser.c | 67 +++++++++++++++++++++++++++++++++++++++--------- fastiv-view.c | 5 ++++ fastiv.c | 31 +++++++++++++++++----- 3 files changed, 85 insertions(+), 18 deletions(-) diff --git a/fastiv-browser.c b/fastiv-browser.c index 5d1dcff..4c2b5dd 100644 --- a/fastiv-browser.c +++ b/fastiv-browser.c @@ -200,26 +200,52 @@ draw_item_border(FastivBrowser *self, cairo_t *cr, int width, int height) cairo_pattern_destroy(mask); } +static GdkRectangle +item_extents(const Item *item, const Row *row) +{ + int width = cairo_image_surface_get_width(item->entry->thumbnail); + int height = cairo_image_surface_get_height(item->entry->thumbnail); + return (GdkRectangle) { + .x = row->x_offset + item->x_offset, + .y = row->y_offset + g_row_height - height, + .width = width, + .height = height, + }; +} + +static const Entry * +entry_at(FastivBrowser *self, int x, int y) +{ + for (guint i = 0; i < self->layouted_rows->len; i++) { + const Row *row = &g_array_index(self->layouted_rows, Row, i); + for (Item *item = row->items; item->entry; item++) { + GdkRectangle extents = item_extents(item, row); + if (x >= extents.x && + y >= extents.y && + x <= extents.x + extents.width && + y <= extents.y + extents.height) + return item->entry; + } + } + return NULL; +} + static void draw_row(FastivBrowser *self, cairo_t *cr, const Row *row) { for (Item *item = row->items; item->entry; item++) { - cairo_surface_t *thumbnail = item->entry->thumbnail; - int width = cairo_image_surface_get_width(thumbnail); - int height = cairo_image_surface_get_height(thumbnail); - int x = row->x_offset + item->x_offset; - int y = row->y_offset + g_row_height - height; + GdkRectangle extents = item_extents(item, row); cairo_save(cr); - cairo_translate(cr, x, y); - draw_item_border(self, cr, width, height); + cairo_translate(cr, extents.x, extents.y); + draw_item_border(self, cr, extents.width, extents.height); // TODO(p): See if a mild checkerboard pattern would not look nice. - cairo_rectangle(cr, 0, 0, width, height); + cairo_rectangle(cr, 0, 0, extents.width, extents.height); cairo_set_source_rgb(cr, .25, .25, .25); cairo_fill(cr); - cairo_set_source_surface(cr, thumbnail, 0, 0); + cairo_set_source_surface(cr, item->entry->thumbnail, 0, 0); cairo_paint(cr); cairo_restore(cr); } @@ -345,6 +371,22 @@ fastiv_browser_draw(GtkWidget *widget, cairo_t *cr) return TRUE; } +static gboolean +fastiv_browser_button_press_event(GtkWidget *widget, GdkEventButton *event) +{ + FastivBrowser *self = FASTIV_BROWSER(widget); + if (event->type != GDK_BUTTON_PRESS || event->button != 1 || + event->state != 0) + return FALSE; + + const Entry *entry = entry_at(self, event->x, event->y); + if (!entry) + return FALSE; + + g_signal_emit(widget, browser_signals[ITEM_ACTIVATED], 0, entry->filename); + return TRUE; +} + static void fastiv_browser_class_init(FastivBrowserClass *klass) { @@ -359,10 +401,11 @@ fastiv_browser_class_init(FastivBrowserClass *klass) widget_class->realize = fastiv_browser_realize; widget_class->draw = fastiv_browser_draw; widget_class->size_allocate = fastiv_browser_size_allocate; + widget_class->button_press_event = fastiv_browser_button_press_event; - // TODO(p): Connect to this and emit it. - browser_signals[ITEM_ACTIVATED] = g_signal_new("item-activated", - G_TYPE_FROM_CLASS(klass), 0, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + browser_signals[ITEM_ACTIVATED] = + g_signal_new("item-activated", G_TYPE_FROM_CLASS(klass), 0, 0, + NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); // TODO(p): Later override "screen_changed", recreate Pango layouts there, // if we get to have any, or otherwise reflect DPI changes. diff --git a/fastiv-view.c b/fastiv-view.c index 1cf4f17..037008d 100644 --- a/fastiv-view.c +++ b/fastiv-view.c @@ -131,6 +131,11 @@ fastiv_view_draw(GtkWidget *widget, cairo_t *cr) if (h < allocation.height) y = round((allocation.height - h) / 2.); + // XXX: The rounding together with padding may result in up to + // a pixel's worth of made-up picture data. + cairo_rectangle(cr, x, y, w, h); + cairo_clip(cr); + cairo_scale(cr, self->scale, self->scale); cairo_set_source_surface( cr, self->surface, x / self->scale, y / self->scale); diff --git a/fastiv.c b/fastiv.c index c02c723..5af7324 100644 --- a/fastiv.c +++ b/fastiv.c @@ -61,6 +61,7 @@ struct { gchar *basename; GtkWidget *window; + GtkWidget *stack; GtkWidget *view; GtkWidget *browser; GtkWidget *browser_scroller; @@ -148,6 +149,7 @@ open(const gchar *path) } gtk_window_set_title(GTK_WINDOW(g.window), path); + gtk_stack_set_visible_child(GTK_STACK(g.stack), g.view); gchar *basename = g_path_get_basename(path); g_free(g.basename); @@ -222,6 +224,13 @@ on_next(void) } } +static void +on_item_activated(G_GNUC_UNUSED FastivBrowser *browser, const char *path, + G_GNUC_UNUSED gpointer data) +{ + open(path); +} + // Cursor keys, e.g., simply cannot be bound through accelerators // (and GtkWidget::keynav-failed would arguably be an awful solution). // @@ -268,6 +277,14 @@ on_key_press(G_GNUC_UNUSED GtkWidget *widget, GdkEvent *event, case GDK_KEY_space: on_next(); return TRUE; + + case GDK_KEY_Tab: + case GDK_KEY_Return: + gtk_stack_set_visible_child(GTK_STACK(g.stack), + gtk_stack_get_visible_child(GTK_STACK(g.stack)) == g.view + ? g.browser_scroller + : g.view); + return TRUE; } } return FALSE; @@ -318,15 +335,17 @@ main(int argc, char *argv[]) g.browser = g_object_new(FASTIV_TYPE_BROWSER, NULL); gtk_widget_set_vexpand(g.browser, TRUE); gtk_widget_set_hexpand(g.browser, TRUE); + g_signal_connect(g.browser, "item-activated", + G_CALLBACK(on_item_activated), NULL); gtk_container_add(GTK_CONTAINER(g.browser_scroller), g.browser); // TODO(p): Can we not do it here separately? gtk_widget_show_all(g.browser_scroller); - GtkWidget *stack = gtk_stack_new(); + g.stack = gtk_stack_new(); gtk_stack_set_transition_type( - GTK_STACK(stack), GTK_STACK_TRANSITION_TYPE_NONE); - gtk_container_add(GTK_CONTAINER(stack), g.view); - gtk_container_add(GTK_CONTAINER(stack), g.browser_scroller); + GTK_STACK(g.stack), GTK_STACK_TRANSITION_TYPE_NONE); + gtk_container_add(GTK_CONTAINER(g.stack), g.view); + gtk_container_add(GTK_CONTAINER(g.stack), g.browser_scroller); g.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size (GTK_WINDOW (g.window), 800, 600); @@ -334,7 +353,7 @@ main(int argc, char *argv[]) G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(g.window, "key-press-event", G_CALLBACK(on_key_press), NULL); - gtk_container_add(GTK_CONTAINER(g.window), stack); + gtk_container_add(GTK_CONTAINER(g.window), g.stack); g.supported_globs = extract_mime_globs(fastiv_io_supported_media_types); g.files = g_ptr_array_new_full(16, g_free); @@ -359,7 +378,7 @@ main(int argc, char *argv[]) g_free(cwd); if (g.files_index < 0) - gtk_stack_set_visible_child(GTK_STACK(stack), g.browser_scroller); + gtk_stack_set_visible_child(GTK_STACK(g.stack), g.browser_scroller); gtk_widget_show_all(g.window); gtk_main();