Migrate from GTK3 to GTK4#786
Open
rabin-io wants to merge 33 commits into
Open
Conversation
Migrate from GTK 3.0 to GTK 4.0 version requirements and update all trivial API changes: Container.add→append/set_child, pack_start→append, show_all removal, delete-event→close-request, connect_signals→manual connections, STYLE_CLASS constants→strings, get_toplevel→get_root, removed widgets (Arrow, HBox, IconSize), set_shadow_type removal, and HeaderBar/Button API updates. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Run gtk4-builder-tool simplify --3to4 on date_range.ui, edit_activity.ui, and preferences.ui. Then strip remaining deprecated elements: GtkAlignment→GtkBox, GtkButtonBox→GtkBox, remove shadow_type/border_width/padding/relief/use_stock/events properties, remove all <signal> elements (connected manually in Python), and remove <packing> blocks. Delete unused stats.ui (no Python code references it, targets GTK+ 2.16). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Menu system: Gtk.Menu/MenuItem → Gio.Menu + PopoverMenu with
window-scoped SimpleActions
- Dialogs: ReportChooserDialog rewritten to use Gtk.FileDialog
async API; MessageDialog uses named params; AboutDialog updated
- Styling: add_hint() replaced with set_placeholder_text(),
override_background_color → CSS provider, get_background_color →
lookup_color("theme_bg_color"), get_style().font_desc → default
- Cursors: CursorType enums → Cursor.new_from_name() strings
- Colors: Gdk.Color removed, RGBA-only in ColorUtils
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add SceneEvent dataclass shim to preserve event-passing interface for Scene/Sprite consumers without rewriting 40+ handler sites - Replace do_draw() override with set_draw_func() callback - Replace do_configure_event with draw_func width/height params - Replace set_events() + GTK3 event signals with GTK4 controllers: EventControllerMotion, GestureClick, EventControllerScroll, EventControllerKey - Remove GDK window tracking (_window/get_window/get_pointer); store mouse coords from motion controller instead - Replace IconTheme.get_default() with get_for_display() - Update Icon.load_icon to use lookup_icon() + file path - Fix Totals widget: enter/leave-notify → Scene on-mouse-over/out, style-updated → notify::css-classes - Fix Overview.on_key_press for EventControllerKey signature - Fix layout.py pointer query to use scene.mouse_x/y Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CmdLineEntry, TimeInput, TagsEntry: Replace Gtk.Window(POPUP) with Gtk.Popover, remove manual move()/resize() positioning - Replace key-press-event/focus-out-event/focus-in-event with EventControllerKey/EventControllerFocus on all Entry subclasses - Replace button-press-event on TreeView with GestureClick - Remove _parent_click_watcher pattern (Popover auto-dismisses) - Remove EntryCompletion from ActivityEntry and CategoryEntry (removed in GTK4; CmdLineEntry popup pattern is the replacement) - Update icon-press signal handlers for GTK4 signature (no event) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove GTK3 DnD (enable_model_drag_source/dest, TargetFlags, drag_data_get/received) — marked TODO for GTK4 DragSource/DropTarget - Replace button-press/release-event on tree lists with GestureClick - Replace key-press-event on tree lists with EventControllerKey - Replace focus-out-event on tags textarea with EventControllerFocus - Add EventControllerKey for preferences window (Ctrl+W, Escape) - Update all handler signatures for GTK4 controller params Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update Flatpak runtime to GNOME 47 (GTK4) - Update README: gir1.2-gtk-3.0 → gir1.2-gtk-4.0, Gtk3 → Gtk4 - Fix po/wscript shebang: python2 → python3 - Remove dead _test_label (GTK4 Label doesn't accept positional args) - Remove GtkEventBox from edit_activity.ui (replaced with GtkBox) - Remove AtkObject accessibility blocks from all UI files - Add tests/test_gtk4_smoke.py: verifies Scene, SceneEvent, and UI file loading work under GTK4 - All 45 tests pass (41 existing + 4 new) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Calendar: day-selected-double-click → day-selected, select_month → select_day(GLib.DateTime), get_date tuple → GLib.DateTime - facttree: has_toplevel_focus() → is_active() - Entry subclasses: remove parent=parent from __init__ (not writable in GTK4), append to parent container explicitly instead - tags.py Tag: gtk.Style() → pango.FontDescription(graphics._font_desc) - edit_activity: focus_in/out_event → EventControllerFocus - dayline: get_color(StateFlags.NORMAL) → get_color() - edit_activity: day_preview.set_child → .append (GtkBox not single-child) - preferences: remove dead DnD methods using removed GTK3 APIs - overview: register menu actions on app (not window, which lacks add_action) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- DayLine: guard against width=0 in on_enter_frame (first frame before layout) - Add margin-start/end/top/bottom to main containers in all UI files to replace the border_width/padding that was stripped from GtkAlignment wrappers during conversion - Remove leftover GtkEntryCompletion objects from edit_activity.ui - Remove dead DnD methods from preferences.py (used removed APIs) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The GtkEntryCompletion objects were removed but two entries still referenced them via <property name="completion">. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- TimeInput: remove GestureClick on Entry (interfered with text input), add secondary dropdown icon instead, fix icon-release signature (no event param in GTK4) - Calendar UI: remove invalid year/month/day/resize_toplevel properties (GTK4 Calendar uses GDateTime) - DayLine: guard against zero width on first frame Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Switch from icon-release to icon-press signal - Remove popup-on-focus-in (caused focus fight with Popover stealing focus → immediate focus-out → hide loop) - Popup now only shows via dropdown icon click Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use raw strings for regex patterns with \D and \s - Set key controller to CAPTURE phase so window-level shortcuts (Ctrl+Space, Ctrl+N, arrows) work even when a child has focus Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
datetime.utcfromtimestamp() is deprecated since Python 3.12. Replace with fromtimestamp(ts, tz=timezone.utc).replace(tzinfo=None) to get the same naive-UTC datetime the codebase expects. For .date() calls, use fromtimestamp(ts, tz=timezone.utc).date(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GTK4 Calendar.get_date() returns GLib.DateTime, not a (y,m,d) tuple. Use get_year()/get_month()/get_day_of_month() methods. Month is already 1-based in GLib.DateTime (unlike GTK3's 0-based). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Defer set_cursor(start_editing=True) to idle callback so the GestureClick gesture finishes processing before TreeView tries to enter edit mode. Use set_cursor instead of set_cursor_on_cell which is more reliable in GTK4. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All set_cursor_on_cell calls replaced with set_cursor + grab_focus deferred via GLib.idle_add. GTK4 TreeView requires the gesture to finish processing before editing can start. Applied consistently to: add button, edit button, double-click-to-edit, and F2 key handlers for both category and activity lists. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove per-tree EventControllerKey that was intercepting keystrokes meant for the CellEditable entry widget. Move Delete/F2 handling to the window-level key controller, which skips handling when a cell is in edit mode (editable=True). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GTK 4.22 has a bug where TreeView set_cursor(start_editing=True) triggers a css_node_insert_after assertion failure and never creates the CellEditable widget. Work around this by using a simple modal dialog with an Entry for all add/edit operations on categories and activities. Double-click and F2 also open the dialog instead of trying inline editing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The activity and category ScrolledWindows and their parent boxes needed hexpand=1 to fill the available space in the GtkPaned. Without it, GTK4 gave them minimum width, truncating text. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
get_allocation().width can be very small before full layout in GTK4, causing tags to wrap at tiny widths and show truncated. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Setting Tag.width=0 before creating the Label caused Label._bounds_width=0, making Pango wrap text at 0 pixels wide (one character per line). Remove the premature width/height=0 initialization — the correct size is set by __setattr__ when self.text is assigned. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The autocomplete tags text view and its parent containers needed hexpand to fill the available width. Without it, text wrapped at a few pixels wide showing one character per line. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EventControllerKey on the window intercepts ALL keystrokes in GTK4, preventing TextViews and other editable widgets from receiving input. Replace with ShortcutController that only handles specific key combinations (Ctrl+W, Escape, Delete, F2) and lets all other keys pass through to focused widgets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Avoid window-wide Delete shortcut intercepting text editing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Even ShortcutController on the window prevents the tags TextView from receiving keyboard input in GTK4. Remove all keyboard interception — the window can be closed via the title bar button. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The CAPTURE-phase EventControllerKey on the overview window was intercepting ALL keystrokes application-wide, preventing typing in the preferences text view and other windows. Replace with: - ShortcutController for Ctrl+shortcuts and Escape (only triggers on specific key combos, doesn't intercept normal typing) - BUBBLE-phase EventControllerKey for arrow/nav key forwarding to the fact tree (only when overview is active and filter entry doesn't have focus) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
In GTK4, can_focus=False on a parent container prevents grab_focus() from succeeding on ALL descendant widgets, unlike GTK3 where it only affected the container itself. This was the root cause of the tags text view not accepting keyboard input in preferences. Also removes empty <child internal-child="accessible"> ATK blocks that gtk4-builder-tool failed to clean up during migration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DrawingArea does not inherit the theme foreground color, so get_color() returns black regardless of theme. Use the root window's style context instead, and update colors on map to ensure the correct color is applied after the widget is in the tree. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
kiwisimosten-a11y
approved these changes
Jun 10, 2026
kiwisimosten-a11y
approved these changes
Jun 10, 2026
kiwisimosten-a11y
approved these changes
Jun 10, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Complete migration of Hamster Time Tracker from GTK3 to GTK4, covering all UI files, Python code, and the graphics/animation framework.
What's included
gtk4-builder-tool+ extensive manual cleanupgtk_css_node_insert_afterassertion bug)can_focus=Falseand emptyinternal-child="accessible"ATK blocks removed from all UI files (breaks descendant focus in GTK4)utcfromtimestampreplaced with timezone-aware equivalentKey GTK3→GTK4 behavioral differences discovered
can_focus=Falseon a container preventsgrab_focus()on ALL descendants — unlike GTK3 where it only affected the container itselfget_color()returns black regardless of theme; must use root window's style contextset_cursor(start_editing=True)triggers assertion failureNot included (deferred)
add_provider_for_screen→add_provider_for_display)test_round_triptest failure (missingdt.timezoneexport, unrelated to GTK4)Test plan
🤖 Generated with Claude Code