Enable opening images from the browser
Also, make it possible to go back, in a roughly implemented manner.
This commit is contained in:
		
							parent
							
								
									a0408abdf2
								
							
						
					
					
						commit
						a135d6f332
					
				| @ -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.
 | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
							
								
								
									
										31
									
								
								fastiv.c
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								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(); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user