Translating GtkEventController coordinates

Im trying to create a Widget that uses a custom snapshot method to render a scaled child widget (a Gtk.TextView). Currently this is a simple as:

static void mdnotebook_zoomview_snapshot(GtkWidget* widget, GtkSnapshot* snapshot) {
	MdNotebookZoomView* self = MDNOTEBOOK_ZOOMVIEW(widget);
	MdNotebookZoomViewPrivate* priv = mdnotebook_zoomview_get_instance_private(self);

	gtk_snapshot_scale(snapshot, priv->zoom, priv->zoom);

	gtk_widget_snapshot_child(widget, priv->child, snapshot);
}

The idea behind this is to create a pinch to zoom behavior like is known from image viewers or mobile/modern-webbrowsers. I’m also very happy with the result in in of itself (obv. the font rendering is at some zoom factors a bit wonky, but surprisingly decent).

The problem I have now is, that I somehow need to figure out how to translate/transform the mouse/touch events back to the actual size of the text view, because otherwise the location of the pointer obv. doesn’t represent where the text cursor gets placed, or where drag events start/stop etc.

I’ve managed to add event handlers that trigger during capture, and then claim the event; so that the underlying Gtk.TextView doesn’t receive an event, however I’m unable to send any modified events back to the textview. (I’ve even tried building gtk4 myself, with default symbol visibility readded to try call gtk_text_view_gesture_pressed myself, however that doesn’t to anything either since it doesn’t used the passed x,y parameters anyway).

Is this transforming of coordinates somehow possible with GTK4 and if not, is there a possibility that such a feature request could be implemented?

1 Like

Hi, if you want to do arbitrary coordinate transformations in two directions, usually instead of using gtk_snapshot_scale, you would do something like this:

If you want a child widget transformed, just implement size_allocate instead and gtk_widget_allocate() your child widget with the GskTransform you want. That will handle the input coordinates automatically.

That sound promising, I will try that. Since a pinch to zoom
implementation will resize the widget very often during such a gesture,
I presume that It’d be more perfomat to use gtk_snapshot_scale during
it, and only call the gtk_widget_size_allocate after it completed
(btw, do I need to call some kind of deallocate method first? There
isn’t such a method available, but do I need to call unmap/unrealize?)
since malfunctioning input events aren’t an issue if the user is
preoccupied with “zooming” :).

So, I finally implemented it and played around with it some more. Something I’ve noticed with allocating a Gtk.TextView with a GskTransform is, that since there is the “Bin”-Widget that does the allocating and transforming between the Gtk.ScrolledWindow and the TextView.

Thus, by default the ScrolledWindow will put the “ZoomWidget” inside a Viewport. This results in the scrolling of the TextView working, since the the Viewport will just be whatever size the TextView is. However that leads the TextView to believe that it has infinite space and thus it won’t scroll the the view when the cursor is outside the viewable space since the view isn’t aware of the available space.

What is necessary is to “proxy” the Gtk.Scrollable interface from the TextView to the “ZoomWidget”. However this doesn’t seem just as easy as simply taking the “[hv]adjustment” property from the TextView each time the the get/set_property method is called, since this GtkAdjustment doesn’t appear to respect the GskTransformation the TextView was allocated with.
This means, I need to adopt Adjustment changes from the TextView, rescale them and put changes into a “ZoomWidget” specific Adjustment, that the ScrolledWindow will then use. When the “ZoomWidgets” adjustment value changes, it needs to be back-scaled for the TextView. I’ve tried to implement this, however I was so far unsuccessful doing it properly. (i.e. I never seem to be able to scroll fully to the bottom, etc.).

static void mdnotebook_zoomview_vadjustment_cb(GtkAdjustment* vadj, MdNotebookZoomView* self) {
	MdNotebookZoomViewPrivate* priv;
	g_return_if_fail(MDNOTEBOOK_IS_ZOOMVIEW(self));
	priv = mdnotebook_zoomview_get_instance_private(self);
	g_return_if_fail(GTK_IS_SCROLLABLE(priv->child));

	GtkAdjustment* childvadj = gtk_scrollable_get_vadjustment((GtkScrollable*)priv->child);
	gtk_adjustment_set_value(childvadj, gtk_adjustment_get_value(vadj) / priv->zoom);
}

void mdnotebook_zoomview_child_vadjustment_update(MdNotebookZoomView* self) {
	GtkAdjustment* childv;
	MdNotebookZoomViewPrivate* priv = mdnotebook_zoomview_get_instance_private(self);
	g_return_if_fail(GTK_IS_SCROLLABLE(priv->child));
	childv = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(priv->child));

	gtk_adjustment_set_value(priv->vadjustment, gtk_adjustment_get_value(childv) * priv->zoom);
	gtk_adjustment_set_lower(priv->vadjustment, gtk_adjustment_get_lower(childv) * priv->zoom);
	gtk_adjustment_set_upper(priv->vadjustment, gtk_adjustment_get_upper(childv) * priv->zoom);
	gtk_adjustment_set_page_size(priv->vadjustment, gtk_adjustment_get_page_size(childv));
	gtk_adjustment_set_page_increment(priv->vadjustment, gtk_adjustment_get_page_increment(childv));
	gtk_adjustment_set_step_increment(priv->vadjustment, gtk_adjustment_get_step_increment(childv));
}

where mdnotebook_zoomview_vadjustment_cb is connected to the “value-changed” of the "ZoomWidget"s vadjustment, and mdnotebook_zoomview_child_vadjustment_update gets called when the child TextView’s vadjustment emits “changed” or the “ZoomWidgets” zoom property gets changed.

Is there any way I could make this work, either by properly multiplications/divisions of the zoom (I haven’t fully understood Gtk.Scrollable interface yet) or is there maybe an altogether different method (maybe somehow appling the Transform onto the Adjustment aswell).

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.