From 23429d9631ed8b94007fa9af49d7ae83c47e419e Mon Sep 17 00:00:00 2001 From: Přemysl Eric Janouch
Date: Sat, 16 Jul 2022 16:49:47 +0200 Subject: Implement GtkScrollable in FivView This fixes rendering and positioning behaviour when dragging on X11, where we aim to use a native GdkWindow. --- fiv-browser.c | 11 ++-- fiv-view.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 147 insertions(+), 33 deletions(-) diff --git a/fiv-browser.c b/fiv-browser.c index fdce71a..c1c6a48 100644 --- a/fiv-browser.c +++ b/fiv-browser.c @@ -198,12 +198,12 @@ relayout(FivBrowser *self, int width) if (self->vadjustment) { gtk_adjustment_set_lower(self->vadjustment, 0); gtk_adjustment_set_upper(self->vadjustment, total_height); - gtk_adjustment_set_page_size( - self->vadjustment, gtk_widget_get_allocated_height(widget)); - gtk_adjustment_set_page_increment( - self->vadjustment, gtk_widget_get_allocated_height(widget)); gtk_adjustment_set_step_increment(self->vadjustment, self->item_height + self->item_spacing + 2 * self->item_border_y); + gtk_adjustment_set_page_increment( + self->vadjustment, gtk_widget_get_allocated_height(widget) * 0.9); + gtk_adjustment_set_page_size( + self->vadjustment, gtk_widget_get_allocated_height(widget)); } return total_height; } @@ -765,8 +765,7 @@ static void on_adjustment_value_changed( G_GNUC_UNUSED GtkAdjustment *adjustment, gpointer user_data) { - FivBrowser *self = FIV_BROWSER(user_data); - gtk_widget_queue_draw(GTK_WIDGET(self)); + gtk_widget_queue_draw(GTK_WIDGET(user_data)); } static gboolean diff --git a/fiv-view.c b/fiv-view.c index d4a95c9..25494b0 100644 --- a/fiv-view.c +++ b/fiv-view.c @@ -50,6 +50,10 @@ fiv_view_command_get_type(void) struct _FivView { GtkWidget parent_instance; + GtkAdjustment *hadjustment; ///< GtkScrollable boilerplate + GtkAdjustment *vadjustment; ///< GtkScrollable boilerplate + GtkScrollablePolicy hscroll_policy; ///< GtkScrollable boilerplate + GtkScrollablePolicy vscroll_policy; ///< GtkScrollable boilerplate gchar *messages; ///< Image load information gchar *uri; ///< Path to the current image (if any) @@ -73,7 +77,8 @@ struct _FivView { gulong frame_update_connection; ///< GdkFrameClock::update }; -G_DEFINE_TYPE(FivView, fiv_view, GTK_TYPE_WIDGET) +G_DEFINE_TYPE_EXTENDED(FivView, fiv_view, GTK_TYPE_WIDGET, 0, + G_IMPLEMENT_INTERFACE(GTK_TYPE_SCROLLABLE, NULL)) typedef struct _Dimensions { double width, height; @@ -128,7 +133,13 @@ enum { PROP_CAN_ANIMATE, PROP_HAS_PREVIOUS_PAGE, PROP_HAS_NEXT_PAGE, - N_PROPERTIES + N_PROPERTIES, + + // These are overriden, we do not register them. + PROP_HADJUSTMENT, + PROP_VADJUSTMENT, + PROP_HSCROLL_POLICY, + PROP_VSCROLL_POLICY, }; static GParamSpec *view_properties[N_PROPERTIES]; @@ -141,6 +152,75 @@ enum { // Globals are, sadly, the canonical way of storing signal numbers. static guint view_signals[LAST_SIGNAL]; +static void +on_adjustment_value_changed( + G_GNUC_UNUSED GtkAdjustment *adjustment, gpointer user_data) +{ + gtk_widget_queue_draw(GTK_WIDGET(user_data)); +} + +static Dimensions +get_surface_dimensions(FivView *self) +{ + if (!self->image) + return (Dimensions) {}; + + Dimensions dimensions = {}; + fiv_io_orientation_dimensions( + self->page, self->orientation, &dimensions.width, &dimensions.height); + return dimensions; +} + +static void +get_display_dimensions(FivView *self, int *width, int *height) +{ + Dimensions surface_dimensions = get_surface_dimensions(self); + *width = ceil(surface_dimensions.width * self->scale); + *height = ceil(surface_dimensions.height * self->scale); +} + +static void +update_adjustments(FivView *self) +{ + int w = 0, h = 0; + get_display_dimensions(self, &w, &h); + + GtkAllocation alloc; + gtk_widget_get_allocation(GTK_WIDGET(self), &alloc); + + if (self->hadjustment) { + gtk_adjustment_configure(self->hadjustment, + gtk_adjustment_get_value(self->hadjustment), 0, w, + alloc.width * 0.1, alloc.width * 0.9, alloc.width); + } + if (self->vadjustment) { + gtk_adjustment_configure(self->vadjustment, + gtk_adjustment_get_value(self->vadjustment), 0, h, + alloc.height * 0.1, alloc.height * 0.9, alloc.height); + } +} + +static gboolean +replace_adjustment( + FivView *self, GtkAdjustment **adjustment, GtkAdjustment *replacement) +{ + if (*adjustment == replacement) + return FALSE; + + if (*adjustment) { + g_signal_handlers_disconnect_by_func( + *adjustment, on_adjustment_value_changed, self); + g_clear_object(adjustment); + } + if (replacement) { + *adjustment = g_object_ref(replacement); + g_signal_connect(*adjustment, "value-changed", + G_CALLBACK(on_adjustment_value_changed), self); + update_adjustments(self); + } + return TRUE; +} + static void fiv_view_finalize(GObject *gobject) { @@ -152,6 +232,9 @@ fiv_view_finalize(GObject *gobject) g_free(self->uri); g_free(self->messages); + replace_adjustment(self, &self->hadjustment, NULL); + replace_adjustment(self, &self->vadjustment, NULL); + G_OBJECT_CLASS(fiv_view_parent_class)->finalize(gobject); } @@ -199,6 +282,19 @@ fiv_view_get_property( g_value_set_boolean(value, self->page && cairo_surface_get_user_data(self->page, &fiv_io_key_page_next)); break; + + case PROP_HADJUSTMENT: + g_value_set_object(value, self->hadjustment); + break; + case PROP_VADJUSTMENT: + g_value_set_object(value, self->vadjustment); + break; + case PROP_HSCROLL_POLICY: + g_value_set_enum(value, self->hscroll_policy); + break; + case PROP_VSCROLL_POLICY: + g_value_set_enum(value, self->vscroll_policy); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } @@ -230,31 +326,36 @@ fiv_view_set_property( if (self->enhance != g_value_get_boolean(value)) fiv_view_command(self, FIV_VIEW_COMMAND_TOGGLE_ENHANCE); break; + + case PROP_HADJUSTMENT: + if (replace_adjustment( + self, &self->hadjustment, g_value_get_object(value))) + g_object_notify_by_pspec(object, pspec); + break; + case PROP_VADJUSTMENT: + if (replace_adjustment( + self, &self->vadjustment, g_value_get_object(value))) + g_object_notify_by_pspec(object, pspec); + break; + case PROP_HSCROLL_POLICY: + if ((gint) self->hscroll_policy != g_value_get_enum(value)) { + self->hscroll_policy = g_value_get_enum(value); + gtk_widget_queue_resize(GTK_WIDGET(self)); + g_object_notify_by_pspec(object, pspec); + } + break; + case PROP_VSCROLL_POLICY: + if ((gint) self->vscroll_policy != g_value_get_enum(value)) { + self->vscroll_policy = g_value_get_enum(value); + gtk_widget_queue_resize(GTK_WIDGET(self)); + g_object_notify_by_pspec(object, pspec); + } + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } -static Dimensions -get_surface_dimensions(FivView *self) -{ - if (!self->image) - return (Dimensions) {}; - - Dimensions dimensions = {}; - fiv_io_orientation_dimensions( - self->page, self->orientation, &dimensions.width, &dimensions.height); - return dimensions; -} - -static void -get_display_dimensions(FivView *self, int *width, int *height) -{ - Dimensions surface_dimensions = get_surface_dimensions(self); - *width = ceil(surface_dimensions.width * self->scale); - *height = ceil(surface_dimensions.height * self->scale); -} - static void fiv_view_get_preferred_height(GtkWidget *widget, gint *minimum, gint *natural) { @@ -308,7 +409,7 @@ fiv_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation) FivView *self = FIV_VIEW(widget); if (!self->image || !self->scale_to_fit) - return; + goto out; Dimensions surface_dimensions = get_surface_dimensions(self); double scale = 1; @@ -322,6 +423,9 @@ fiv_view_size_allocate(GtkWidget *widget, GtkAllocation *allocation) g_object_notify_by_pspec(G_OBJECT(widget), view_properties[PROP_SCALE]); prescale_page(self); } + +out: + update_adjustments(self); } // https://www.freedesktop.org/wiki/OpenIcc/ICC_Profiles_in_X_Specification_0.4 @@ -438,6 +542,10 @@ fiv_view_draw(GtkWidget *widget, cairo_t *cr) double x = 0; double y = 0; + if (self->hadjustment) + x = -gtk_adjustment_get_value(self->hadjustment); + if (self->vadjustment) + y = -gtk_adjustment_get_value(self->vadjustment); if (w < allocation.width) x = round((allocation.width - w) / 2.); if (h < allocation.height) @@ -554,8 +662,7 @@ static void set_scale_to_fit_width(FivView *self) { double w = get_surface_dimensions(self).width; - int allocated = gtk_widget_get_allocated_width( - gtk_widget_get_parent(GTK_WIDGET(self))); + int allocated = gtk_widget_get_allocated_width(GTK_WIDGET(self)); if (ceil(w * self->scale) > allocated) set_scale(self, allocated / w); } @@ -564,8 +671,7 @@ static void set_scale_to_fit_height(FivView *self) { double h = get_surface_dimensions(self).height; - int allocated = gtk_widget_get_allocated_height( - gtk_widget_get_parent(GTK_WIDGET(self))); + int allocated = gtk_widget_get_allocated_height(GTK_WIDGET(self)); if (ceil(h * self->scale) > allocated) set_scale(self, allocated / h); } @@ -1095,6 +1201,15 @@ fiv_view_class_init(FivViewClass *klass) g_object_class_install_properties( object_class, N_PROPERTIES, view_properties); + g_object_class_override_property( + object_class, PROP_HADJUSTMENT, "hadjustment"); + g_object_class_override_property( + object_class, PROP_VADJUSTMENT, "vadjustment"); + g_object_class_override_property( + object_class, PROP_HSCROLL_POLICY, "hscroll-policy"); + g_object_class_override_property( + object_class, PROP_VSCROLL_POLICY, "vscroll-policy"); + view_signals[COMMAND] = g_signal_new_class_handler("command", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_CALLBACK(fiv_view_command), -- cgit v1.2.3-70-g09d2