Skip to content

Instantly share code, notes, and snippets.

@SpikedPaladin
Last active December 22, 2024 14:18
Show Gist options
  • Save SpikedPaladin/29be3bfa8fba7e1d4701d0eec508c0ad to your computer and use it in GitHub Desktop.
Save SpikedPaladin/29be3bfa8fba7e1d4701d0eec508c0ad to your computer and use it in GitHub Desktop.
Zoomable Widget in GTK4
public class ZoomableView : Gtk.Widget, Gtk.Scrollable {
private Gtk.Adjustment _hadjustment;
private Gtk.Adjustment _vadjustment;
public Gtk.Adjustment hadjustment {
get {
return _hadjustment;
}
set construct {
_hadjustment = value;
if (_hadjustment != null)
_hadjustment.value_changed.connect(() => queue_allocate());
}
}
public Gtk.Adjustment vadjustment {
get { return _vadjustment; }
set construct {
_vadjustment = value;
if (_vadjustment != null)
_vadjustment.value_changed.connect(() => queue_allocate());
}
}
public Gtk.ScrollablePolicy hscroll_policy { get; set; }
public Gtk.ScrollablePolicy vscroll_policy { get; set; }
public float zoom_factor { get; set; default = 1; }
public float custom_width { get; set; default = 1000; }
public float custom_height { get; set; default = 1000; }
public Graphene.Point last_drag_point = { 0, 0 };
construct {
overflow = Gtk.Overflow.HIDDEN;
var button = new Gtk.Button.with_label("Test button");
button.set_parent(this);
var scroll_controller = new Gtk.EventControllerScroll(Gtk.EventControllerScrollFlags.BOTH_AXES);
scroll_controller.scroll.connect((dx, dy) => {
var event = scroll_controller.get_current_event();
if (event.get_modifier_state() == Gdk.ModifierType.CONTROL_MASK) {
zoom_factor = (float) (zoom_factor + (0.1 * -dy));
queue_allocate();
return true;
}
queue_allocate();
return false;
});
add_controller(scroll_controller);
var drag_controller = new Gtk.GestureDrag() {
button = Gdk.BUTTON_MIDDLE
};
drag_controller.drag_begin.connect(() => {
set_cursor(new Gdk.Cursor.from_name("grabbing", null));
last_drag_point = { 0, 0 };
});
drag_controller.drag_update.connect((x, y) => {
var old_x = last_drag_point.x;
var old_y = last_drag_point.y;
last_drag_point = { (float) x, (float) y };
var delta_x = old_x - x;
var delta_y = old_y - y;
hadjustment.value = hadjustment.value + delta_x;
vadjustment.value = vadjustment.value + delta_y;
});
drag_controller.drag_end.connect(() => {
set_cursor(null);
});
add_controller(drag_controller);
}
public Gsk.Transform screen_transform() {
return new Gsk.Transform()
.translate({ (float) (-hadjustment.value), (float) (-vadjustment.value) })
.scale(zoom_factor, zoom_factor);
}
public override void size_allocate(int width, int height, int baseline) {
var x = hadjustment.value;
var y = vadjustment.value;
for (var child = get_first_child(); child != null; child = child.get_next_sibling()) {
Gtk.Requisition _, natural_size;
child.get_preferred_size(out _, out natural_size);
child.allocate(
natural_size.width,
natural_size.height,
baseline,
screen_transform()
.translate({ 50, 50 })
);
}
hadjustment.configure(
x,
0,
double.max(width, custom_height * zoom_factor),
0.1 * width,
0.9 * width,
width
);
vadjustment.configure(
y,
0,
double.max(height, custom_width * zoom_factor),
0.1 * height,
0.9 * height,
height
);
}
public bool get_border(out Gtk.Border border) {
border = {};
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment