From ba52a64c4abfa766e1756573d7562077127e4c19 Mon Sep 17 00:00:00 2001 From: Gabriel Fontes Date: Sat, 15 Aug 2026 16:28:50 -0300 Subject: [PATCH] fix(projects/khora): anchor events to timeline positions Use a fixed event overlay with pixel positions derived from time slots so lane layout cannot distort vertical placement. Assisted-by: pi (gpt-5.6-sol) --- projects/khora/khora/window.py | 51 ++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/projects/khora/khora/window.py b/projects/khora/khora/window.py index 8eeef25b..fddc0520 100644 --- a/projects/khora/khora/window.py +++ b/projects/khora/khora/window.py @@ -1,7 +1,6 @@ from __future__ import annotations import datetime as dt -import math import gi @@ -15,6 +14,7 @@ from .mini_calendar import MiniCalendar from .model import ( Calendar, Event, + EventPlacement, layout_event_lanes, month_grid_dates, period_label, @@ -33,6 +33,38 @@ AGENDA_CHUNK_DAYS = 14 MAX_EMPTY_AGENDA_CHUNKS = 6 +class EventLayer(Gtk.Fixed): + def __init__(self, slot_height: int) -> None: + super().__init__(hexpand=True, vexpand=True) + self._slot_height = slot_height + self._events: list[tuple[Gtk.Widget, EventPlacement]] = [] + self._last_width = -1 + self.add_tick_callback(self._layout_events) + + def add_event(self, widget: Gtk.Widget, placement: EventPlacement) -> None: + self._events.append((widget, placement)) + widget.set_size_request( + -1, + (placement.end_slot - placement.start_slot) * self._slot_height, + ) + self.put(widget, 0, placement.start_slot * self._slot_height) + + def _layout_events(self, _widget: Gtk.Widget, _frame_clock: Gdk.FrameClock) -> bool: + width = self.get_width() + if width <= 0 or width == self._last_width: + return GLib.SOURCE_CONTINUE + self._last_width = width + for widget, placement in self._events: + left = round(width * placement.lane / placement.lane_count) + right = round(width * (placement.lane + 1) / placement.lane_count) + widget.set_size_request( + max(1, right - left), + (placement.end_slot - placement.start_slot) * self._slot_height, + ) + self.move(widget, left, placement.start_slot * self._slot_height) + return GLib.SOURCE_CONTINUE + + class KhoraWindow(Adw.ApplicationWindow): def __init__( self, @@ -588,25 +620,14 @@ class KhoraWindow(Adw.ApplicationWindow): overlay = Gtk.Overlay(child=column) placements = layout_event_lanes(events, day) if placements: - lane_grid = math.lcm(*(placement.lane_count for placement in placements)) - event_layer = Gtk.Grid( - row_homogeneous=True, - column_homogeneous=True, - hexpand=True, - vexpand=True, - ) - event_layer.attach(Gtk.Box(), lane_grid - 1, 47, 1, 1) + event_layer = EventLayer(self._slot_height) for placement in placements: - lane_width = lane_grid // placement.lane_count - event_layer.attach( + event_layer.add_event( self._timed_event_button( placement.event, (placement.end_slot - placement.start_slot) * self._slot_height, ), - placement.lane * lane_width, - placement.start_slot, - lane_width, - placement.end_slot - placement.start_slot, + placement, ) overlay.add_overlay(event_layer) overlay.set_measure_overlay(event_layer, False)