from __future__ import annotations
from gi.repository import Gdk, Gtk
from gaphas.view import GtkView
class Zoom:
def __init__(self, matrix):
self.matrix = matrix
self.x0 = 0
self.y0 = 0
self.sx = 1.0
self.sy = 1.0
def begin(self, x0, y0):
self.x0 = x0
self.y0 = y0
self.sx = self.matrix[0]
self.sy = self.matrix[3]
def update(self, scale):
if self.sx * scale < 0.2:
scale = 0.2 / self.sx
elif self.sx * scale > 20.0:
scale = 20.0 / self.sx
m = self.matrix
sx = m[0]
sy = m[3]
ox = (m[4] - self.x0) / sx
oy = (m[5] - self.y0) / sy
dsx = self.sx * scale / sx
dsy = self.sy * scale / sy
m.translate(-ox, -oy)
m.scale(dsx, dsy)
m.translate(+ox, +oy)
def zoom_tools(
view: GtkView,
) -> tuple[Gtk.GestureZoom] | tuple[Gtk.GestureZoom, Gtk.EventControllerScroll]:
return (
(zoom_tool(view),)
if Gtk.get_major_version() == 3
else (zoom_tool(view), scroll_zoom_tool(view))
)
def on_begin(
gesture: Gtk.GestureZoom,
sequence: None,
zoom: Zoom,
) -> None:
_, x0, y0 = gesture.get_point(sequence)
zoom.begin(x0, y0)
def on_scale_changed(gesture: Gtk.GestureZoom, scale: float, zoom: Zoom) -> None:
zoom.update(scale)
def scroll_zoom_tool(view: GtkView) -> Gtk.EventControllerScroll:
"""Ctrl-scroll wheel zoom.
GTK4 only.
"""
ctrl = (
Gtk.EventControllerScroll.new(
view,
Gtk.EventControllerScrollFlags.BOTH_AXES,
)
if Gtk.get_major_version() == 3
else Gtk.EventControllerScroll.new(Gtk.EventControllerScrollFlags.BOTH_AXES)
)
ctrl.connect("scroll", on_scroll)
return ctrl
def on_scroll(controller, _dx, dy):
view = controller.get_widget()
modifiers = (
Gtk.get_current_event_state()[1]
if Gtk.get_major_version() == 3
else controller.get_current_event_state()
)
if not modifiers & Gdk.ModifierType.CONTROL_MASK:
return False
# Workaround: Gtk.EventController.get_current_event() causes SEGFAULT
view = controller.get_widget()
x = view.get_width() / 2
y = view.get_height() / 2
zoom = Zoom(view.matrix)
zoom.begin(x, y)
zoom_factor = 0.1
d = 1 - dy * zoom_factor
zoom.update(d)
return True