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 row Row; | ||||
| 
 | ||||
| typedef struct _Thumbnailer { | ||||
| typedef struct { | ||||
| 	FivBrowser *self;                   ///< Parent browser
 | ||||
| 	Entry *target;                      ///< Currently processed Entry pointer
 | ||||
| 	GSubprocess *minion;                ///< A slave for the current queue head
 | ||||
| @ -66,7 +66,7 @@ struct _FivBrowser { | ||||
| 	FivIoModel *model;                  ///< Filesystem model
 | ||||
| 	GArray *entries;                    ///< []Entry
 | ||||
| 	GArray *layouted_rows;              ///< []Row
 | ||||
| 	int selected; | ||||
| 	const Entry *selected;              ///< Selected entry or NULL
 | ||||
| 
 | ||||
| 	Thumbnailer *thumbnailers;          ///< Parallelized thumbnailers
 | ||||
| 	size_t thumbnailers_len;            ///< Thumbnailers array size
 | ||||
| @ -244,7 +244,7 @@ static const Entry * | ||||
| entry_at(FivBrowser *self, int x, int y) | ||||
| { | ||||
| 	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++) { | ||||
| 		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_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; | ||||
| 	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++) { | ||||
| 		cairo_save(cr); | ||||
| 		GdkRectangle extents = item_extents(self, item, row); | ||||
| 		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_set_state(style, state); | ||||
| 		if (item->entry->icon) { | ||||
| 			gtk_style_context_add_class(style, "symbolic"); | ||||
| 		} else { | ||||
| 			GdkRGBA glow_color = {}; | ||||
| 			gtk_style_context_get_color(style, state, &glow_color); | ||||
| 			gdk_cairo_set_source_rgba(cr, &glow_color); | ||||
| 			draw_outer_border(self, cr, | ||||
| 				border.left + extents.width + border.right, | ||||
| @ -309,6 +313,9 @@ draw_row(FivBrowser *self, cairo_t *cr, const Row *row) | ||||
| 			cairo_set_source_surface( | ||||
| 				cr, item->entry->thumbnail, border.left, border.top); | ||||
| 			cairo_paint(cr); | ||||
| 
 | ||||
| 			// Here, we could consider multiplying
 | ||||
| 			// the whole rectangle with the selection color.
 | ||||
| 		} | ||||
| 
 | ||||
| 		cairo_restore(cr); | ||||
| @ -1010,7 +1017,7 @@ fiv_browser_draw(GtkWidget *widget, cairo_t *cr) | ||||
| 
 | ||||
| 	// TODO(p): self->hadjustment as well, and test it.
 | ||||
| 	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); | ||||
| 	} | ||||
| 
 | ||||
| @ -1057,8 +1064,21 @@ fiv_browser_button_press_event(GtkWidget *widget, GdkEventButton *event) | ||||
| 		gtk_widget_grab_focus(widget); | ||||
| 
 | ||||
| 	const Entry *entry = entry_at(self, event->x, event->y); | ||||
| 	if (!entry && event->button == GDK_BUTTON_SECONDARY) { | ||||
| 		show_context_menu(widget, fiv_io_model_get_location(self->model)); | ||||
| 	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)); | ||||
| 			break; | ||||
| 		default: | ||||
| 			return FALSE; | ||||
| 		} | ||||
| 
 | ||||
| 		if (self->selected) { | ||||
| 			self->selected = NULL; | ||||
| 			gtk_widget_queue_draw(widget); | ||||
| 		} | ||||
| 		return TRUE; | ||||
| 	} | ||||
| 	if (!entry) | ||||
| @ -1076,6 +1096,9 @@ fiv_browser_button_press_event(GtkWidget *widget, GdkEventButton *event) | ||||
| 			return open_entry(widget, entry, TRUE); | ||||
| 		return FALSE; | ||||
| 	case GDK_BUTTON_SECONDARY: | ||||
| 		self->selected = entry; | ||||
| 		gtk_widget_queue_draw(widget); | ||||
| 
 | ||||
| 		// On X11, after closing the menu, the pointer otherwise remains,
 | ||||
| 		// no matter what its new location is.
 | ||||
| 		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 | ||||
| fiv_browser_query_tooltip(GtkWidget *widget, gint x, gint y, | ||||
| 	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->motion_notify_event = fiv_browser_motion_notify_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->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); | ||||
| 	self->layouted_rows = g_array_new(FALSE, TRUE, sizeof(Row)); | ||||
| 	g_array_set_clear_func(self->layouted_rows, (GDestroyNotify) row_free); | ||||
| 	self->selected = -1; | ||||
| 
 | ||||
| 	self->thumbnailers_len = g_get_num_processors(); | ||||
| 	self->thumbnailers = | ||||
| @ -1299,6 +1334,13 @@ on_model_files_changed(FivIoModel *model, FivBrowser *self) | ||||
| { | ||||
| 	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.
 | ||||
| 	thumbnailers_abort(self); | ||||
| 	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++) { | ||||
| 		g_array_append_val(self->entries, | ||||
| 			((Entry) {.thumbnail = NULL, .uri = files->pdata[i]})); | ||||
| 		if (!g_strcmp0(selected_uri, files->pdata[i])) | ||||
| 			selected = i; | ||||
| 		files->pdata[i] = NULL; | ||||
| 	} | ||||
| 	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); | ||||
| 	thumbnailers_start(self); | ||||
| } | ||||
|  | ||||
							
								
								
									
										31
									
								
								fiv.c
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								fiv.c
									
									
									
									
									
								
							| @ -567,7 +567,7 @@ switch_to_browser(void) | ||||
| { | ||||
| 	set_window_title(g.directory); | ||||
| 	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 | ||||
| @ -1567,6 +1567,8 @@ make_browser_sidebar(FivIoModel *model) | ||||
| // thus resolving the problem using overlaps.
 | ||||
| // 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; \
 | ||||
| 	@define-color fiv-semiselected \ | ||||
| 		mix(@theme_selected_bg_color, @content_view_bg, 0.5); \ | ||||
| 	fiv-view, fiv-browser { background: @content_view_bg; } \ | ||||
| 	placessidebar.fiv .toolbar { padding: 2px 6px; } \ | ||||
| 	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.item { \ | ||||
| 		/* For non-symbolic, color is applied to the glowing margin. */ \ | ||||
| 		color: mix(#000, @content_view_bg, 0.625); margin: 8px; \ | ||||
| 		border: 2px solid #fff; \ | ||||
| 	} \ | ||||
| @ -1591,14 +1594,36 @@ static const char stylesheet[] = "@define-color fiv-tile @content_view_bg; \ | ||||
| 		background-size: 40px 40px; \ | ||||
| 		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); \ | ||||
| 		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 { \ | ||||
| 		border-color: transparent; color: shade(@theme_bg_color, 0.875); \ | ||||
| 		background-blend-mode: color; \ | ||||
| 		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; }"; | ||||
| 
 | ||||
| int | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user