aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2022-01-10 17:54:41 +0100
committerPřemysl Eric Janouch <p@janouch.name>2022-01-11 11:27:35 +0100
commite663f027548a862c051603cf4c626001165e27f0 (patch)
tree7bb2a2cfb32b7d28659103534dedad8920fdd327
parent1a190001fc541c645166e8997f4bb3408835dd1f (diff)
downloadfiv-e663f027548a862c051603cf4c626001165e27f0.tar.gz
fiv-e663f027548a862c051603cf4c626001165e27f0.tar.xz
fiv-e663f027548a862c051603cf4c626001165e27f0.zip
Implement selection in the browser
Keyboard controls are missing so far.
-rw-r--r--fiv-browser.c73
-rw-r--r--fiv.c31
2 files changed, 89 insertions, 15 deletions
diff --git a/fiv-browser.c b/fiv-browser.c
index e184fa8..e370ad6 100644
--- a/fiv-browser.c
+++ b/fiv-browser.c
@@ -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);
@@ -1128,6 +1151,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);
}
diff --git a/fiv.c b/fiv.c
index c326fdf..0c62c11 100644
--- a/fiv.c
+++ b/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