Last active
December 22, 2024 14:18
-
-
Save SpikedPaladin/29be3bfa8fba7e1d4701d0eec508c0ad to your computer and use it in GitHub Desktop.
Zoomable Widget in GTK4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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