Implement GtkScrollable in FivView

This fixes rendering and positioning behaviour when dragging on X11,
where we aim to use a native GdkWindow.
This commit is contained in:
Přemysl Eric Janouch 2022-07-16 16:49:47 +02:00
parent ca57c2632a
commit 23429d9631
Signed by: p
GPG Key ID: A0420B94F92B9493
2 changed files with 147 additions and 33 deletions

View File

@ -198,12 +198,12 @@ relayout(FivBrowser *self, int width)
if (self->vadjustment) { if (self->vadjustment) {
gtk_adjustment_set_lower(self->vadjustment, 0); gtk_adjustment_set_lower(self->vadjustment, 0);
gtk_adjustment_set_upper(self->vadjustment, total_height); 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, gtk_adjustment_set_step_increment(self->vadjustment,
self->item_height + self->item_spacing + 2 * self->item_border_y); 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; return total_height;
} }
@ -765,8 +765,7 @@ static void
on_adjustment_value_changed( on_adjustment_value_changed(
G_GNUC_UNUSED GtkAdjustment *adjustment, gpointer user_data) G_GNUC_UNUSED GtkAdjustment *adjustment, gpointer user_data)
{ {
FivBrowser *self = FIV_BROWSER(user_data); gtk_widget_queue_draw(GTK_WIDGET(user_data));
gtk_widget_queue_draw(GTK_WIDGET(self));
} }
static gboolean static gboolean

View File

@ -50,6 +50,10 @@ fiv_view_command_get_type(void)
struct _FivView { struct _FivView {
GtkWidget parent_instance; 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 *messages; ///< Image load information
gchar *uri; ///< Path to the current image (if any) gchar *uri; ///< Path to the current image (if any)
@ -73,7 +77,8 @@ struct _FivView {
gulong frame_update_connection; ///< GdkFrameClock::update 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 { typedef struct _Dimensions {
double width, height; double width, height;
@ -128,7 +133,13 @@ enum {
PROP_CAN_ANIMATE, PROP_CAN_ANIMATE,
PROP_HAS_PREVIOUS_PAGE, PROP_HAS_PREVIOUS_PAGE,
PROP_HAS_NEXT_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]; static GParamSpec *view_properties[N_PROPERTIES];
@ -141,6 +152,75 @@ enum {
// Globals are, sadly, the canonical way of storing signal numbers. // Globals are, sadly, the canonical way of storing signal numbers.
static guint view_signals[LAST_SIGNAL]; 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 static void
fiv_view_finalize(GObject *gobject) fiv_view_finalize(GObject *gobject)
{ {
@ -152,6 +232,9 @@ fiv_view_finalize(GObject *gobject)
g_free(self->uri); g_free(self->uri);
g_free(self->messages); 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); G_OBJECT_CLASS(fiv_view_parent_class)->finalize(gobject);
} }
@ -199,6 +282,19 @@ fiv_view_get_property(
g_value_set_boolean(value, self->page && g_value_set_boolean(value, self->page &&
cairo_surface_get_user_data(self->page, &fiv_io_key_page_next)); cairo_surface_get_user_data(self->page, &fiv_io_key_page_next));
break; 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: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); 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)) if (self->enhance != g_value_get_boolean(value))
fiv_view_command(self, FIV_VIEW_COMMAND_TOGGLE_ENHANCE); fiv_view_command(self, FIV_VIEW_COMMAND_TOGGLE_ENHANCE);
break; 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: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); 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 static void
fiv_view_get_preferred_height(GtkWidget *widget, gint *minimum, gint *natural) 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); FivView *self = FIV_VIEW(widget);
if (!self->image || !self->scale_to_fit) if (!self->image || !self->scale_to_fit)
return; goto out;
Dimensions surface_dimensions = get_surface_dimensions(self); Dimensions surface_dimensions = get_surface_dimensions(self);
double scale = 1; 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]); g_object_notify_by_pspec(G_OBJECT(widget), view_properties[PROP_SCALE]);
prescale_page(self); prescale_page(self);
} }
out:
update_adjustments(self);
} }
// https://www.freedesktop.org/wiki/OpenIcc/ICC_Profiles_in_X_Specification_0.4 // 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 x = 0;
double y = 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) if (w < allocation.width)
x = round((allocation.width - w) / 2.); x = round((allocation.width - w) / 2.);
if (h < allocation.height) if (h < allocation.height)
@ -554,8 +662,7 @@ static void
set_scale_to_fit_width(FivView *self) set_scale_to_fit_width(FivView *self)
{ {
double w = get_surface_dimensions(self).width; double w = get_surface_dimensions(self).width;
int allocated = gtk_widget_get_allocated_width( int allocated = gtk_widget_get_allocated_width(GTK_WIDGET(self));
gtk_widget_get_parent(GTK_WIDGET(self)));
if (ceil(w * self->scale) > allocated) if (ceil(w * self->scale) > allocated)
set_scale(self, allocated / w); set_scale(self, allocated / w);
} }
@ -564,8 +671,7 @@ static void
set_scale_to_fit_height(FivView *self) set_scale_to_fit_height(FivView *self)
{ {
double h = get_surface_dimensions(self).height; double h = get_surface_dimensions(self).height;
int allocated = gtk_widget_get_allocated_height( int allocated = gtk_widget_get_allocated_height(GTK_WIDGET(self));
gtk_widget_get_parent(GTK_WIDGET(self)));
if (ceil(h * self->scale) > allocated) if (ceil(h * self->scale) > allocated)
set_scale(self, allocated / h); set_scale(self, allocated / h);
} }
@ -1095,6 +1201,15 @@ fiv_view_class_init(FivViewClass *klass)
g_object_class_install_properties( g_object_class_install_properties(
object_class, N_PROPERTIES, view_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] = view_signals[COMMAND] =
g_signal_new_class_handler("command", G_TYPE_FROM_CLASS(klass), g_signal_new_class_handler("command", G_TYPE_FROM_CLASS(klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_CALLBACK(fiv_view_command), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_CALLBACK(fiv_view_command),