Turn the browser into a DnD source
The destination does all the work of handling file operations. Also, add some missing logic for horizontal scrolling.
This commit is contained in:
		
							parent
							
								
									31f9feab7b
								
							
						
					
					
						commit
						580b68789b
					
				
							
								
								
									
										113
									
								
								fiv-browser.c
									
									
									
									
									
								
							
							
						
						
									
										113
									
								
								fiv-browser.c
									
									
									
									
									
								
							| @ -68,6 +68,10 @@ struct _FivBrowser { | |||||||
| 	GArray *layouted_rows;              ///< []Row
 | 	GArray *layouted_rows;              ///< []Row
 | ||||||
| 	const Entry *selected;              ///< Selected entry or NULL
 | 	const Entry *selected;              ///< Selected entry or NULL
 | ||||||
| 
 | 
 | ||||||
|  | 	guint tracked_button;               ///< Pressed mouse button number or 0
 | ||||||
|  | 	double drag_begin_x;                ///< Viewport start X coordinate or -1
 | ||||||
|  | 	double drag_begin_y;                ///< Viewport start Y coordinate or -1
 | ||||||
|  | 
 | ||||||
| 	Thumbnailer *thumbnailers;          ///< Parallelized thumbnailers
 | 	Thumbnailer *thumbnailers;          ///< Parallelized thumbnailers
 | ||||||
| 	size_t thumbnailers_len;            ///< Thumbnailers array size
 | 	size_t thumbnailers_len;            ///< Thumbnailers array size
 | ||||||
| 	GList *thumbnailers_queue;          ///< Queued up Entry pointers
 | 	GList *thumbnailers_queue;          ///< Queued up Entry pointers
 | ||||||
| @ -132,6 +136,13 @@ append_row(FivBrowser *self, int *y, int x, GArray *items_array) | |||||||
| 	*y += self->item_border_y; | 	*y += self->item_border_y; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void | ||||||
|  | abort_button_tracking(FivBrowser *self) | ||||||
|  | { | ||||||
|  | 	self->tracked_button = 0; | ||||||
|  | 	self->drag_begin_x = self->drag_begin_y = -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int | static int | ||||||
| relayout(FivBrowser *self, int width) | relayout(FivBrowser *self, int width) | ||||||
| { | { | ||||||
| @ -143,6 +154,8 @@ relayout(FivBrowser *self, int width) | |||||||
| 	int available_width = width - padding.left - padding.right; | 	int available_width = width - padding.left - padding.right; | ||||||
| 
 | 
 | ||||||
| 	g_array_set_size(self->layouted_rows, 0); | 	g_array_set_size(self->layouted_rows, 0); | ||||||
|  | 	// Whatever these used to point at might no longer be there.
 | ||||||
|  | 	abort_button_tracking(self); | ||||||
| 
 | 
 | ||||||
| 	GArray *items = g_array_new(TRUE, TRUE, sizeof(Item)); | 	GArray *items = g_array_new(TRUE, TRUE, sizeof(Item)); | ||||||
| 	int x = 0, y = padding.top; | 	int x = 0, y = padding.top; | ||||||
| @ -242,6 +255,8 @@ item_extents(FivBrowser *self, const Item *item, const Row *row) | |||||||
| static const Entry * | static const Entry * | ||||||
| entry_at(FivBrowser *self, int x, int y) | entry_at(FivBrowser *self, int x, int y) | ||||||
| { | { | ||||||
|  | 	if (self->hadjustment) | ||||||
|  | 		x += round(gtk_adjustment_get_value(self->hadjustment)); | ||||||
| 	if (self->vadjustment) | 	if (self->vadjustment) | ||||||
| 		y += round(gtk_adjustment_get_value(self->vadjustment)); | 		y += round(gtk_adjustment_get_value(self->vadjustment)); | ||||||
| 
 | 
 | ||||||
| @ -272,6 +287,8 @@ entry_rect(FivBrowser *self, const Entry *entry) | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	if (self->hadjustment) | ||||||
|  | 		rect.x -= round(gtk_adjustment_get_value(self->hadjustment)); | ||||||
| 	if (self->vadjustment) | 	if (self->vadjustment) | ||||||
| 		rect.y -= round(gtk_adjustment_get_value(self->vadjustment)); | 		rect.y -= round(gtk_adjustment_get_value(self->vadjustment)); | ||||||
| 	return rect; | 	return rect; | ||||||
| @ -1088,11 +1105,12 @@ fiv_browser_draw(GtkWidget *widget, cairo_t *cr) | |||||||
| 	gtk_render_background(gtk_widget_get_style_context(widget), cr, 0, 0, | 	gtk_render_background(gtk_widget_get_style_context(widget), cr, 0, 0, | ||||||
| 		allocation.width, allocation.height); | 		allocation.width, allocation.height); | ||||||
| 
 | 
 | ||||||
| 	// TODO(p): self->hadjustment as well, and test it.
 | 	if (self->hadjustment) | ||||||
| 	if (self->vadjustment) { | 		cairo_translate( | ||||||
| 		gdouble y = round(gtk_adjustment_get_value(self->vadjustment)); | 			cr, -round(gtk_adjustment_get_value(self->hadjustment)), 0); | ||||||
| 		cairo_translate(cr, 0, -y); | 	if (self->vadjustment) | ||||||
| 	} | 		cairo_translate( | ||||||
|  | 			cr, 0, -round(gtk_adjustment_get_value(self->vadjustment))); | ||||||
| 
 | 
 | ||||||
| 	GdkRectangle clip = {}; | 	GdkRectangle clip = {}; | ||||||
| 	gboolean have_clip = gdk_cairo_get_clip_rectangle(cr, &clip); | 	gboolean have_clip = gdk_cairo_get_clip_rectangle(cr, &clip); | ||||||
| @ -1124,10 +1142,15 @@ open_entry(GtkWidget *self, const Entry *entry, gboolean new_window) | |||||||
| static gboolean | static gboolean | ||||||
| fiv_browser_button_press_event(GtkWidget *widget, GdkEventButton *event) | fiv_browser_button_press_event(GtkWidget *widget, GdkEventButton *event) | ||||||
| { | { | ||||||
|  | 	FivBrowser *self = FIV_BROWSER(widget); | ||||||
| 	GTK_WIDGET_CLASS(fiv_browser_parent_class) | 	GTK_WIDGET_CLASS(fiv_browser_parent_class) | ||||||
| 		->button_press_event(widget, event); | 		->button_press_event(widget, event); | ||||||
| 
 | 
 | ||||||
| 	FivBrowser *self = FIV_BROWSER(widget); | 	// Make pressing multiple mouse buttons at once cancel a click.
 | ||||||
|  | 	if (self->tracked_button) { | ||||||
|  | 		abort_button_tracking(self); | ||||||
|  | 		return TRUE; | ||||||
|  | 	} | ||||||
| 	if (event->type != GDK_BUTTON_PRESS) | 	if (event->type != GDK_BUTTON_PRESS) | ||||||
| 		return FALSE; | 		return FALSE; | ||||||
| 
 | 
 | ||||||
| @ -1166,18 +1189,33 @@ fiv_browser_button_press_event(GtkWidget *widget, GdkEventButton *event) | |||||||
| 		g_object_unref(file); | 		g_object_unref(file); | ||||||
| 		return TRUE; | 		return TRUE; | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	// gtk_drag_source_set() would span the whole widget area, we'd have to
 | ||||||
|  | 	// un/set it as needed, in particular to handle empty space.
 | ||||||
|  | 	// It might be a good idea to use GtkGestureDrag instead.
 | ||||||
|  | 	if (event->button == GDK_BUTTON_PRIMARY || | ||||||
|  | 		event->button == GDK_BUTTON_MIDDLE) { | ||||||
|  | 		self->tracked_button = event->button; | ||||||
|  | 		self->drag_begin_x = event->x; | ||||||
|  | 		self->drag_begin_y = event->y; | ||||||
|  | 		return TRUE; | ||||||
|  | 	} | ||||||
| 	return FALSE; | 	return FALSE; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static gboolean | static gboolean | ||||||
| fiv_browser_button_release_event(GtkWidget *widget, GdkEventButton *event) | fiv_browser_button_release_event(GtkWidget *widget, GdkEventButton *event) | ||||||
| { | { | ||||||
|  | 	FivBrowser *self = FIV_BROWSER(widget); | ||||||
| 	GTK_WIDGET_CLASS(fiv_browser_parent_class) | 	GTK_WIDGET_CLASS(fiv_browser_parent_class) | ||||||
| 		->button_release_event(widget, event); | 		->button_release_event(widget, event); | ||||||
|  | 	if (event->button != self->tracked_button) | ||||||
|  | 		return FALSE; | ||||||
| 
 | 
 | ||||||
| 	FivBrowser *self = FIV_BROWSER(widget); | 	// Middle clicks should only work on the starting entry.
 | ||||||
| 	const Entry *entry = entry_at(self, event->x, event->y); | 	const Entry *entry = entry_at(self, self->drag_begin_x, self->drag_begin_y); | ||||||
| 	if (!entry) | 	abort_button_tracking(self); | ||||||
|  | 	if (!entry || entry != entry_at(self, event->x, event->y)) | ||||||
| 		return FALSE; | 		return FALSE; | ||||||
| 
 | 
 | ||||||
| 	guint state = event->state & gtk_accelerator_get_default_mod_mask(); | 	guint state = event->state & gtk_accelerator_get_default_mod_mask(); | ||||||
| @ -1192,16 +1230,37 @@ fiv_browser_button_release_event(GtkWidget *widget, GdkEventButton *event) | |||||||
| static gboolean | static gboolean | ||||||
| fiv_browser_motion_notify_event(GtkWidget *widget, GdkEventMotion *event) | fiv_browser_motion_notify_event(GtkWidget *widget, GdkEventMotion *event) | ||||||
| { | { | ||||||
|  | 	FivBrowser *self = FIV_BROWSER(widget); | ||||||
| 	GTK_WIDGET_CLASS(fiv_browser_parent_class) | 	GTK_WIDGET_CLASS(fiv_browser_parent_class) | ||||||
| 		->motion_notify_event(widget, event); | 		->motion_notify_event(widget, event); | ||||||
| 
 | 
 | ||||||
| 	FivBrowser *self = FIV_BROWSER(widget); | 	guint state = event->state & gtk_accelerator_get_default_mod_mask(); | ||||||
| 	if (event->state != 0) | 	if (state != 0 || self->tracked_button != GDK_BUTTON_PRIMARY || | ||||||
|  | 		!gtk_drag_check_threshold(widget, self->drag_begin_x, | ||||||
|  | 			self->drag_begin_y, event->x, event->y)) { | ||||||
|  | 		const Entry *entry = entry_at(self, event->x, event->y); | ||||||
|  | 		GdkWindow *window = gtk_widget_get_window(widget); | ||||||
|  | 		gdk_window_set_cursor(window, entry ? self->pointer : NULL); | ||||||
| 		return FALSE; | 		return FALSE; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	const Entry *entry = entry_at(self, event->x, event->y); | 	// The "correct" behaviour is to set the selection on a left mouse button
 | ||||||
| 	GdkWindow *window = gtk_widget_get_window(widget); | 	// press immediately, but that is regarded as visual noise.
 | ||||||
| 	gdk_window_set_cursor(window, entry ? self->pointer : NULL); | 	const Entry *entry = entry_at(self, self->drag_begin_x, self->drag_begin_y); | ||||||
|  | 	abort_button_tracking(self); | ||||||
|  | 	if (!entry) | ||||||
|  | 		return TRUE; | ||||||
|  | 
 | ||||||
|  | 	self->selected = entry; | ||||||
|  | 	gtk_widget_queue_draw(widget); | ||||||
|  | 
 | ||||||
|  | 	GtkTargetList *target_list = gtk_target_list_new(NULL, 0); | ||||||
|  | 	gtk_target_list_add_uri_targets(target_list, 0); | ||||||
|  | 	GdkDragAction actions = GDK_ACTION_COPY | GDK_ACTION_MOVE | | ||||||
|  | 		GDK_ACTION_LINK | GDK_ACTION_ASK; | ||||||
|  | 	gtk_drag_begin_with_coordinates(widget, target_list, actions, | ||||||
|  | 		self->tracked_button, (GdkEvent *) event, event->x, event->y); | ||||||
|  | 	gtk_target_list_unref(target_list); | ||||||
| 	return TRUE; | 	return TRUE; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -1209,6 +1268,7 @@ static gboolean | |||||||
| fiv_browser_scroll_event(GtkWidget *widget, GdkEventScroll *event) | fiv_browser_scroll_event(GtkWidget *widget, GdkEventScroll *event) | ||||||
| { | { | ||||||
| 	FivBrowser *self = FIV_BROWSER(widget); | 	FivBrowser *self = FIV_BROWSER(widget); | ||||||
|  | 	abort_button_tracking(self); | ||||||
| 	if ((event->state & gtk_accelerator_get_default_mod_mask()) != | 	if ((event->state & gtk_accelerator_get_default_mod_mask()) != | ||||||
| 		GDK_CONTROL_MASK) | 		GDK_CONTROL_MASK) | ||||||
| 		return FALSE; | 		return FALSE; | ||||||
| @ -1227,6 +1287,28 @@ fiv_browser_scroll_event(GtkWidget *widget, GdkEventScroll *event) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void | ||||||
|  | fiv_browser_drag_begin(GtkWidget *widget, GdkDragContext *context) | ||||||
|  | { | ||||||
|  | 	FivBrowser *self = FIV_BROWSER(widget); | ||||||
|  | 	if (self->selected) { | ||||||
|  | 		// There doesn't seem to be a size limit.
 | ||||||
|  | 		gtk_drag_set_icon_surface(context, self->selected->thumbnail); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | fiv_browser_drag_data_get(GtkWidget *widget, | ||||||
|  | 	G_GNUC_UNUSED GdkDragContext *context, GtkSelectionData *data, | ||||||
|  | 	G_GNUC_UNUSED guint info, G_GNUC_UNUSED guint time) | ||||||
|  | { | ||||||
|  | 	FivBrowser *self = FIV_BROWSER(widget); | ||||||
|  | 	if (self->selected) { | ||||||
|  | 		(void) gtk_selection_data_set_uris( | ||||||
|  | 			data, (gchar *[]){self->selected->uri, NULL}); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void | static void | ||||||
| select_closest(FivBrowser *self, const Row *row, int target) | select_closest(FivBrowser *self, const Row *row, int target) | ||||||
| { | { | ||||||
| @ -1561,6 +1643,8 @@ 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->button_release_event = fiv_browser_button_release_event; | 	widget_class->button_release_event = fiv_browser_button_release_event; | ||||||
| 	widget_class->motion_notify_event = fiv_browser_motion_notify_event; | 	widget_class->motion_notify_event = fiv_browser_motion_notify_event; | ||||||
|  | 	widget_class->drag_begin = fiv_browser_drag_begin; | ||||||
|  | 	widget_class->drag_data_get = fiv_browser_drag_data_get; | ||||||
| 	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->key_press_event = fiv_browser_key_press_event; | ||||||
| 	widget_class->query_tooltip = fiv_browser_query_tooltip; | 	widget_class->query_tooltip = fiv_browser_query_tooltip; | ||||||
| @ -1588,6 +1672,7 @@ 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); | ||||||
|  | 	abort_button_tracking(self); | ||||||
| 
 | 
 | ||||||
| 	self->thumbnailers_len = g_get_num_processors(); | 	self->thumbnailers_len = g_get_num_processors(); | ||||||
| 	self->thumbnailers = | 	self->thumbnailers = | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user