Enable opening images from the browser

Also, make it possible to go back, in a roughly implemented manner.
This commit is contained in:
Přemysl Eric Janouch 2021-11-08 08:00:18 +01:00
parent a0408abdf2
commit a135d6f332
Signed by: p
GPG Key ID: A0420B94F92B9493
3 changed files with 85 additions and 18 deletions

View File

@ -200,26 +200,52 @@ draw_item_border(FastivBrowser *self, cairo_t *cr, int width, int height)
cairo_pattern_destroy(mask); 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 static void
draw_row(FastivBrowser *self, cairo_t *cr, const Row *row) draw_row(FastivBrowser *self, cairo_t *cr, const Row *row)
{ {
for (Item *item = row->items; item->entry; item++) { for (Item *item = row->items; item->entry; item++) {
cairo_surface_t *thumbnail = item->entry->thumbnail; GdkRectangle extents = item_extents(item, row);
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;
cairo_save(cr); cairo_save(cr);
cairo_translate(cr, x, y); cairo_translate(cr, extents.x, extents.y);
draw_item_border(self, cr, width, height); draw_item_border(self, cr, extents.width, extents.height);
// TODO(p): See if a mild checkerboard pattern would not look nice. // 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_set_source_rgb(cr, .25, .25, .25);
cairo_fill(cr); 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_paint(cr);
cairo_restore(cr); cairo_restore(cr);
} }
@ -345,6 +371,22 @@ fastiv_browser_draw(GtkWidget *widget, cairo_t *cr)
return TRUE; 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 static void
fastiv_browser_class_init(FastivBrowserClass *klass) fastiv_browser_class_init(FastivBrowserClass *klass)
{ {
@ -359,10 +401,11 @@ fastiv_browser_class_init(FastivBrowserClass *klass)
widget_class->realize = fastiv_browser_realize; widget_class->realize = fastiv_browser_realize;
widget_class->draw = fastiv_browser_draw; widget_class->draw = fastiv_browser_draw;
widget_class->size_allocate = fastiv_browser_size_allocate; 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] =
browser_signals[ITEM_ACTIVATED] = g_signal_new("item-activated", g_signal_new("item-activated", G_TYPE_FROM_CLASS(klass), 0, 0,
G_TYPE_FROM_CLASS(klass), 0, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING);
// TODO(p): Later override "screen_changed", recreate Pango layouts there, // TODO(p): Later override "screen_changed", recreate Pango layouts there,
// if we get to have any, or otherwise reflect DPI changes. // if we get to have any, or otherwise reflect DPI changes.

View File

@ -131,6 +131,11 @@ fastiv_view_draw(GtkWidget *widget, cairo_t *cr)
if (h < allocation.height) if (h < allocation.height)
y = round((allocation.height - h) / 2.); 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_scale(cr, self->scale, self->scale);
cairo_set_source_surface( cairo_set_source_surface(
cr, self->surface, x / self->scale, y / self->scale); cr, self->surface, x / self->scale, y / self->scale);

View File

@ -61,6 +61,7 @@ struct {
gchar *basename; gchar *basename;
GtkWidget *window; GtkWidget *window;
GtkWidget *stack;
GtkWidget *view; GtkWidget *view;
GtkWidget *browser; GtkWidget *browser;
GtkWidget *browser_scroller; GtkWidget *browser_scroller;
@ -148,6 +149,7 @@ open(const gchar *path)
} }
gtk_window_set_title(GTK_WINDOW(g.window), 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); gchar *basename = g_path_get_basename(path);
g_free(g.basename); 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 // Cursor keys, e.g., simply cannot be bound through accelerators
// (and GtkWidget::keynav-failed would arguably be an awful solution). // (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: case GDK_KEY_space:
on_next(); on_next();
return TRUE; 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; return FALSE;
@ -318,15 +335,17 @@ main(int argc, char *argv[])
g.browser = g_object_new(FASTIV_TYPE_BROWSER, NULL); g.browser = g_object_new(FASTIV_TYPE_BROWSER, NULL);
gtk_widget_set_vexpand(g.browser, TRUE); gtk_widget_set_vexpand(g.browser, TRUE);
gtk_widget_set_hexpand(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); gtk_container_add(GTK_CONTAINER(g.browser_scroller), g.browser);
// TODO(p): Can we not do it here separately? // TODO(p): Can we not do it here separately?
gtk_widget_show_all(g.browser_scroller); gtk_widget_show_all(g.browser_scroller);
GtkWidget *stack = gtk_stack_new(); g.stack = gtk_stack_new();
gtk_stack_set_transition_type( gtk_stack_set_transition_type(
GTK_STACK(stack), GTK_STACK_TRANSITION_TYPE_NONE); GTK_STACK(g.stack), GTK_STACK_TRANSITION_TYPE_NONE);
gtk_container_add(GTK_CONTAINER(stack), g.view); gtk_container_add(GTK_CONTAINER(g.stack), g.view);
gtk_container_add(GTK_CONTAINER(stack), g.browser_scroller); gtk_container_add(GTK_CONTAINER(g.stack), g.browser_scroller);
g.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); g.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (g.window), 800, 600); 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_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(g.window, "key-press-event", g_signal_connect(g.window, "key-press-event",
G_CALLBACK(on_key_press), NULL); 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.supported_globs = extract_mime_globs(fastiv_io_supported_media_types);
g.files = g_ptr_array_new_full(16, g_free); g.files = g_ptr_array_new_full(16, g_free);
@ -359,7 +378,7 @@ main(int argc, char *argv[])
g_free(cwd); g_free(cwd);
if (g.files_index < 0) 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_widget_show_all(g.window);
gtk_main(); gtk_main();