Implement selection in the browser
Keyboard controls are missing so far.
This commit is contained in:
		
							parent
							
								
									1a190001fc
								
							
						
					
					
						commit
						e663f02754
					
				| @ -45,7 +45,7 @@ typedef struct entry Entry; | |||||||
| typedef struct item Item; | typedef struct item Item; | ||||||
| typedef struct row Row; | typedef struct row Row; | ||||||
| 
 | 
 | ||||||
| typedef struct _Thumbnailer { | typedef struct { | ||||||
| 	FivBrowser *self;                   ///< Parent browser
 | 	FivBrowser *self;                   ///< Parent browser
 | ||||||
| 	Entry *target;                      ///< Currently processed Entry pointer
 | 	Entry *target;                      ///< Currently processed Entry pointer
 | ||||||
| 	GSubprocess *minion;                ///< A slave for the current queue head
 | 	GSubprocess *minion;                ///< A slave for the current queue head
 | ||||||
| @ -66,7 +66,7 @@ struct _FivBrowser { | |||||||
| 	FivIoModel *model;                  ///< Filesystem model
 | 	FivIoModel *model;                  ///< Filesystem model
 | ||||||
| 	GArray *entries;                    ///< []Entry
 | 	GArray *entries;                    ///< []Entry
 | ||||||
| 	GArray *layouted_rows;              ///< []Row
 | 	GArray *layouted_rows;              ///< []Row
 | ||||||
| 	int selected; | 	const Entry *selected;              ///< Selected entry or NULL
 | ||||||
| 
 | 
 | ||||||
| 	Thumbnailer *thumbnailers;          ///< Parallelized thumbnailers
 | 	Thumbnailer *thumbnailers;          ///< Parallelized thumbnailers
 | ||||||
| 	size_t thumbnailers_len;            ///< Thumbnailers array size
 | 	size_t thumbnailers_len;            ///< Thumbnailers array size
 | ||||||
| @ -244,7 +244,7 @@ static const Entry * | |||||||
| entry_at(FivBrowser *self, int x, int y) | entry_at(FivBrowser *self, int x, int y) | ||||||
| { | { | ||||||
| 	if (self->vadjustment) | 	if (self->vadjustment) | ||||||
| 		y += gtk_adjustment_get_value(self->vadjustment); | 		y += round(gtk_adjustment_get_value(self->vadjustment)); | ||||||
| 
 | 
 | ||||||
| 	for (guint i = 0; i < self->layouted_rows->len; i++) { | 	for (guint i = 0; i < self->layouted_rows->len; i++) { | ||||||
| 		const Row *row = &g_array_index(self->layouted_rows, Row, i); | 		const Row *row = &g_array_index(self->layouted_rows, Row, i); | ||||||
| @ -267,21 +267,25 @@ draw_row(FivBrowser *self, cairo_t *cr, const Row *row) | |||||||
| 	gtk_style_context_save(style); | 	gtk_style_context_save(style); | ||||||
| 	gtk_style_context_add_class(style, "item"); | 	gtk_style_context_add_class(style, "item"); | ||||||
| 
 | 
 | ||||||
| 	GdkRGBA glow_color = {}; |  | ||||||
| 	GtkStateFlags state = gtk_style_context_get_state (style); |  | ||||||
| 	gtk_style_context_get_color(style, state, &glow_color); |  | ||||||
| 
 |  | ||||||
| 	GtkBorder border; | 	GtkBorder border; | ||||||
| 	gtk_style_context_get_border(style, state, &border); | 	GtkStateFlags common_state = gtk_style_context_get_state(style); | ||||||
|  | 	gtk_style_context_get_border(style, common_state, &border); | ||||||
| 	for (Item *item = row->items; item->entry; item++) { | 	for (Item *item = row->items; item->entry; item++) { | ||||||
| 		cairo_save(cr); | 		cairo_save(cr); | ||||||
| 		GdkRectangle extents = item_extents(self, item, row); | 		GdkRectangle extents = item_extents(self, item, row); | ||||||
| 		cairo_translate(cr, extents.x - border.left, extents.y - border.top); | 		cairo_translate(cr, extents.x - border.left, extents.y - border.top); | ||||||
| 
 | 
 | ||||||
|  | 		GtkStateFlags state = common_state; | ||||||
|  | 		if (item->entry == self->selected) | ||||||
|  | 			state |= GTK_STATE_FLAG_SELECTED; | ||||||
|  | 
 | ||||||
| 		gtk_style_context_save(style); | 		gtk_style_context_save(style); | ||||||
|  | 		gtk_style_context_set_state(style, state); | ||||||
| 		if (item->entry->icon) { | 		if (item->entry->icon) { | ||||||
| 			gtk_style_context_add_class(style, "symbolic"); | 			gtk_style_context_add_class(style, "symbolic"); | ||||||
| 		} else { | 		} else { | ||||||
|  | 			GdkRGBA glow_color = {}; | ||||||
|  | 			gtk_style_context_get_color(style, state, &glow_color); | ||||||
| 			gdk_cairo_set_source_rgba(cr, &glow_color); | 			gdk_cairo_set_source_rgba(cr, &glow_color); | ||||||
| 			draw_outer_border(self, cr, | 			draw_outer_border(self, cr, | ||||||
| 				border.left + extents.width + border.right, | 				border.left + extents.width + border.right, | ||||||
| @ -309,6 +313,9 @@ draw_row(FivBrowser *self, cairo_t *cr, const Row *row) | |||||||
| 			cairo_set_source_surface( | 			cairo_set_source_surface( | ||||||
| 				cr, item->entry->thumbnail, border.left, border.top); | 				cr, item->entry->thumbnail, border.left, border.top); | ||||||
| 			cairo_paint(cr); | 			cairo_paint(cr); | ||||||
|  | 
 | ||||||
|  | 			// Here, we could consider multiplying
 | ||||||
|  | 			// the whole rectangle with the selection color.
 | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		cairo_restore(cr); | 		cairo_restore(cr); | ||||||
| @ -1010,7 +1017,7 @@ fiv_browser_draw(GtkWidget *widget, cairo_t *cr) | |||||||
| 
 | 
 | ||||||
| 	// TODO(p): self->hadjustment as well, and test it.
 | 	// TODO(p): self->hadjustment as well, and test it.
 | ||||||
| 	if (self->vadjustment) { | 	if (self->vadjustment) { | ||||||
| 		gdouble y = gtk_adjustment_get_value(self->vadjustment); | 		gdouble y = round(gtk_adjustment_get_value(self->vadjustment)); | ||||||
| 		cairo_translate(cr, 0, -y); | 		cairo_translate(cr, 0, -y); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -1057,8 +1064,21 @@ fiv_browser_button_press_event(GtkWidget *widget, GdkEventButton *event) | |||||||
| 		gtk_widget_grab_focus(widget); | 		gtk_widget_grab_focus(widget); | ||||||
| 
 | 
 | ||||||
| 	const Entry *entry = entry_at(self, event->x, event->y); | 	const Entry *entry = entry_at(self, event->x, event->y); | ||||||
| 	if (!entry && event->button == GDK_BUTTON_SECONDARY) { | 	if (!entry && state == 0) { | ||||||
|  | 		switch (event->button) { | ||||||
|  | 		case GDK_BUTTON_PRIMARY: | ||||||
|  | 			break; | ||||||
|  | 		case GDK_BUTTON_SECONDARY: | ||||||
| 			show_context_menu(widget, fiv_io_model_get_location(self->model)); | 			show_context_menu(widget, fiv_io_model_get_location(self->model)); | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 			return FALSE; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (self->selected) { | ||||||
|  | 			self->selected = NULL; | ||||||
|  | 			gtk_widget_queue_draw(widget); | ||||||
|  | 		} | ||||||
| 		return TRUE; | 		return TRUE; | ||||||
| 	} | 	} | ||||||
| 	if (!entry) | 	if (!entry) | ||||||
| @ -1076,6 +1096,9 @@ fiv_browser_button_press_event(GtkWidget *widget, GdkEventButton *event) | |||||||
| 			return open_entry(widget, entry, TRUE); | 			return open_entry(widget, entry, TRUE); | ||||||
| 		return FALSE; | 		return FALSE; | ||||||
| 	case GDK_BUTTON_SECONDARY: | 	case GDK_BUTTON_SECONDARY: | ||||||
|  | 		self->selected = entry; | ||||||
|  | 		gtk_widget_queue_draw(widget); | ||||||
|  | 
 | ||||||
| 		// On X11, after closing the menu, the pointer otherwise remains,
 | 		// On X11, after closing the menu, the pointer otherwise remains,
 | ||||||
| 		// no matter what its new location is.
 | 		// no matter what its new location is.
 | ||||||
| 		gdk_window_set_cursor(gtk_widget_get_window(widget), NULL); | 		gdk_window_set_cursor(gtk_widget_get_window(widget), NULL); | ||||||
| @ -1127,6 +1150,18 @@ fiv_browser_scroll_event(GtkWidget *widget, GdkEventScroll *event) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static gboolean | ||||||
|  | fiv_browser_key_press_event(GtkWidget *widget, GdkEventKey *event) | ||||||
|  | { | ||||||
|  | 	FivBrowser *self = FIV_BROWSER(widget); | ||||||
|  | 	if (!(event->state & gtk_accelerator_get_default_mod_mask()) && | ||||||
|  | 		event->keyval == GDK_KEY_Return && self->selected) | ||||||
|  | 		return open_entry(widget, self->selected, FALSE); | ||||||
|  | 
 | ||||||
|  | 	return GTK_WIDGET_CLASS(fiv_browser_parent_class) | ||||||
|  | 		->key_press_event(widget, event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static gboolean | static gboolean | ||||||
| fiv_browser_query_tooltip(GtkWidget *widget, gint x, gint y, | fiv_browser_query_tooltip(GtkWidget *widget, gint x, gint y, | ||||||
| 	G_GNUC_UNUSED gboolean keyboard_tooltip, GtkTooltip *tooltip) | 	G_GNUC_UNUSED gboolean keyboard_tooltip, GtkTooltip *tooltip) | ||||||
| @ -1253,6 +1288,7 @@ fiv_browser_class_init(FivBrowserClass *klass) | |||||||
| 	widget_class->button_press_event = fiv_browser_button_press_event; | 	widget_class->button_press_event = fiv_browser_button_press_event; | ||||||
| 	widget_class->motion_notify_event = fiv_browser_motion_notify_event; | 	widget_class->motion_notify_event = fiv_browser_motion_notify_event; | ||||||
| 	widget_class->scroll_event = fiv_browser_scroll_event; | 	widget_class->scroll_event = fiv_browser_scroll_event; | ||||||
|  | 	widget_class->key_press_event = fiv_browser_key_press_event; | ||||||
| 	widget_class->query_tooltip = fiv_browser_query_tooltip; | 	widget_class->query_tooltip = fiv_browser_query_tooltip; | ||||||
| 	widget_class->style_updated = fiv_browser_style_updated; | 	widget_class->style_updated = fiv_browser_style_updated; | ||||||
| 
 | 
 | ||||||
| @ -1277,7 +1313,6 @@ fiv_browser_init(FivBrowser *self) | |||||||
| 	g_array_set_clear_func(self->entries, (GDestroyNotify) entry_free); | 	g_array_set_clear_func(self->entries, (GDestroyNotify) entry_free); | ||||||
| 	self->layouted_rows = g_array_new(FALSE, TRUE, sizeof(Row)); | 	self->layouted_rows = g_array_new(FALSE, TRUE, sizeof(Row)); | ||||||
| 	g_array_set_clear_func(self->layouted_rows, (GDestroyNotify) row_free); | 	g_array_set_clear_func(self->layouted_rows, (GDestroyNotify) row_free); | ||||||
| 	self->selected = -1; |  | ||||||
| 
 | 
 | ||||||
| 	self->thumbnailers_len = g_get_num_processors(); | 	self->thumbnailers_len = g_get_num_processors(); | ||||||
| 	self->thumbnailers = | 	self->thumbnailers = | ||||||
| @ -1299,6 +1334,13 @@ on_model_files_changed(FivIoModel *model, FivBrowser *self) | |||||||
| { | { | ||||||
| 	g_return_if_fail(model == self->model); | 	g_return_if_fail(model == self->model); | ||||||
| 
 | 
 | ||||||
|  | 	int selected = -1; | ||||||
|  | 	gchar *selected_uri = NULL; | ||||||
|  | 	if (self->selected) { | ||||||
|  | 		selected_uri = g_strdup(self->selected->uri); | ||||||
|  | 		self->selected = NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// TODO(p): Later implement arguments.
 | 	// TODO(p): Later implement arguments.
 | ||||||
| 	thumbnailers_abort(self); | 	thumbnailers_abort(self); | ||||||
| 	g_array_set_size(self->entries, 0); | 	g_array_set_size(self->entries, 0); | ||||||
| @ -1308,10 +1350,17 @@ on_model_files_changed(FivIoModel *model, FivBrowser *self) | |||||||
| 	for (guint i = 0; i < files->len; i++) { | 	for (guint i = 0; i < files->len; i++) { | ||||||
| 		g_array_append_val(self->entries, | 		g_array_append_val(self->entries, | ||||||
| 			((Entry) {.thumbnail = NULL, .uri = files->pdata[i]})); | 			((Entry) {.thumbnail = NULL, .uri = files->pdata[i]})); | ||||||
|  | 		if (!g_strcmp0(selected_uri, files->pdata[i])) | ||||||
|  | 			selected = i; | ||||||
| 		files->pdata[i] = NULL; | 		files->pdata[i] = NULL; | ||||||
| 	} | 	} | ||||||
| 	g_ptr_array_free(files, TRUE); | 	g_ptr_array_free(files, TRUE); | ||||||
| 
 | 
 | ||||||
|  | 	// Beware that the pointer may shift with the storage.
 | ||||||
|  | 	g_free(selected_uri); | ||||||
|  | 	if (selected >= 0) | ||||||
|  | 		self->selected = &g_array_index(self->entries, Entry, selected); | ||||||
|  | 
 | ||||||
| 	reload_thumbnails(self); | 	reload_thumbnails(self); | ||||||
| 	thumbnailers_start(self); | 	thumbnailers_start(self); | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										31
									
								
								fiv.c
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								fiv.c
									
									
									
									
									
								
							| @ -567,7 +567,7 @@ switch_to_browser(void) | |||||||
| { | { | ||||||
| 	set_window_title(g.directory); | 	set_window_title(g.directory); | ||||||
| 	gtk_stack_set_visible_child(GTK_STACK(g.stack), g.browser_paned); | 	gtk_stack_set_visible_child(GTK_STACK(g.stack), g.browser_paned); | ||||||
| 	gtk_widget_grab_focus(g.browser_scroller); | 	gtk_widget_grab_focus(g.browser); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| @ -1567,6 +1567,8 @@ make_browser_sidebar(FivIoModel *model) | |||||||
| // thus resolving the problem using overlaps.
 | // thus resolving the problem using overlaps.
 | ||||||
| // We're trying to be universal for light and dark themes both. It's hard.
 | // We're trying to be universal for light and dark themes both. It's hard.
 | ||||||
| static const char stylesheet[] = "@define-color fiv-tile @content_view_bg; \
 | static const char stylesheet[] = "@define-color fiv-tile @content_view_bg; \
 | ||||||
|  | 	@define-color fiv-semiselected \ | ||||||
|  | 		mix(@theme_selected_bg_color, @content_view_bg, 0.5); \ | ||||||
| 	fiv-view, fiv-browser { background: @content_view_bg; } \ | 	fiv-view, fiv-browser { background: @content_view_bg; } \ | ||||||
| 	placessidebar.fiv .toolbar { padding: 2px 6px; } \ | 	placessidebar.fiv .toolbar { padding: 2px 6px; } \ | ||||||
| 	placessidebar.fiv box > separator { margin: 4px 0; } \ | 	placessidebar.fiv box > separator { margin: 4px 0; } \ | ||||||
| @ -1579,6 +1581,7 @@ static const char stylesheet[] = "@define-color fiv-tile @content_view_bg; \ | |||||||
| 	} \ | 	} \ | ||||||
| 	fiv-browser { padding: 5px; } \ | 	fiv-browser { padding: 5px; } \ | ||||||
| 	fiv-browser.item { \ | 	fiv-browser.item { \ | ||||||
|  | 		/* For non-symbolic, color is applied to the glowing margin. */ \ | ||||||
| 		color: mix(#000, @content_view_bg, 0.625); margin: 8px; \ | 		color: mix(#000, @content_view_bg, 0.625); margin: 8px; \ | ||||||
| 		border: 2px solid #fff; \ | 		border: 2px solid #fff; \ | ||||||
| 	} \ | 	} \ | ||||||
| @ -1591,14 +1594,36 @@ static const char stylesheet[] = "@define-color fiv-tile @content_view_bg; \ | |||||||
| 		background-size: 40px 40px; \ | 		background-size: 40px 40px; \ | ||||||
| 		background-position: 0 0, 0 20px, 20px -20px, -20px 0px; \ | 		background-position: 0 0, 0 20px, 20px -20px, -20px 0px; \ | ||||||
| 	} \ | 	} \ | ||||||
| 	fiv-browser.item:backdrop { \ | 	fiv-browser.item:selected { \ | ||||||
|  | 		color: @theme_selected_bg_color; \ | ||||||
|  | 		border-color: @theme_selected_bg_color; \ | ||||||
|  | 	} \ | ||||||
|  | 	fiv-browser.item:selected:not(:focus) { \ | ||||||
|  | 		color: @fiv-semiselected; \ | ||||||
|  | 		border-color: @fiv-semiselected; \ | ||||||
|  | 	} \ | ||||||
|  | 	fiv-browser.item:backdrop:not(:selected) { \ | ||||||
| 		color: mix(#000, @content_view_bg, 0.875); \ | 		color: mix(#000, @content_view_bg, 0.875); \ | ||||||
| 		border-color: mix(#fff, @content_view_bg, 0.5); \ | 		border-color: mix(#fff, @content_view_bg, 0.5); \ | ||||||
| 	} \ | 	} \ | ||||||
|  | 	fiv-browser.item.symbolic, \ | ||||||
|  | 	fiv-browser.item.symbolic:selected, \ | ||||||
|  | 	fiv-browser.item.symbolic:backdrop { \ | ||||||
|  | 		color: shade(@theme_bg_color, 0.875); \ | ||||||
|  | 		border-color: transparent; \ | ||||||
|  | 	} \ | ||||||
| 	fiv-browser.item.symbolic { \ | 	fiv-browser.item.symbolic { \ | ||||||
| 		border-color: transparent; color: shade(@theme_bg_color, 0.875); \ | 		background-blend-mode: color; \ | ||||||
| 		background: @theme_bg_color; background-image: none; \ | 		background: @theme_bg_color; background-image: none; \ | ||||||
| 	} \ | 	} \ | ||||||
|  | 	fiv-browser.item.symbolic:selected { \ | ||||||
|  | 		color: @theme_selected_bg_color; background-image: linear-gradient(0, \ | ||||||
|  | 			@theme_selected_bg_color, @theme_selected_bg_color); \ | ||||||
|  | 	} \ | ||||||
|  | 	fiv-browser.item.symbolic:selected:not(:focus) { \ | ||||||
|  | 		color: @fiv-semiselected; background-image: linear-gradient(0, \ | ||||||
|  | 			@fiv-semiselected, @fiv-semiselected); \ | ||||||
|  | 	} \ | ||||||
| 	.fiv-information label { padding: 0 4px; }"; | 	.fiv-information label { padding: 0 4px; }"; | ||||||
| 
 | 
 | ||||||
| int | int | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user