|
|
|
|
@@ -43,6 +43,7 @@ struct _FastivBrowser {
|
|
|
|
|
|
|
|
|
|
FastivIoThumbnailSize item_size; ///< Thumbnail size
|
|
|
|
|
int item_height; ///< Thumbnail height in pixels
|
|
|
|
|
int item_spacing; ///< Space between items in pixels
|
|
|
|
|
|
|
|
|
|
GArray *entries; ///< [Entry]
|
|
|
|
|
GArray *layouted_rows; ///< [Row]
|
|
|
|
|
@@ -60,10 +61,6 @@ typedef struct row Row;
|
|
|
|
|
|
|
|
|
|
static const double g_permitted_width_multiplier = 2;
|
|
|
|
|
|
|
|
|
|
// Could be split out to also-idiomatic row-spacing/column-spacing properties.
|
|
|
|
|
// TODO(p): Make a property for this.
|
|
|
|
|
static const int g_item_spacing = 1;
|
|
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
|
|
|
|
|
|
struct entry {
|
|
|
|
|
@@ -106,7 +103,7 @@ static void
|
|
|
|
|
append_row(FastivBrowser *self, int *y, int x, GArray *items_array)
|
|
|
|
|
{
|
|
|
|
|
if (self->layouted_rows->len)
|
|
|
|
|
*y += g_item_spacing;
|
|
|
|
|
*y += self->item_spacing;
|
|
|
|
|
|
|
|
|
|
*y += self->item_border_y;
|
|
|
|
|
g_array_append_val(self->layouted_rows, ((Row) {
|
|
|
|
|
@@ -143,8 +140,8 @@ relayout(FastivBrowser *self, int width)
|
|
|
|
|
2 * self->item_border_x;
|
|
|
|
|
if (!items->len) {
|
|
|
|
|
// Just insert it, whether or not there's any space.
|
|
|
|
|
} else if (x + g_item_spacing + width <= available_width) {
|
|
|
|
|
x += g_item_spacing;
|
|
|
|
|
} else if (x + self->item_spacing + width <= available_width) {
|
|
|
|
|
x += self->item_spacing;
|
|
|
|
|
} else {
|
|
|
|
|
append_row(self, &y,
|
|
|
|
|
padding.left + MAX(0, available_width - x) / 2, items);
|
|
|
|
|
@@ -485,6 +482,23 @@ fastiv_browser_get_property(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
set_item_size(FastivBrowser *self, FastivIoThumbnailSize size)
|
|
|
|
|
{
|
|
|
|
|
if (size < FASTIV_IO_THUMBNAIL_SIZE_MIN ||
|
|
|
|
|
size > FASTIV_IO_THUMBNAIL_SIZE_MAX)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (size != self->item_size) {
|
|
|
|
|
self->item_size = size;
|
|
|
|
|
self->item_height = fastiv_io_thumbnail_sizes[self->item_size].size;
|
|
|
|
|
reload_thumbnails(self);
|
|
|
|
|
|
|
|
|
|
g_object_notify_by_pspec(
|
|
|
|
|
G_OBJECT(self), browser_properties[PROP_THUMBNAIL_SIZE]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
fastiv_browser_set_property(
|
|
|
|
|
GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
|
|
|
|
|
@@ -492,11 +506,7 @@ fastiv_browser_set_property(
|
|
|
|
|
FastivBrowser *self = FASTIV_BROWSER(object);
|
|
|
|
|
switch (property_id) {
|
|
|
|
|
case PROP_THUMBNAIL_SIZE:
|
|
|
|
|
if (g_value_get_enum(value) != (int) self->item_size) {
|
|
|
|
|
self->item_size = g_value_get_enum(value);
|
|
|
|
|
self->item_height = fastiv_io_thumbnail_sizes[self->item_size].size;
|
|
|
|
|
reload_thumbnails(self);
|
|
|
|
|
}
|
|
|
|
|
set_item_size(self, g_value_get_enum(value));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
|
|
|
|
|
@@ -550,7 +560,8 @@ fastiv_browser_realize(GtkWidget *widget)
|
|
|
|
|
|
|
|
|
|
.visual = gtk_widget_get_visual(widget),
|
|
|
|
|
.event_mask = gtk_widget_get_events(widget) | GDK_KEY_PRESS_MASK |
|
|
|
|
|
GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK,
|
|
|
|
|
GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
|
|
|
|
|
GDK_SCROLL_MASK,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// We need this window to receive input events at all.
|
|
|
|
|
@@ -652,6 +663,51 @@ fastiv_browser_motion_notify_event(GtkWidget *widget, GdkEventMotion *event)
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
fastiv_browser_scroll_event(GtkWidget *widget, GdkEventScroll *event)
|
|
|
|
|
{
|
|
|
|
|
FastivBrowser *self = FASTIV_BROWSER(widget);
|
|
|
|
|
if ((event->state & gtk_accelerator_get_default_mod_mask()) !=
|
|
|
|
|
GDK_CONTROL_MASK)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
switch (event->direction) {
|
|
|
|
|
case GDK_SCROLL_UP:
|
|
|
|
|
set_item_size(self, self->item_size + 1);
|
|
|
|
|
return TRUE;
|
|
|
|
|
case GDK_SCROLL_DOWN:
|
|
|
|
|
set_item_size(self, self->item_size - 1);
|
|
|
|
|
return TRUE;
|
|
|
|
|
default:
|
|
|
|
|
// For some reason, we can also get GDK_SCROLL_SMOOTH.
|
|
|
|
|
// Left/right are good to steal from GtkScrolledWindow for consistency.
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
fastiv_browser_query_tooltip(GtkWidget *widget, gint x, gint y,
|
|
|
|
|
G_GNUC_UNUSED gboolean keyboard_tooltip, GtkTooltip *tooltip)
|
|
|
|
|
{
|
|
|
|
|
FastivBrowser *self = FASTIV_BROWSER(widget);
|
|
|
|
|
const Entry *entry = entry_at(self, x, y);
|
|
|
|
|
if (!entry)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
GFile *file = g_file_new_for_path(entry->filename);
|
|
|
|
|
GFileInfo *info = g_file_query_info(file,
|
|
|
|
|
G_FILE_ATTRIBUTE_STANDARD_NAME
|
|
|
|
|
"," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
|
|
|
|
|
G_FILE_QUERY_INFO_NONE, NULL, NULL);
|
|
|
|
|
g_object_unref(file);
|
|
|
|
|
if (!info)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
gtk_tooltip_set_text(tooltip, g_file_info_get_display_name(info));
|
|
|
|
|
g_object_unref(info);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
fastiv_browser_style_updated(GtkWidget *widget)
|
|
|
|
|
{
|
|
|
|
|
@@ -661,6 +717,11 @@ fastiv_browser_style_updated(GtkWidget *widget)
|
|
|
|
|
GtkStyleContext *style = gtk_widget_get_style_context(widget);
|
|
|
|
|
GtkBorder border = {}, margin = {};
|
|
|
|
|
|
|
|
|
|
int item_spacing = self->item_spacing;
|
|
|
|
|
gtk_widget_style_get(widget, "spacing", &self->item_spacing, NULL);
|
|
|
|
|
if (item_spacing != self->item_spacing)
|
|
|
|
|
gtk_widget_queue_resize(widget);
|
|
|
|
|
|
|
|
|
|
// Using a pseudo-class, because GTK+ regions are deprecated.
|
|
|
|
|
gtk_style_context_save(style);
|
|
|
|
|
gtk_style_context_add_class(style, "item");
|
|
|
|
|
@@ -741,8 +802,16 @@ fastiv_browser_class_init(FastivBrowserClass *klass)
|
|
|
|
|
widget_class->size_allocate = fastiv_browser_size_allocate;
|
|
|
|
|
widget_class->button_press_event = fastiv_browser_button_press_event;
|
|
|
|
|
widget_class->motion_notify_event = fastiv_browser_motion_notify_event;
|
|
|
|
|
widget_class->scroll_event = fastiv_browser_scroll_event;
|
|
|
|
|
widget_class->query_tooltip = fastiv_browser_query_tooltip;
|
|
|
|
|
widget_class->style_updated = fastiv_browser_style_updated;
|
|
|
|
|
|
|
|
|
|
// Could be split to also-idiomatic row-spacing/column-spacing properties.
|
|
|
|
|
// The GParamSpec is sinked by this call.
|
|
|
|
|
gtk_widget_class_install_style_property(widget_class,
|
|
|
|
|
g_param_spec_int("spacing", "Spacing", "Space between items",
|
|
|
|
|
0, G_MAXINT, 1, G_PARAM_READWRITE));
|
|
|
|
|
|
|
|
|
|
// TODO(p): Later override "screen_changed", recreate Pango layouts there,
|
|
|
|
|
// if we get to have any, or otherwise reflect DPI changes.
|
|
|
|
|
gtk_widget_class_set_css_name(widget_class, "fastiv-browser");
|
|
|
|
|
@@ -752,14 +821,14 @@ static void
|
|
|
|
|
fastiv_browser_init(FastivBrowser *self)
|
|
|
|
|
{
|
|
|
|
|
gtk_widget_set_can_focus(GTK_WIDGET(self), TRUE);
|
|
|
|
|
gtk_widget_set_has_tooltip(GTK_WIDGET(self), TRUE);
|
|
|
|
|
|
|
|
|
|
self->entries = g_array_new(FALSE, TRUE, sizeof(Entry));
|
|
|
|
|
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->item_size = FASTIV_IO_THUMBNAIL_SIZE_NORMAL;
|
|
|
|
|
self->item_height = fastiv_io_thumbnail_sizes[self->item_size].size;
|
|
|
|
|
set_item_size(self, FASTIV_IO_THUMBNAIL_SIZE_NORMAL);
|
|
|
|
|
self->selected = -1;
|
|
|
|
|
self->glow = cairo_image_surface_create(CAIRO_FORMAT_A1, 0, 0);
|
|
|
|
|
|
|
|
|
|
|