From 6db7b7a5e52f067222267ca858089577ef6ddbf1 Mon Sep 17 00:00:00 2001 From: teamcons Date: Tue, 7 Jul 2026 20:34:42 +0200 Subject: [PATCH 01/12] add destructors and plug granites hypertextview --- po/POTFILES | 1 + src/Services/ColorController.vala | 2 +- src/Services/NoteManager.vala | 8 +- src/Services/ScribblyController.vala | 2 +- src/Services/ZoomController.vala | 2 +- src/Views/NoteView.vala | 4 +- src/Widgets/EditableLabel.vala | 4 + src/Widgets/HyperTextView.vala | 308 +++++++++++++++++++ src/Widgets/PopoverWidgets/ColorBox.vala | 4 + src/Widgets/PopoverWidgets/MonospaceBox.vala | 4 + src/Widgets/PopoverWidgets/ZoomBox.vala | 4 + src/Widgets/TextView.vala | 4 +- src/Windows/StickyNoteWindow.vala | 19 +- src/meson.build | 1 + 14 files changed, 349 insertions(+), 18 deletions(-) create mode 100644 src/Widgets/HyperTextView.vala diff --git a/po/POTFILES b/po/POTFILES index b8da5b7b..75146cf6 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -11,6 +11,7 @@ src/Views/PreferencesView.vala src/Widgets/ActionBar.vala src/Widgets/EditableLabel.vala src/Widgets/EditableLabel.vala +src/Widgets/HyperTextView.vala src/Widgets/Popover.vala src/Widgets/PopoverWidgets/ColorBox.vala src/Widgets/PopoverWidgets/MonospaceBox.vala diff --git a/src/Services/ColorController.vala b/src/Services/ColorController.vala index 1017e380..d60affd7 100644 --- a/src/Services/ColorController.vala +++ b/src/Services/ColorController.vala @@ -12,7 +12,7 @@ */ public class Jorts.ColorController : Object { - private weak Jorts.StickyNoteWindow window; + private unowned Jorts.StickyNoteWindow window; private Jorts.Themes _theme; public Jorts.Themes theme { diff --git a/src/Services/NoteManager.vala b/src/Services/NoteManager.vala index 63722179..362c6eea 100644 --- a/src/Services/NoteManager.vala +++ b/src/Services/NoteManager.vala @@ -16,7 +16,7 @@ public class Jorts.NoteManager : Object { private static NoteData? last_deleted = null; private Jorts.Application application; - public Gee.ArrayList open_notes; + public Gee.ArrayList open_notes; public Jorts.Storage storage; public NoteManager (Jorts.Application app) { @@ -24,7 +24,7 @@ public class Jorts.NoteManager : Object { } construct { - open_notes = new Gee.ArrayList (); + open_notes = new Gee.ArrayList (); storage = new Jorts.Storage (); } @@ -40,6 +40,7 @@ public class Jorts.NoteManager : Object { Json.Array loaded_data = storage.load (); if (loaded_data.get_length () == 0) { + var note_data = new NoteData (); note_data.theme = DEFAULT_THEME; @@ -103,6 +104,9 @@ public class Jorts.NoteManager : Object { application.remove_window ((Gtk.Window)note); note.close (); + note.dispose (); + note.destroy (); + note = null; immediately_save (); } diff --git a/src/Services/ScribblyController.vala b/src/Services/ScribblyController.vala index 7622331b..07e0e77a 100644 --- a/src/Services/ScribblyController.vala +++ b/src/Services/ScribblyController.vala @@ -13,7 +13,7 @@ public class Jorts.ScribblyController : Object { private const string STYLE_SCRIBBLED = "scribbled"; - private weak Jorts.StickyNoteWindow window; + private unowned Jorts.StickyNoteWindow window; private bool _scribble; public bool scribble { diff --git a/src/Services/ZoomController.vala b/src/Services/ZoomController.vala index e70d960b..f224a811 100644 --- a/src/Services/ZoomController.vala +++ b/src/Services/ZoomController.vala @@ -14,7 +14,7 @@ public class Jorts.ZoomController : Object { private static bool is_control_key_pressed = false; - private weak Jorts.StickyNoteWindow window {get; set;} + private unowned Jorts.StickyNoteWindow window {get; set;} // Avoid setting this unless it is to restore a specific value, do_set_zoom does not check input private int _old_zoom; diff --git a/src/Views/NoteView.vala b/src/Views/NoteView.vala index 850c8d30..27a44cd8 100644 --- a/src/Views/NoteView.vala +++ b/src/Views/NoteView.vala @@ -67,9 +67,6 @@ app.set_accels_for_action (ACTION_PREFIX + ACTION_SHOW_MENU, {"G", "O"}); app.set_accels_for_action (ACTION_PREFIX + ACTION_TOGGLE_MONO, {"m"}); - - - orientation = VERTICAL; spacing = 0; @@ -142,6 +139,7 @@ private void action_toggle_mono () {monospace = !monospace;} ~NoteView () { + actionbar.destroy (); debug ("Destroyed"); } } diff --git a/src/Widgets/EditableLabel.vala b/src/Widgets/EditableLabel.vala index 3bdac5fd..874c1d87 100644 --- a/src/Widgets/EditableLabel.vala +++ b/src/Widgets/EditableLabel.vala @@ -57,4 +57,8 @@ public class Jorts.EditableLabel : Granite.Bin { } remove_css_class (STYLE_MONOSPACE); } + + ~EditableLabel () { + debug ("Destroyed"); + } } diff --git a/src/Widgets/HyperTextView.vala b/src/Widgets/HyperTextView.vala new file mode 100644 index 00000000..f79fa220 --- /dev/null +++ b/src/Widgets/HyperTextView.vala @@ -0,0 +1,308 @@ +/* + * Copyright 2021 elementary, Inc. + * + * This program or library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +/** + * This class enables navigatable URLs in Gtk.TextView + * @since 6.1.3 + */ +public class Jorts.HyperTextView : Gtk.TextView { + + private const int FORCE_FULL_BUFFER_RESCAN_CHANGE_START_OFFSET = -1; + + private uint buffer_changed_debounce_timeout_id = 0; + private int buffer_cursor_position_when_change_started = 0; + + private GLib.HashTable uri_text_tags; + private Regex uri_regex; + private Gtk.EventControllerKey keypress_controller; + + private bool is_control_key_pressed = false; + + private int pointer_x; + private int pointer_y; + + construct { + var http_charset = "[\\w\\/\\-\\+\\.:@\\?&%=#]"; + var email_charset = "[\\w\\-\\.]"; + var email_tld_charset = "[\\w\\-]"; + + var http_match_str = @"https?:\\/\\/$(http_charset)+\\.$(http_charset)+"; + var email_match_str = @"(mailto:)?$(email_charset)+@$(email_charset)+\\.$(email_tld_charset)+"; + + var uri_regex_str = "(?:(" + + http_match_str + + ")|(" + + email_match_str + + "))"; + + uri_text_tags = new GLib.HashTable (str_hash, direct_equal); + try { + uri_regex = new Regex (uri_regex_str); + } catch (GLib.RegexError e) { + critical ("RegexError while constructing URI regex: %s", e.message); + } + + buffer_connect (buffer); + notify["buffer"].connect (on_buffer_changed); + + var motion_controller = new Gtk.EventControllerMotion (); + motion_controller.motion.connect (on_motion_notify_event); + add_controller (motion_controller); + + keypress_controller = new Gtk.EventControllerKey (); + keypress_controller.key_pressed.connect (on_key_press_event); + keypress_controller.key_released.connect (on_key_release_event); + + var click_controller = new Gtk.GestureClick (); + click_controller.pressed.connect (on_click_event); + add_controller (click_controller); + + var focus_controller = new Gtk.EventControllerFocus (); + focus_controller.leave.connect (on_focus_leave); + add_controller (focus_controller); + + realize.connect (on_realize); + } + + private void buffer_connect (Gtk.TextBuffer buffer) { + buffer.notify["cursor-position"].connect (on_buffer_cursor_position_changed); + buffer.paste_done.connect (on_paste_done); + buffer.changed.connect_after (on_after_buffer_changed); + } + + private void on_buffer_cursor_position_changed () { + if (buffer_cursor_position_when_change_started == 0) { + buffer_cursor_position_when_change_started = buffer.cursor_position; + } + } + + private void on_paste_done (Gdk.Clipboard clipboard) { + // force rescan of whole buffer: + buffer_cursor_position_when_change_started = FORCE_FULL_BUFFER_RESCAN_CHANGE_START_OFFSET; + } + + private void on_after_buffer_changed () { + if (buffer_changed_debounce_timeout_id != 0) { + Source.remove (buffer_changed_debounce_timeout_id); + buffer_changed_debounce_timeout_id = 0; + } + + buffer_changed_debounce_timeout_id = GLib.Timeout.add (300, debouncer); + } + + private async void update_tags_in_buffer_for_range (int buffer_start_offset, int buffer_end_offset) { + if (buffer_start_offset == buffer_end_offset) { + return; + } + + Gtk.TextIter buffer_start_iter, buffer_end_iter; + buffer.get_iter_at_offset (out buffer_start_iter, buffer_start_offset); + buffer_start_iter.backward_line (); + buffer_start_offset = buffer_start_iter.get_offset (); + + buffer.get_iter_at_offset (out buffer_end_iter, buffer_end_offset); + buffer_end_iter.forward_line (); + buffer_end_offset = buffer_end_iter.get_offset (); + + // Delete all tags in buffer for range [start_iter.offset,end_iter.offset] + lock (uri_text_tags) { + foreach (var tag_key in uri_text_tags.get_keys ()) { + int tag_start_offset, tag_end_offset; + tag_key.scanf ("[%i,%i]", out tag_start_offset, out tag_end_offset); + + if ( + tag_start_offset > buffer_start_offset && tag_start_offset < buffer_end_offset + || + tag_end_offset > buffer_start_offset && tag_end_offset < buffer_end_offset + ) { + buffer.tag_table.remove (uri_text_tags.take (tag_key)); + } + } + } + + /* + Character counts are usually referred to as offsets, while byte counts are called indexes. + If you confuse these two, things will work fine with ASCII, but as soon as your + buffer contains multibyte characters, bad things will happen. + https://developer.gnome.org/gtk3/stable/TextWidget.html + */ + var buffer_start_index = buffer.text.index_of_nth_char (buffer_start_offset); + var buffer_end_index = buffer.text.index_of_nth_char (buffer_end_offset); + var buffer_substring = buffer.text.substring (buffer_start_index, buffer_end_index - buffer_start_index); + + if (buffer_substring.strip () == "") { + // if the substring is empty, we do not have anything to do... + return; + } + + // Add new tags in buffer for range [start_iter.offset,end_iter.offset] + GLib.MatchInfo match_info; + uri_regex.match (buffer_substring, 0, out match_info); + + while (match_info.matches ()) { + string match_text = match_info.fetch (0); + + int match_start_index, match_end_index; + match_info.fetch_pos (0, out match_start_index, out match_end_index); + + int match_start_offset, match_end_offset; + match_start_offset = buffer_substring.substring (0, match_start_index).char_count (); + match_end_offset = buffer_substring.substring (0, match_end_index).char_count (); + + var buffer_match_start_offset = buffer_start_offset + match_start_offset; + var buffer_match_end_offset = buffer_start_offset + match_end_offset; + + Gtk.TextIter buffer_match_start_iter, buffer_match_end_iter; + buffer.get_iter_at_offset (out buffer_match_start_iter, buffer_match_start_offset); + buffer.get_iter_at_offset (out buffer_match_end_iter, buffer_match_end_offset); + + var tag = buffer.create_tag (null, "underline", Pango.Underline.SINGLE); + if (!match_text.contains ("://") && match_text.contains ("@") && !match_text.has_prefix ("mailto:")) { + match_text = "mailto:" + match_text; + } + tag.set_data ("uri", match_text); + buffer.apply_tag (tag, buffer_match_start_iter, buffer_match_end_iter); + + lock (uri_text_tags) { + uri_text_tags.set ("[%i,%i]".printf (buffer_match_start_offset, buffer_match_end_offset), tag); + } + + try { + match_info.next (); + } catch (GLib.RegexError e) { + warning ("RegexError while scanning for the next URI match: %s", e.message); + } + } + } + + private bool on_key_press_event (uint keyval, uint keycode, Gdk.ModifierType state) { + if (keyval == Gdk.Key.Control_L || keyval == Gdk.Key.Control_R) { + var uri_hovering_over = get_uri_at_location (pointer_x, pointer_y); + if (uri_hovering_over != null) { + set_cursor (new Gdk.Cursor.from_name ("pointer", null)); + } + + is_control_key_pressed = true; + } + + return Gdk.EVENT_PROPAGATE; + } + + private void on_key_release_event (uint keyval, uint keycode, Gdk.ModifierType state) { + if (keyval == Gdk.Key.Control_L || keyval == Gdk.Key.Control_R) { + set_cursor (new Gdk.Cursor.from_name ("text", null)); + is_control_key_pressed = false; + } + } + + private void on_click_event (int n_press, double x, double y) { + if (!is_control_key_pressed) { + return; + } + + var uri = get_uri_at_location ((int)x, (int)y); + if (uri == null) { + return; + } + + Gtk.show_uri (null, uri, Gdk.CURRENT_TIME); + set_cursor (new Gdk.Cursor.from_name ("text", null)); + is_control_key_pressed = false; + } + + private void on_motion_notify_event (double x, double y) { + pointer_x = (int)x; + pointer_y = (int)y; + + var uri_hovering_over = get_uri_at_location (pointer_x, pointer_y); + + if (uri_hovering_over != null && !has_tooltip) { + has_tooltip = true; + tooltip_markup = string.joinv ("\n", { + _("Follow Link"), + Granite.TOOLTIP_SECONDARY_TEXT_MARKUP.printf (_("Control + Click")) + }); + + } else if (uri_hovering_over == null && has_tooltip) { + has_tooltip = false; + } + } + + private string? get_uri_at_location (int location_x, int location_y) { + string? uri = null; + + int x, y; + window_to_buffer_coords (Gtk.TextWindowType.TEXT, location_x, location_y, out x, out y); + + Gtk.TextIter text_iter; + if (get_iter_at_location (out text_iter, x, y)) { + var tags = text_iter.get_tags (); + + foreach (var tag in tags) { + if (tag.get_data ("uri") != null) { + uri = tag.get_data ("uri"); + break; + } + } + } + + return uri; + } + + private void on_buffer_changed () { + buffer_connect (buffer); + buffer.changed (); + } + + private void on_realize () { + // Attach the keypress controller to the root so we can see Ctrl key presses + // even when the widget isn't focused + get_root ().add_controller (keypress_controller); + } + + private void on_focus_leave () { + set_cursor (new Gdk.Cursor.from_name ("text", null)); + is_control_key_pressed = false; + } + + private bool debouncer () { + buffer_changed_debounce_timeout_id = 0; + + var change_start_offset = buffer_cursor_position_when_change_started; + var change_end_offset = buffer.cursor_position; + + buffer_cursor_position_when_change_started = 0; + + if (change_start_offset == FORCE_FULL_BUFFER_RESCAN_CHANGE_START_OFFSET || change_start_offset == change_end_offset) { + change_start_offset = 0; + change_end_offset = buffer.text.length; + } + + update_tags_in_buffer_for_range.begin ( + int.min (change_start_offset, change_end_offset), + int.max (change_start_offset, change_end_offset) + ); + + return GLib.Source.REMOVE; + } + + ~HyperTextView () { + debug ("Destroyed"); + } +} \ No newline at end of file diff --git a/src/Widgets/PopoverWidgets/ColorBox.vala b/src/Widgets/PopoverWidgets/ColorBox.vala index 725d51af..ebb017a2 100644 --- a/src/Widgets/PopoverWidgets/ColorBox.vala +++ b/src/Widgets/PopoverWidgets/ColorBox.vala @@ -79,4 +79,8 @@ public class Jorts.ColorBox : Gtk.Box { theme_changed ((Jorts.Themes)color); } } + + ~ColorBox () { + debug ("Destroyed"); + } } diff --git a/src/Widgets/PopoverWidgets/MonospaceBox.vala b/src/Widgets/PopoverWidgets/MonospaceBox.vala index 5cdd3df1..09b4f916 100644 --- a/src/Widgets/PopoverWidgets/MonospaceBox.vala +++ b/src/Widgets/PopoverWidgets/MonospaceBox.vala @@ -64,4 +64,8 @@ public class Jorts.MonospaceBox : Gtk.Box { GLib.BindingFlags.BIDIRECTIONAL | GLib.BindingFlags.INVERT_BOOLEAN ); } + + ~MonospaceBox () { + debug ("Destroyed"); + } } diff --git a/src/Widgets/PopoverWidgets/ZoomBox.vala b/src/Widgets/PopoverWidgets/ZoomBox.vala index 4d32e14f..f07498d4 100644 --- a/src/Widgets/PopoverWidgets/ZoomBox.vala +++ b/src/Widgets/PopoverWidgets/ZoomBox.vala @@ -69,4 +69,8 @@ public class Jorts.ZoomBox : Gtk.Box { append (zoom_default_button); append (zoom_in_button); } + + ~ZoomBox () { + debug ("Destroyed"); + } } diff --git a/src/Widgets/TextView.vala b/src/Widgets/TextView.vala index dee5be67..d08f52ca 100644 --- a/src/Widgets/TextView.vala +++ b/src/Widgets/TextView.vala @@ -10,7 +10,7 @@ * Inherits Hypertextview to detect links and emails * Adds a list feature which is a hot mess */ -public class Jorts.TextView : Granite.HyperTextView { +public class Jorts.TextView : Jorts.HyperTextView { // We subclass the buffer to manage the list feature at a lower level // We need to keep a reference to its "Extended version" @@ -228,8 +228,6 @@ public class Jorts.TextView : Granite.HyperTextView { } ~TextView () { - list_buffer.dispose (); - buffer.dispose (); debug ("Destroyed"); } } diff --git a/src/Windows/StickyNoteWindow.vala b/src/Windows/StickyNoteWindow.vala index 6d89ccf4..d6f6f9d0 100644 --- a/src/Windows/StickyNoteWindow.vala +++ b/src/Windows/StickyNoteWindow.vala @@ -102,10 +102,6 @@ public class Jorts.StickyNoteWindow : Gtk.ApplicationWindow { scroll_controller.scroll.connect (zoom_controller.on_scroll); gesturezoom_controller.scale_changed.connect (zoom_controller.on_pinch); - - - debug ("Built UI. Lets do connects and binds"); - // Save when title or text have changed view.editablelabel.changed.connect (on_editable_changed); view.textview.buffer.changed.connect (has_changed); @@ -129,11 +125,14 @@ public class Jorts.StickyNoteWindow : Gtk.ApplicationWindow { * This is more for the Aesthetic */ private void delayed_show () { + debug ("delayed show"); Timeout.add_once (250, bind_hidebar); show.disconnect (delayed_show); } private void bind_hidebar () { + debug ("Bind hidebar"); + Application.settings.bind ( KEY_HIDEBAR, view.actionbar.actionbar, @@ -221,8 +220,14 @@ public class Jorts.StickyNoteWindow : Gtk.ApplicationWindow { view.textview.buffer.changed.disconnect (has_changed); popover.theme_changed.disconnect (color_controller.on_color_changed); - color_controller.dispose (); - zoom_controller.dispose (); - scribbly_controller.dispose (); + + view = null; + popover = null; + textview = null; + + color_controller = null; + zoom_controller = null; + scribbly_controller = null; + application = null; } } diff --git a/src/meson.build b/src/meson.build index 8695f126..f23255eb 100644 --- a/src/meson.build +++ b/src/meson.build @@ -37,6 +37,7 @@ sources = files ( 'Services' / 'ColorController.vala', 'Widgets' / 'EditableLabel.vala', + 'Widgets' / 'HyperTextView.vala', 'Widgets' / 'TextView.vala', 'Widgets' / 'ActionBar.vala', 'Widgets' / 'TextBuffer.vala', From 7faee9d9054c4a508d9d7934da57002609e9a439 Mon Sep 17 00:00:00 2001 From: teamcons Date: Tue, 7 Jul 2026 20:56:04 +0200 Subject: [PATCH 02/12] linter --- src/Widgets/HyperTextView.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Widgets/HyperTextView.vala b/src/Widgets/HyperTextView.vala index f79fa220..24d4f408 100644 --- a/src/Widgets/HyperTextView.vala +++ b/src/Widgets/HyperTextView.vala @@ -305,4 +305,4 @@ public class Jorts.HyperTextView : Gtk.TextView { ~HyperTextView () { debug ("Destroyed"); } -} \ No newline at end of file +} From ecce733407546640fa30a6dcc831aabf9abf7c22 Mon Sep 17 00:00:00 2001 From: teamcons Date: Wed, 15 Jul 2026 14:25:35 +0200 Subject: [PATCH 03/12] move main outside so we get space for env variables --- src/Application.vala | 10 ---------- src/main.vala | 27 +++++++++++++++++++++++++++ src/meson.build | 1 + 3 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 src/main.vala diff --git a/src/Application.vala b/src/Application.vala index 87b994bb..1387888d 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -90,12 +90,6 @@ public class Jorts.Application : Gtk.Application { Intl.bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); Intl.textdomain (GETTEXT_PACKAGE); - -#if DEVEL - //GLib.Environment.set_variable ("LANGUAGE", "C", true); - GLib.Environment.set_variable ("GTK_DEBUG", "interactive", true); - //print (LOCALEDIR); -#endif } /*************************************************/ @@ -194,10 +188,6 @@ Please wait while the app remembers all the things… if (show_pref) {action_show_preferences (); show_pref = false;} } - public static int main (string[] args) { - return new Application ().run (args); - } - private void action_show_preferences () { debug ("Showing preferences!"); diff --git a/src/main.vala b/src/main.vala new file mode 100644 index 00000000..a1fe561b --- /dev/null +++ b/src/main.vala @@ -0,0 +1,27 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2017-2024 Lains + * 2025 Contributions from the ellie_Commons community (github.com/ellie-commons/) + * 2025-2026 Stella & Charlie (teamcons.carrd.co) + */ + +int main (string[] args) { + +#if DEVEL + + //GLib.Environment.set_variable ("LANGUAGE", "C", true); + GLib.Environment.set_variable ("GTK_DEBUG", "interactive", true); + //print (LOCALEDIR); + + warning (""" + ---------------------------------------- + You are running a development version. + Here be dragons. + Tread carefully. + ---------------------------------------- + """); + +#endif + + return new Jorts.Application ().run (args); +} diff --git a/src/meson.build b/src/meson.build index f23255eb..2e50b13b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -56,6 +56,7 @@ sources = files ( 'Windows' / 'StickyNoteWindow.vala', 'Constants.vala', + 'main.vala', 'Application.vala', ) From 383c6865d217cffb1cbf7298db5afbe346b0c50d Mon Sep 17 00:00:00 2001 From: teamcons Date: Wed, 15 Jul 2026 15:17:51 +0200 Subject: [PATCH 04/12] ActionBar: handle the delay_show itself --- src/Widgets/ActionBar.vala | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/Widgets/ActionBar.vala b/src/Widgets/ActionBar.vala index a35baf7c..0cd9da29 100644 --- a/src/Widgets/ActionBar.vala +++ b/src/Widgets/ActionBar.vala @@ -19,7 +19,8 @@ public Gtk.WindowHandle handle; public Jorts.Popover popover; - const int ICON_SIZE = 32; + const int ICON_SIZE = 32; // mobile needs bigger + const int REVEAL_DELAY = 250; // in ms construct { @@ -111,12 +112,46 @@ child = handle; + + /***************************************************/ + /* CONNECTS AND BINDS */ + /***************************************************/ + // Randomize-skip emoji icon emojichooser_popover.show.connect (on_emoji_popover); // Hide the list button if user has specified no list item symbol on_prefix_changed (); Application.settings.changed[KEY_LIST].connect (on_prefix_changed); + + // Respect animation settings for showing ui elements + if (Application.gtk_settings.gtk_enable_animations && !Application.settings.get_boolean (KEY_HIDEBAR)) { + actionbar.revealed = false; + realize.connect_after (delayed_show); + + } else { + bind_hidebar (); + } + } + + /** + * Show Actionbar shortly after the window is shown + * This is more for the Aesthetic + */ + private void delayed_show () { + debug ("delayed show"); + Timeout.add_once (REVEAL_DELAY, bind_hidebar); + realize.disconnect (delayed_show); + } + + private void bind_hidebar () { + debug ("Bind hidebar"); + + Application.settings.bind ( + KEY_HIDEBAR, + actionbar, + "revealed", + SettingsBindFlags.INVERT_BOOLEAN); } /** From e32e1827c78abf9620cdec974612fcf34506d52c Mon Sep 17 00:00:00 2001 From: teamcons Date: Wed, 15 Jul 2026 16:00:00 +0200 Subject: [PATCH 05/12] Implement a Zoomable middleman --- src/Interfaces/Zoomable.vala | 18 ++++ src/Views/NoteView.vala | 17 +++- src/Widgets/Popover.vala | 8 +- src/Widgets/PopoverWidgets/ZoomBox.vala | 6 +- .../ZoomedWindow.vala} | 81 +++++++++++----- src/Windows/StickyNoteWindow.vala | 92 ++++--------------- src/meson.build | 4 +- 7 files changed, 116 insertions(+), 110 deletions(-) create mode 100644 src/Interfaces/Zoomable.vala rename src/{Services/ZoomController.vala => Widgets/ZoomedWindow.vala} (69%) diff --git a/src/Interfaces/Zoomable.vala b/src/Interfaces/Zoomable.vala new file mode 100644 index 00000000..e9d89fa4 --- /dev/null +++ b/src/Interfaces/Zoomable.vala @@ -0,0 +1,18 @@ +/* + * SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2017-2024 Lains + * 2025 Contributions from the ellie_Commons community (github.com/ellie-commons/) + * 2025-2026 Stella & Charlie (teamcons.carrd.co) + */ + + // vala-lint=skip-file + +/*************************************************/ +/** +* An object used to package all data conveniently as needed. +*/ +public interface Jorts.Zoomable: Gtk.Widget { + + public abstract void on_zoom_changed (int new_zoom); + +} diff --git a/src/Views/NoteView.vala b/src/Views/NoteView.vala index 27a44cd8..ddd6078c 100644 --- a/src/Views/NoteView.vala +++ b/src/Views/NoteView.vala @@ -5,7 +5,7 @@ * 2025-2026 Stella & Charlie (teamcons.carrd.co) */ - public class Jorts.NoteView : Gtk.Box { + public class Jorts.NoteView : Gtk.Box, Zoomable { public Gtk.HeaderBar headerbar; public Jorts.EditableLabel editablelabel; public Jorts.TextView textview; @@ -138,6 +138,21 @@ private void action_show_menu () {menu_button.activate ();} private void action_toggle_mono () {monospace = !monospace;} + public void on_zoom_changed (int new_zoom) { + textview.queue_refresh_indentation (); + + // Adapt headerbar size to avoid weird flickering + headerbar.height_request = Jorts.Zoom.from_int (new_zoom).to_ui_size (); + + // Reflect the number in the popover + popover.zoom = new_zoom; + + // Keep it for next new notes + NoteData.latest_zoom = new_zoom; + + //window.has_changed (); + } + ~NoteView () { actionbar.destroy (); debug ("Destroyed"); diff --git a/src/Widgets/Popover.vala b/src/Widgets/Popover.vala index 19ed1532..a07544d3 100644 --- a/src/Widgets/Popover.vala +++ b/src/Widgets/Popover.vala @@ -39,10 +39,10 @@ public class Jorts.Popover : Gtk.Popover { } static construct { - add_binding_action (Gdk.Key.plus, Gdk.ModifierType.CONTROL_MASK, ZoomController.ACTION_PREFIX + ZoomController.ACTION_ZOOM_IN, null); - add_binding_action (Gdk.Key.equal, Gdk.ModifierType.CONTROL_MASK, ZoomController.ACTION_PREFIX + ZoomController.ACTION_ZOOM_DEFAULT, null); - add_binding_action (48, Gdk.ModifierType.CONTROL_MASK, ZoomController.ACTION_PREFIX + ZoomController.ACTION_ZOOM_DEFAULT, null); - add_binding_action (Gdk.Key.minus, Gdk.ModifierType.CONTROL_MASK, ZoomController.ACTION_PREFIX + ZoomController.ACTION_ZOOM_OUT, null); + add_binding_action (Gdk.Key.plus, Gdk.ModifierType.CONTROL_MASK, ZoomedWindow.ACTION_PREFIX + ZoomedWindow.ACTION_ZOOM_IN, null); + add_binding_action (Gdk.Key.equal, Gdk.ModifierType.CONTROL_MASK, ZoomedWindow.ACTION_PREFIX + ZoomedWindow.ACTION_ZOOM_DEFAULT, null); + add_binding_action (48, Gdk.ModifierType.CONTROL_MASK, ZoomedWindow.ACTION_PREFIX + ZoomedWindow.ACTION_ZOOM_DEFAULT, null); + add_binding_action (Gdk.Key.minus, Gdk.ModifierType.CONTROL_MASK, ZoomedWindow.ACTION_PREFIX + ZoomedWindow.ACTION_ZOOM_OUT, null); add_binding_action (Gdk.Key.n, Gdk.ModifierType.CONTROL_MASK, Application.ACTION_PREFIX + Application.ACTION_NEW, null); add_binding_action (Gdk.Key.w, Gdk.ModifierType.CONTROL_MASK, StickyNoteWindow.ACTION_PREFIX + StickyNoteWindow.ACTION_DELETE, null); diff --git a/src/Widgets/PopoverWidgets/ZoomBox.vala b/src/Widgets/PopoverWidgets/ZoomBox.vala index f07498d4..8a50a00f 100644 --- a/src/Widgets/PopoverWidgets/ZoomBox.vala +++ b/src/Widgets/PopoverWidgets/ZoomBox.vala @@ -42,7 +42,7 @@ public class Jorts.ZoomBox : Gtk.Box { //TRANSLATORS: These are displayed on small linked buttons in a menu. User can click them to change zoom var zoom_out_button = new Gtk.Button.from_icon_name ("zoom-out-symbolic") { - action_name = ZoomController.ACTION_PREFIX + ZoomController.ACTION_ZOOM_OUT, + action_name = ZoomedWindow.ACTION_PREFIX + ZoomedWindow.ACTION_ZOOM_OUT, tooltip_markup = Granite.markup_accel_tooltip ( {"minus", "KP_Subtract"}, _("Zoom out") @@ -50,7 +50,7 @@ public class Jorts.ZoomBox : Gtk.Box { }; zoom_default_button = new Gtk.Button () { - action_name = ZoomController.ACTION_PREFIX + ZoomController.ACTION_ZOOM_DEFAULT, + action_name = ZoomedWindow.ACTION_PREFIX + ZoomedWindow.ACTION_ZOOM_DEFAULT, tooltip_markup = Granite.markup_accel_tooltip ( { "equal", "0", "KP_0" }, _("Default zoom level") @@ -58,7 +58,7 @@ public class Jorts.ZoomBox : Gtk.Box { }; var zoom_in_button = new Gtk.Button.from_icon_name ("zoom-in-symbolic") { - action_name = ZoomController.ACTION_PREFIX + ZoomController.ACTION_ZOOM_IN, + action_name = ZoomedWindow.ACTION_PREFIX + ZoomedWindow.ACTION_ZOOM_IN, tooltip_markup = Granite.markup_accel_tooltip ( {"plus", "KP_Add"}, _("Zoom in") diff --git a/src/Services/ZoomController.vala b/src/Widgets/ZoomedWindow.vala similarity index 69% rename from src/Services/ZoomController.vala rename to src/Widgets/ZoomedWindow.vala index f224a811..90a8565c 100644 --- a/src/Services/ZoomController.vala +++ b/src/Widgets/ZoomedWindow.vala @@ -5,16 +5,17 @@ * 2025-2026 Stella & Charlie (teamcons.carrd.co) */ -/*************************************************/ /** -* Responsible to apply zoom appropriately to a window. -* Mainly, this abstracts zoom into an int and swap CSS classes -* As a treat it includes also the plumbing for ctrl+scroll zooming +* A */ -public class Jorts.ZoomController : Object { + public class Jorts.ZoomedWindow : Gtk.Widget { + // Scroll handers need that information to decide whether to act private static bool is_control_key_pressed = false; - private unowned Jorts.StickyNoteWindow window {get; set;} + + private Gtk.EventControllerKey keypress_controller; + private Gtk.EventControllerScroll scroll_controller; + private Gtk.GestureZoom gesturezoom_controller; // Avoid setting this unless it is to restore a specific value, do_set_zoom does not check input private int _old_zoom; @@ -24,7 +25,7 @@ public class Jorts.ZoomController : Object { } public SimpleActionGroup actions { get; construct; } - public const string ACTION_PREFIX = "zoom_controller."; + public const string ACTION_PREFIX = "zoomed_window."; public const string ACTION_ZOOM_OUT = "action_zoom_out"; public const string ACTION_ZOOM_DEFAULT = "action_zoom_default"; public const string ACTION_ZOOM_IN = "action_zoom_in"; @@ -37,9 +38,32 @@ public class Jorts.ZoomController : Object { { ACTION_ZOOM_IN, zoom_in} }; + class construct { + set_layout_manager_type (typeof (Gtk.BinLayout)); + } + + private Zoomable? _child; + public Zoomable? child { + get { + return _child; + } + + set { + if (value != null && value.get_parent () != null) { + critical ("Tried to set a widget as child that already has a parent."); + return; + } - public ZoomController (Jorts.StickyNoteWindow window) { - this.window = window; + if (_child != null) { + _child.unparent (); + } + + _child = value; + + if (_child != null) { + _child.set_parent (this); + } + } } construct { @@ -50,6 +74,21 @@ public class Jorts.ZoomController : Object { app.set_accels_for_action (ACTION_PREFIX + ACTION_ZOOM_OUT, {"minus", "KP_Subtract"}); app.set_accels_for_action (ACTION_PREFIX + ACTION_ZOOM_DEFAULT, {"equal", "0", "KP_0"}); app.set_accels_for_action (ACTION_PREFIX + ACTION_ZOOM_IN, {"plus", "KP_Add"}); + + keypress_controller = new Gtk.EventControllerKey (); + scroll_controller = new Gtk.EventControllerScroll (VERTICAL) { + propagation_phase = Gtk.PropagationPhase.CAPTURE + }; + gesturezoom_controller = new Gtk.GestureZoom (); + + add_controller (keypress_controller); + add_controller (scroll_controller); + add_controller (gesturezoom_controller); + + keypress_controller.key_pressed.connect (on_key_press_event); + keypress_controller.key_released.connect (on_key_release_event); + scroll_controller.scroll.connect (on_scroll); + gesturezoom_controller.scale_changed.connect (on_pinch); } /** @@ -102,22 +141,11 @@ public class Jorts.ZoomController : Object { debug ("Setting zoom: " + zoom.to_string ()); // Switches the classes that control font size - window.remove_css_class (Jorts.Zoom.from_int ( _old_zoom).to_css_class ()); + remove_css_class (Jorts.Zoom.from_int ( _old_zoom).to_css_class ()); _old_zoom = new_zoom; - window.add_css_class (Jorts.Zoom.from_int ( new_zoom).to_css_class ()); - window.textview.queue_refresh_indentation (); - - - // Adapt headerbar size to avoid weird flickering - window.view.headerbar.height_request = Jorts.Zoom.from_int (new_zoom).to_ui_size (); + add_css_class (Jorts.Zoom.from_int ( new_zoom).to_css_class ()); - // Reflect the number in the popover - window.popover.zoom = new_zoom; - - // Keep it for next new notes - NoteData.latest_zoom = zoom; - - window.has_changed (); + _child.on_zoom_changed (new_zoom); } public bool on_key_press_event (uint keyval, uint keycode, Gdk.ModifierType state) { @@ -161,8 +189,11 @@ public class Jorts.ZoomController : Object { //return Gdk.EVENT_STOP; } - ~ZoomController () { + ~ZoomedWindow () { + if (_child != null) { + _child.unparent (); + } + debug ("Destroyed"); - window = null; } } diff --git a/src/Windows/StickyNoteWindow.vala b/src/Windows/StickyNoteWindow.vala index d6f6f9d0..02525c68 100644 --- a/src/Windows/StickyNoteWindow.vala +++ b/src/Windows/StickyNoteWindow.vala @@ -19,12 +19,10 @@ public class Jorts.StickyNoteWindow : Gtk.ApplicationWindow { public Popover popover; public TextView textview; + private Jorts.ZoomedWindow zoomed_window; private Jorts.ColorController color_controller; - public Jorts.ZoomController zoom_controller; private Jorts.ScribblyController scribbly_controller; - private Gtk.EventControllerKey keypress_controller; - private Gtk.EventControllerScroll scroll_controller; - private Gtk.GestureZoom gesturezoom_controller; + public NoteData data { owned get {return packaged ();} @@ -50,40 +48,26 @@ public class Jorts.StickyNoteWindow : Gtk.ApplicationWindow { app.set_accels_for_action (ACTION_PREFIX + ACTION_DELETE, {"W"}); color_controller = new Jorts.ColorController (this); - zoom_controller = new Jorts.ZoomController (this); scribbly_controller = new Jorts.ScribblyController (this); - keypress_controller = new Gtk.EventControllerKey (); - scroll_controller = new Gtk.EventControllerScroll (VERTICAL) { - propagation_phase = Gtk.PropagationPhase.CAPTURE - }; - - gesturezoom_controller = new Gtk.GestureZoom (); - - - ((Gtk.Widget)this).add_controller (keypress_controller); - ((Gtk.Widget)this).add_controller (scroll_controller); - ((Gtk.Widget)this).add_controller (gesturezoom_controller); - // The view has its own titlebar titlebar = new Gtk.Grid () {visible = false}; view = new NoteView (); - textview = view.textview; - insert_action_group ("noteview", view.actions); - insert_action_group ("textview", textview.actions); - insert_action_group ("zoom_controller", zoom_controller.actions); - // Have shortcuts keep working this.destroy ()with the popover open. + zoomed_window = new ZoomedWindow () { + child = view + }; + + textview = view.textview; popover = view.popover; - view.popover.scroll_controller.scroll.connect (zoom_controller.on_scroll); - view.popover.keypress_controller.key_pressed.connect (zoom_controller.on_key_press_event); - view.popover.keypress_controller.key_released.connect (zoom_controller.on_key_release_event); - //zoom_controller.notify["zoom"].connect_after (textview.refresh_indentation); + insert_action_group ("noteview", view.actions); + insert_action_group ("textview", textview.actions); + insert_action_group ("zoomed_window", zoomed_window.actions); - set_child (view); - set_focus (view); + set_child (zoomed_window); + set_focus (zoomed_window); load_data (data); #if DEVEL @@ -96,48 +80,11 @@ public class Jorts.StickyNoteWindow : Gtk.ApplicationWindow { /* CONNECTS AND BINDS */ /***************************************************/ - // We need this for Ctr + Scroll. We delegate everything to zoomcontroller - keypress_controller.key_pressed.connect (zoom_controller.on_key_press_event); - keypress_controller.key_released.connect (zoom_controller.on_key_release_event); - scroll_controller.scroll.connect (zoom_controller.on_scroll); - gesturezoom_controller.scale_changed.connect (zoom_controller.on_pinch); - // Save when title or text have changed view.editablelabel.changed.connect (on_editable_changed); view.textview.buffer.changed.connect (has_changed); popover.theme_changed.connect (color_controller.on_color_changed); - - // Respect animation settings for showing ui elements - if (Application.gtk_settings.gtk_enable_animations && (!Application.settings.get_boolean (KEY_HIDEBAR))) { - show.connect_after (delayed_show); - - } else { - bind_hidebar (); - } - } - - /********************************************/ - /* METHODS */ - /********************************************/ - - /** - * Show Actionbar shortly after the window is shown - * This is more for the Aesthetic - */ - private void delayed_show () { - debug ("delayed show"); - Timeout.add_once (250, bind_hidebar); - show.disconnect (delayed_show); - } - - private void bind_hidebar () { - debug ("Bind hidebar"); - - Application.settings.bind ( - KEY_HIDEBAR, - view.actionbar.actionbar, - "revealed", - SettingsBindFlags.INVERT_BOOLEAN); + zoomed_window.notify ["zoom"].connect (has_changed); } /** @@ -170,7 +117,7 @@ public class Jorts.StickyNoteWindow : Gtk.ApplicationWindow { theme = popover.color, content = view.content, monospace = popover.monospace, - zoom = zoom_controller.zoom, + zoom = zoomed_window.zoom, width = this_width, height = this_height }; @@ -196,7 +143,7 @@ public class Jorts.StickyNoteWindow : Gtk.ApplicationWindow { view.content = data.content; color_controller.theme = data.theme; - zoom_controller.zoom = data.zoom; + zoomed_window.zoom = data.zoom; view.monospace = data.monospace; } @@ -210,23 +157,16 @@ public class Jorts.StickyNoteWindow : Gtk.ApplicationWindow { ~StickyNoteWindow () { debug ("Destroying %s", view.title); - - keypress_controller.key_pressed.disconnect (zoom_controller.on_key_press_event); - keypress_controller.key_released.disconnect (zoom_controller.on_key_release_event); - scroll_controller.scroll.disconnect (zoom_controller.on_scroll); - gesturezoom_controller.scale_changed.disconnect (zoom_controller.on_pinch); - view.editablelabel.changed.disconnect (on_editable_changed); view.textview.buffer.changed.disconnect (has_changed); popover.theme_changed.disconnect (color_controller.on_color_changed); - + zoomed_window = null; view = null; popover = null; textview = null; color_controller = null; - zoom_controller = null; scribbly_controller = null; application = null; } diff --git a/src/meson.build b/src/meson.build index 2e50b13b..93673634 100644 --- a/src/meson.build +++ b/src/meson.build @@ -22,6 +22,8 @@ dependencies = [ #================================ sources = files ( + 'Interfaces' / 'Zoomable.vala', + 'Objects' / 'ZoomType.vala', 'Objects' / 'Zoom.vala', 'Objects' / 'Themes.vala', @@ -32,7 +34,6 @@ sources = files ( 'Services' / 'Storage.vala', 'Services' / 'NoteManager.vala', - 'Services' / 'ZoomController.vala', 'Services' / 'ScribblyController.vala', 'Services' / 'ColorController.vala', @@ -41,6 +42,7 @@ sources = files ( 'Widgets' / 'TextView.vala', 'Widgets' / 'ActionBar.vala', 'Widgets' / 'TextBuffer.vala', + 'Widgets' / 'ZoomedWindow.vala', 'Widgets' / 'PopoverWidgets' / 'ColorPill.vala', 'Widgets' / 'PopoverWidgets' / 'ColorBox.vala', From 4ebf58b377aeabbc4e8c3b05491eb26cbec7cdd5 Mon Sep 17 00:00:00 2001 From: teamcons Date: Wed, 15 Jul 2026 16:03:44 +0200 Subject: [PATCH 06/12] docs --- src/Widgets/ZoomedWindow.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Widgets/ZoomedWindow.vala b/src/Widgets/ZoomedWindow.vala index 90a8565c..0dbdba19 100644 --- a/src/Widgets/ZoomedWindow.vala +++ b/src/Widgets/ZoomedWindow.vala @@ -6,7 +6,7 @@ */ /** -* A +* Copying the ScrolledWindow. This wraps a child and holds a zoom property */ public class Jorts.ZoomedWindow : Gtk.Widget { From 14ae67654d0ac18bfcecafaf0ff13c0d16ea7b7d Mon Sep 17 00:00:00 2001 From: teamcons Date: Wed, 15 Jul 2026 16:14:41 +0200 Subject: [PATCH 07/12] connect popover with zoomedwindow --- src/Widgets/Popover.vala | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/Widgets/Popover.vala b/src/Widgets/Popover.vala index a07544d3..7f41ed75 100644 --- a/src/Widgets/Popover.vala +++ b/src/Widgets/Popover.vala @@ -11,9 +11,6 @@ */ public class Jorts.Popover : Gtk.Popover { - public Gtk.EventControllerKey keypress_controller; - public Gtk.EventControllerScroll scroll_controller; - private Jorts.ColorBox color_box; private Jorts.MonospaceBox monospace_box; private Jorts.ZoomBox font_size_box; @@ -70,17 +67,30 @@ public class Jorts.Popover : Gtk.Popover { child = view; - // Allow scrolling shenanigans from popover - keypress_controller = new Gtk.EventControllerKey (); - scroll_controller = new Gtk.EventControllerScroll (VERTICAL) { + // Propagate settings changes to the higher level + color_box.theme_changed.connect ((theme) => {theme_changed (theme);}); + + // Allow zooming shenanigans from popover + ((Gtk.Widget)this).realize.connect (on_realize); + } + + private void on_realize () { + var zoomed_window = (ZoomedWindow)(this.get_ancestor (typeof (ZoomedWindow))); + + var keypress_controller = new Gtk.EventControllerKey (); + var scroll_controller = new Gtk.EventControllerScroll (VERTICAL) { propagation_phase = Gtk.PropagationPhase.CAPTURE }; + var gesturezoom_controller = new Gtk.GestureZoom (); ((Gtk.Widget)this).add_controller (keypress_controller); ((Gtk.Widget)this).add_controller (scroll_controller); + ((Gtk.Widget)this).add_controller (gesturezoom_controller); - // Propagate settings changes to the higher level - color_box.theme_changed.connect ((theme) => {theme_changed (theme);}); + keypress_controller.key_pressed.connect (zoomed_window.on_key_press_event); + keypress_controller.key_released.connect (zoomed_window.on_key_release_event); + scroll_controller.scroll.connect (zoomed_window.on_scroll); + gesturezoom_controller.scale_changed.connect (zoomed_window.on_pinch); } /** From ef1246c8afb7c10e6ad780e8ced05e1b80151330 Mon Sep 17 00:00:00 2001 From: teamcons Date: Fri, 17 Jul 2026 22:53:27 +0200 Subject: [PATCH 08/12] Zoomable: document a tad --- src/Interfaces/Zoomable.vala | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Interfaces/Zoomable.vala b/src/Interfaces/Zoomable.vala index e9d89fa4..6dced7b0 100644 --- a/src/Interfaces/Zoomable.vala +++ b/src/Interfaces/Zoomable.vala @@ -5,14 +5,21 @@ * 2025-2026 Stella & Charlie (teamcons.carrd.co) */ - // vala-lint=skip-file - /*************************************************/ /** -* An object used to package all data conveniently as needed. -*/ -public interface Jorts.Zoomable: Gtk.Widget { - - public abstract void on_zoom_changed (int new_zoom); + * Implemented by childs of ZoomedWindow + */ +public interface Jorts.Zoomable : Gtk.Widget { + /** + * Called by ZoomedWindow after changing zoom value. + * + * The default implementation does nothing. + * + * If you need to do some changes, such as display the new value, or refresh some displayed elements, override this. + */ + public virtual void on_zoom_changed (int new_zoom) { + debug ("Zoom changed: %i", new_zoom); + return; + } } From d59a259d3cfd96995c6a3b67a582a8bde9f9e09c Mon Sep 17 00:00:00 2001 From: teamcons Date: Sat, 18 Jul 2026 19:52:35 +0200 Subject: [PATCH 09/12] mark as unowned, just in case --- src/Services/NoteManager.vala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Services/NoteManager.vala b/src/Services/NoteManager.vala index 362c6eea..990e6779 100644 --- a/src/Services/NoteManager.vala +++ b/src/Services/NoteManager.vala @@ -15,7 +15,7 @@ public class Jorts.NoteManager : Object { private static bool saving_lock = true; private static NoteData? last_deleted = null; - private Jorts.Application application; + private unowned Jorts.Application application; public Gee.ArrayList open_notes; public Jorts.Storage storage; @@ -135,7 +135,7 @@ public class Jorts.NoteManager : Object { public void immediately_save () { var array = new Json.Array (); - foreach (Jorts.StickyNoteWindow note in open_notes) { + foreach (unowned Jorts.StickyNoteWindow note in open_notes) { var data = note.packaged (); var object = data.to_json (); array.add_object_element (object); From 5b12db45b435041f84f04dc92543ef0b57d58f8f Mon Sep 17 00:00:00 2001 From: teamcons Date: Sat, 25 Jul 2026 15:50:27 +0200 Subject: [PATCH 10/12] Meson: do debugs by default --- io.github.ellie_commons.jorts.flathub.yml | 3 +-- io.github.elly_code.jorts.devel.yml | 2 +- meson.build | 10 +++++++--- src/main.vala | 4 ++++ src/meson.build | 1 - 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/io.github.ellie_commons.jorts.flathub.yml b/io.github.ellie_commons.jorts.flathub.yml index 338e823d..4d1bef08 100644 --- a/io.github.ellie_commons.jorts.flathub.yml +++ b/io.github.ellie_commons.jorts.flathub.yml @@ -20,7 +20,6 @@ cleanup: - /share/gir-1.0 - /share/vala -# Cleanup development-related files inherited from the BaseApp to reduce package size of your app. cleanup-commands: - /app/cleanup-BaseApp.sh @@ -29,8 +28,8 @@ modules: buildsystem: meson config-opts: - --buildtype=release - - -Dlegacy_rdnn=true - -Dicon_variant=default + - -Dlegacy_rdnn=true sources: - type: dir path: . diff --git a/io.github.elly_code.jorts.devel.yml b/io.github.elly_code.jorts.devel.yml index 22e80ad1..44a86105 100644 --- a/io.github.elly_code.jorts.devel.yml +++ b/io.github.elly_code.jorts.devel.yml @@ -24,8 +24,8 @@ modules: buildsystem: meson config-opts: - --buildtype=debug - - -Ddevelopment=true - -Dicon_variant=default + - -Ddevelopment=true sources: - type: dir path: . diff --git a/meson.build b/meson.build index b88d99e1..cd503592 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'jorts', - ['c', 'vala'], # The floor is vala + ['c', 'vala'], # The floor is vala license: 'GPL-3.0-or-later', version: '4.3.0', # Versioning: first should follow Gtk version, second and third depending how proud you are of the update meson_version: '>= 1.1' # Use meson.option instead of meson_options.txt @@ -48,7 +48,12 @@ endif gnome = import('gnome') i18n = import('i18n') -add_global_arguments('-DGETTEXT_PACKAGE="@0@"'.format (app_id), language:'c') +add_project_arguments( + '-DGETTEXT_PACKAGE="@0@"'.format (app_id), # + '-DG_LOG_DOMAIN="@0@"'.format (app_id), # Dev builds add the app_id to G_MESSAGES_DEBUG + '-w', # Because of Vala we get ten thousand warnings we can do nothing about + language:'c' + ) # Play nice with clang @@ -84,7 +89,6 @@ build_machine_system = build_machine.system() if build_machine_system == 'windows' windows = true - endif # Here is to adjust the project accordingly to stuff diff --git a/src/main.vala b/src/main.vala index a1fe561b..fa9ee293 100644 --- a/src/main.vala +++ b/src/main.vala @@ -9,8 +9,12 @@ int main (string[] args) { #if DEVEL + // Do not overwrite environment, just show our apps debugs by default on devel builds + var new_messages_domain = "%s %s".printf (Environment.get_variable ("G_MESSAGES_DEBUG"), APP_ID); + //GLib.Environment.set_variable ("LANGUAGE", "C", true); GLib.Environment.set_variable ("GTK_DEBUG", "interactive", true); + GLib.Environment.set_variable ("G_MESSAGES_DEBUG", new_messages_domain, true); //print (LOCALEDIR); warning (""" diff --git a/src/meson.build b/src/meson.build index 93673634..c589361e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -100,7 +100,6 @@ executable( config_file, sources, dependencies: dependencies, - c_args: '-w', # Because of Vala we get ten thousand warnings we can do nothing about win_subsystem: development ? 'console' : 'windows', # Show a console in windows dev builds. Seem to not bother standard builds install : true ) From e9d5ec278d11b5cdb604d8ee8b964b6362a4b6ee Mon Sep 17 00:00:00 2001 From: teamcons Date: Sat, 25 Jul 2026 22:35:31 +0200 Subject: [PATCH 11/12] a --- data/icons/meson.build | 9 +++------ data/meson.build | 15 +++++++-------- meson.build | 14 +++++++------- windows/meson.build | 4 ++-- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/data/icons/meson.build b/data/icons/meson.build index f8e01f72..b74bf65d 100644 --- a/data/icons/meson.build +++ b/data/icons/meson.build @@ -17,13 +17,13 @@ foreach i : icon_sizes icon_variant / 'hicolor' / i + '.png', install_dir: datadir / 'icons' / 'hicolor' / i + 'x' + i / 'apps', rename: app_id + '.png' - ) + ) install_data( icon_variant / 'hicolor@2' / i + '@2.png', install_dir: datadir / 'icons' / 'hicolor' / i + 'x' + i + '@2' / 'apps', rename: app_id + '.png' - ) + ) endforeach @@ -31,7 +31,4 @@ install_data( icon_variant / 'scalable.svg', install_dir: datadir / 'icons' / 'scalable' / 'apps', rename: app_id + '.svg' -) - - - + ) diff --git a/data/meson.build b/data/meson.build index 9fca466c..c97cd90d 100644 --- a/data/meson.build +++ b/data/meson.build @@ -9,13 +9,13 @@ gresource_file = configure_file( input: app_name + '.gresource.xml.in', output: app_id + '.gresource.xml', configuration: config_data, -) + ) gresource = gnome.compile_resources( 'gresource', gresource_file, source_dir: '.' -) + ) #======================== @@ -30,7 +30,7 @@ gschema_file = configure_file( install_data( gschema_file, install_dir: datadir / 'glib-2.0' / 'schemas' -) + ) #======================== @@ -62,7 +62,7 @@ desktop_file_in = configure_file( input: app_name + '.desktop.in.in', output: app_id + '.desktop.in', configuration: config_data, -) + ) desktop_file = i18n.merge_file( input: desktop_file_in, @@ -71,7 +71,7 @@ desktop_file = i18n.merge_file( type: 'desktop', install: true, install_dir: datadir / 'applications', -) + ) #======================== @@ -82,7 +82,7 @@ appstream_file_in = configure_file( input: app_name + '.metainfo.xml.in.in', output: app_id + '.metainfo.xml.in', configuration: config_data, -) + ) appstream_file = i18n.merge_file( input: appstream_file_in, @@ -91,5 +91,4 @@ appstream_file = i18n.merge_file( type: 'xml', install: true, install_dir: datadir / 'metainfo', -) - + ) diff --git a/meson.build b/meson.build index cd503592..b3e4dc14 100644 --- a/meson.build +++ b/meson.build @@ -4,7 +4,7 @@ project( license: 'GPL-3.0-or-later', version: '4.3.0', # Versioning: first should follow Gtk version, second and third depending how proud you are of the update meson_version: '>= 1.1' # Use meson.option instead of meson_options.txt -) + ) app_name = meson.project_name() app_version = meson.project_version() @@ -49,9 +49,9 @@ endif gnome = import('gnome') i18n = import('i18n') add_project_arguments( - '-DGETTEXT_PACKAGE="@0@"'.format (app_id), # - '-DG_LOG_DOMAIN="@0@"'.format (app_id), # Dev builds add the app_id to G_MESSAGES_DEBUG - '-w', # Because of Vala we get ten thousand warnings we can do nothing about + '-DGETTEXT_PACKAGE="@0@"'.format (app_id), # + '-DG_LOG_DOMAIN="@0@"'.format (app_id), # Dev builds add the app_id to G_MESSAGES_DEBUG + '-w', # Because of Vala we get ten thousand warnings we can do nothing about language:'c' ) @@ -120,8 +120,8 @@ config_data = configuration_data( 'PUBLISHER': publisher, 'ALTERNATE_APP_ID': alternate_app_id, 'LOCALEDIR': prefix / get_option('localedir') - } -) + } + ) subdir('po') subdir('data') @@ -138,4 +138,4 @@ gnome.post_install( glib_compile_schemas: true, gtk_update_icon_cache: true, update_desktop_database: true -) + ) diff --git a/windows/meson.build b/windows/meson.build index d1f9ffb3..4d64f1d4 100644 --- a/windows/meson.build +++ b/windows/meson.build @@ -9,7 +9,7 @@ config_installer_script = configure_file( input: 'installer.nsi.in', output: 'installer.nsi', configuration: config_data -) + ) #======================== # DEPLOY @@ -18,4 +18,4 @@ config_deploy_script = configure_file( input: 'deploy.sh.in', output: 'deploy.sh', configuration: config_data -) + ) From d23cee31f1f1ef10d84a4f682a63e9815117f03d Mon Sep 17 00:00:00 2001 From: teamcons Date: Sun, 26 Jul 2026 00:11:06 +0200 Subject: [PATCH 12/12] Move the greeter --- src/Application.vala | 10 ---------- src/main.vala | 10 ++++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Application.vala b/src/Application.vala index 1387888d..49aca57a 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -134,16 +134,6 @@ public class Jorts.Application : Gtk.Application { ); }); - print (""" -🎉✨ ACTIVATING: SUPER COOL JORTS 😎🔥❗🎶🤌 -Your Notes are all belong to us! - _ _ - (\o/) (\o/) <--- Tiny electric angels working in the background - /_\ /_\ - -Please wait while the app remembers all the things… -"""); - /* Quit if all sticky notes are closed and preferences arent shown */ window_removed.connect (check_if_quit); diff --git a/src/main.vala b/src/main.vala index fa9ee293..4d3c06fd 100644 --- a/src/main.vala +++ b/src/main.vala @@ -27,5 +27,15 @@ int main (string[] args) { #endif + print (""" +🎉✨ ACTIVATING: SUPER COOL JORTS 😎🔥❗🎶🤌 +Your Notes are all belong to us! + _ _ + (\o/) (\o/) <--- Tiny electric angels working in the background + /_\ /_\ + +Please wait while the app remembers all the things… +"""); + return new Jorts.Application ().run (args); }