From 342a8ad9d902f1c5d94f89ca9feb5e9ec7841032 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Wed, 2 Aug 2023 03:29:06 +0300 Subject: [PATCH 01/76] feat(BookWyrmPage): use builder files --- data/gresource.xml | 1 + data/ui/widgets/bookwyrmpage.ui | 86 +++++++++++++++++++++++++ src/Widgets/BookWyrmPage.vala | 108 +++++++++++--------------------- 3 files changed, 125 insertions(+), 70 deletions(-) create mode 100644 data/ui/widgets/bookwyrmpage.ui diff --git a/data/gresource.xml b/data/gresource.xml index 6045ee205..68dc0b471 100644 --- a/data/gresource.xml +++ b/data/gresource.xml @@ -78,6 +78,7 @@ ui/views/sidebar/view.ui ui/views/sidebar/account.ui ui/views/sidebar/item.ui + ui/widgets/bookwyrmpage.ui ui/widgets/status.ui ui/widgets/votebox.ui ui/dialogs/new_account.ui diff --git a/data/ui/widgets/bookwyrmpage.ui b/data/ui/widgets/bookwyrmpage.ui new file mode 100644 index 000000000..cfd956eee --- /dev/null +++ b/data/ui/widgets/bookwyrmpage.ui @@ -0,0 +1,86 @@ + + + + + diff --git a/src/Widgets/BookWyrmPage.vala b/src/Widgets/BookWyrmPage.vala index 3b5c2cf32..ecd6e7e27 100644 --- a/src/Widgets/BookWyrmPage.vala +++ b/src/Widgets/BookWyrmPage.vala @@ -1,70 +1,46 @@ +[GtkTemplate (ui = "/dev/geopjr/Tuba/ui/widgets/bookwyrmpage.ui")] public class Tuba.Widgets.BookWyrmPage : Gtk.Box { public API.BookWyrm book { get; private set; } - private Gtk.Picture cover; + + [GtkChild] unowned Gtk.Picture cover; + [GtkChild] unowned Gtk.Label title; + [GtkChild] unowned Gtk.Label authors; + [GtkChild] unowned Gtk.Label isbn; + [GtkChild] unowned Gtk.Button ol_btn; + [GtkChild] unowned Gtk.Label description; + [GtkChild] unowned Gtk.Label date; // setting labels as selectable before they // are visible, causes them to be already // selected when they become visible - private Gtk.Label [] selectable_labels = {}; public bool selectable { set { - foreach (var label in selectable_labels) { - label.selectable = value; - } + title.selectable = + isbn.selectable = + description.selectable = + value; } } - construct { - orientation = Gtk.Orientation.VERTICAL; - spacing = 12; - margin_bottom = 12; - cover = new Gtk.Picture () { - height_request = 200, - css_classes = { "attachment-picture" } - }; - - #if GTK_4_8 - cover.set_property ("content-fit", 2); - #endif - } ~BookWyrmPage () { message ("Destroying BookWyrmPage"); - selectable_labels = {}; } public BookWyrmPage (API.BookWyrm t_obj) { book = t_obj; + title.label = t_obj.title; - var header_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 32); if (t_obj.cover != null && t_obj.cover.url != null && t_obj.cover.url != "") { - header_box.append (cover); image_cache.request_paintable (t_obj.cover.url, on_cache_response); if (t_obj.cover.name != "") { cover.alternative_text = cover.tooltip_text = t_obj.cover.name; } + } else { + cover.visible = false; } - var title_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 6) { - valign = Gtk.Align.CENTER, - hexpand = true - }; - var book_title = new Gtk.Label (t_obj.title) { - wrap = true, - css_classes = { "title-1" }, - halign = Gtk.Align.START - }; - selectable_labels += book_title; - title_box.append (book_title); - if (t_obj.authors != null && t_obj.authors.size > 0) { - Gtk.Label author_label = new Gtk.Label ("") { - wrap = true, - use_markup = true, - halign = Gtk.Align.START - }; - title_box.insert_child_after (author_label, book_title); - foreach (var author in t_obj.authors) { new Request.GET (@"$author.json") .then ((sess, msg, in_stream) => { @@ -72,54 +48,44 @@ public class Tuba.Widgets.BookWyrmPage : Gtk.Box { var node = network.parse_node (parser); var author_obj = API.BookWyrmAuthor.from (node); if (author_obj.id == author) { - author_label.label = generate_authors_label (author_obj.name, author_obj.id); + authors.label = generate_authors_label (author_obj.name, author_obj.id); } }) + .on_error (() => authors.visible = false) .exec (); } + } else { + authors.visible = false; } if (t_obj.isbn13 != "") { - var isbn_label = new Gtk.Label (@"ISBN: $(t_obj.isbn13)") { - wrap=true, - halign = Gtk.Align.START - }; - selectable_labels += isbn_label; - title_box.append (isbn_label); + isbn.label = @"ISBN: $(t_obj.isbn13)"; + } else { + isbn.visible = false; } - header_box.append (title_box); - append (header_box); - - var btn_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 12); - - var bw_btn = new Gtk.Button.with_label ("BookWyrm"); - bw_btn.clicked.connect (open_on_bw); - btn_box.append (bw_btn); - - if (t_obj.openlibraryKey != "") { - var ol_btn = new Gtk.Button.with_label ("OpenLibrary"); - ol_btn.clicked.connect (open_on_openlibrary); - btn_box.append (ol_btn); + if (t_obj.openlibraryKey == "") { + ol_btn.visible = false; } - append (btn_box); - if (t_obj.description != "") { - var description_label = new Gtk.Label (HtmlUtils.remove_tags (t_obj.description)) { - wrap = true, - css_classes = { "card", "bkwm-desc" } - }; - selectable_labels += description_label; - append (description_label); + description.label = HtmlUtils.remove_tags (t_obj.description); + } else { + description.visible = false; } if (t_obj.publishedDate != "") { var date_parsed = new GLib.DateTime.from_iso8601 (t_obj.publishedDate, null); date_parsed = date_parsed.to_timezone (new TimeZone.local ()); - if (date_parsed != null) - append (new Gtk.Label (@"Published: $(date_parsed.format("%x"))") { wrap=true }); + if (date_parsed != null) { + // translators: the variable is the date the book was published + date.label = _("Published: %s").printf (date_parsed.format ("%x")); + } else { + date.visible = false; + } + } else { + date.visible = false; } } @@ -143,10 +109,12 @@ public class Tuba.Widgets.BookWyrmPage : Gtk.Box { cover.paintable = data; } + [GtkCallback] void open_on_openlibrary () { Host.open_uri (@"https://openlibrary.org/books/$(book.openlibraryKey)"); } + [GtkCallback] void open_on_bw () { Host.open_uri (book.id); } From 8f0af7d8d39c936a3ceb586e2efe554b55ff798a Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Wed, 2 Aug 2023 03:33:13 +0300 Subject: [PATCH 02/76] feat(Background): use cover instead --- data/ui/views/profile_header.ui | 2 +- src/Dialogs/ProfileEdit.vala | 7 ++- src/Views/Profile.vala | 2 +- src/Widgets/Background.vala | 97 +++------------------------------ 4 files changed, 14 insertions(+), 94 deletions(-) diff --git a/data/ui/views/profile_header.ui b/data/ui/views/profile_header.ui index 4cab9f105..7d534b007 100644 --- a/data/ui/views/profile_header.ui +++ b/data/ui/views/profile_header.ui @@ -55,7 +55,7 @@ - + 1 1 224 diff --git a/src/Dialogs/ProfileEdit.vala b/src/Dialogs/ProfileEdit.vala index d57dde8f4..e7a2767b5 100644 --- a/src/Dialogs/ProfileEdit.vala +++ b/src/Dialogs/ProfileEdit.vala @@ -114,7 +114,7 @@ public class Tuba.Dialogs.ProfileEdit : Adw.Window { private GtkSource.View bio_text_view { get; set; } #endif private Adw.PreferencesGroup fields_box { get; set; } - private Widgets.Background background { get; set; } + private Gtk.Picture background { get; set; } Gtk.FileFilter filter = new Gtk.FileFilter () { name = _("All Supported Files") @@ -283,9 +283,10 @@ public class Tuba.Dialogs.ProfileEdit : Adw.Window { }; background_edit_button.clicked.connect (on_header_button_clicked); - background = new Widgets.Background () { + background = new Gtk.Picture () { + content_fit = Gtk.ContentFit.COVER, + css_classes = { "background-cover" }, height_request = 128, - css_classes = { "background-cover" } }; var background_overlay = new Gtk.Overlay () { diff --git a/src/Views/Profile.vala b/src/Views/Profile.vala index 81d66994e..b448f802a 100644 --- a/src/Views/Profile.vala +++ b/src/Views/Profile.vala @@ -88,7 +88,7 @@ public class Tuba.Views.Profile : Views.Timeline { [GtkTemplate (ui = "/dev/geopjr/Tuba/ui/views/profile_header.ui")] protected class Cover : Box { - [GtkChild] unowned Widgets.BackgroundWrapper background; + [GtkChild] unowned Widgets.Background background; [GtkChild] unowned Label cover_badge; [GtkChild] unowned Image cover_bot_badge; [GtkChild] unowned Box cover_badge_box; diff --git a/src/Widgets/Background.vala b/src/Widgets/Background.vala index 794d62626..293d14622 100644 --- a/src/Widgets/Background.vala +++ b/src/Widgets/Background.vala @@ -1,97 +1,16 @@ -using Gdk; - -public class Tuba.Widgets.BackgroundWrapper : Gtk.Button { - private Widgets.Background background; - public Paintable? paintable { +public class Tuba.Widgets.Background : Gtk.Button { + private Gtk.Picture background; + public Gdk.Paintable? paintable { get { return background.paintable; } set { background.paintable = value; } } construct { - background = new Tuba.Widgets.Background (); + background = new Gtk.Picture () { + content_fit = Gtk.ContentFit.COVER, + css_classes = { "header-image" } + }; child = background; - css_classes = { "flat", "image-button", "ttl-flat-button", "header-image" }; + css_classes = { "flat", "image-button", "ttl-flat-button" }; } } - -// This widget fits a Paintable inside its bounds without letterboxing. -public class Tuba.Widgets.Background : Gtk.Widget { - - public Paintable? paintable { get; set; default = null; } - - construct { - add_css_class ("header-image"); - notify["paintable"].connect (() => { - this.queue_draw (); - }); - - overflow = Gtk.Overflow.HIDDEN; - } - - public Graphene.Size widget_size { - get { - return Graphene.Size () { - width = get_width (), - height = get_height () - }; - } - } - public Graphene.Size image_size { - get { - if (paintable == null) - return Graphene.Size.zero (); - - return Graphene.Size () { - width = paintable.get_intrinsic_width (), - height = paintable.get_intrinsic_height () - }; - } - } - - Graphene.Rect get_bounds () { - var bounds = Graphene.Rect (); - bounds.init (0, 0, widget_size.width, widget_size.height); - return bounds; - } - - public override void snapshot (Gtk.Snapshot snapshot) { - if (paintable == null) - return; - - // Get ratio - float image_ratio; - var xscale = (float) widget_size.width / image_size.width; - var yscale = (float) widget_size.height / image_size.height; - if (xscale > yscale) - image_ratio = xscale; - else - image_ratio = yscale; - - // Start drawing - snapshot.save (); - - // Clip image - snapshot.push_clip (get_bounds ()); - - // Center the image - var result_h = image_size.height * image_ratio; - var result_w = image_size.width * image_ratio; - var offset_y = (widget_size.height - result_h) / 2; - var offset_x = (widget_size.width - result_w) / 2; - - var offset = Graphene.Point (); - offset.init (offset_x, offset_y); - snapshot.translate (offset); - - // Scale and draw the paintable - snapshot.scale (image_ratio, image_ratio); - paintable.snapshot (snapshot, image_size.width, image_size.height); - - // Clean-up - snapshot.pop (); - snapshot.restore (); - - base.snapshot (snapshot); - } - -} From 97cd2d592f4ce4186933c683b7757d63e70bfaf6 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Wed, 2 Aug 2023 03:37:00 +0300 Subject: [PATCH 03/76] feat(Avatar): do not store account or use signals we only use the account to get the name and the avatar, no point in storing it --- src/Views/Profile.vala | 2 +- src/Widgets/Avatar.vala | 37 ++++++++++++++++++++++++------------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/Views/Profile.vala b/src/Views/Profile.vala index b448f802a..e5f80a66b 100644 --- a/src/Views/Profile.vala +++ b/src/Views/Profile.vala @@ -173,7 +173,7 @@ public class Tuba.Views.Profile : Views.Timeline { if (account.header.contains ("/headers/original/missing.png")) { header_url = ""; - avatar.bind_property ("custom_image", background, "paintable", GLib.BindingFlags.SYNC_CREATE); + background.paintable = avatar.custom_image; } else { header_url = account.header ?? ""; image_cache.request_paintable (account.header, on_cache_response); diff --git a/src/Widgets/Avatar.vala b/src/Widgets/Avatar.vala index 84e81e2da..c368f1fc7 100644 --- a/src/Widgets/Avatar.vala +++ b/src/Widgets/Avatar.vala @@ -3,11 +3,17 @@ using Gdk; public class Tuba.Widgets.Avatar : Button { - public API.Account? account { get; set; } + public API.Account? account { + set { + on_invalidated (value); + } + } + public int size { get { return avatar.size; } set { avatar.size = value; } } + public Paintable? custom_image { get { return avatar.custom_image; } } @@ -15,30 +21,35 @@ public class Tuba.Widgets.Avatar : Button { protected Adw.Avatar? avatar { get { return child as Adw.Avatar; } } - public string? avatar_url { get; set; } + + string? _avatar_url = null; + public string? avatar_url { + get { + return _avatar_url; + } + + set { + _avatar_url = value; + + if (value != null) { + image_cache.request_paintable (value, on_cache_response); + } + } + } construct { child = new Adw.Avatar (48, null, true); halign = valign = Align.CENTER; css_classes = { "flat", "circular", "image-button", "ttl-flat-button" }; - notify["account"].connect (on_invalidated); - notify["avatar-url"].connect (on_avatar_url_change); on_invalidated (); } - void on_avatar_url_change () { - if (avatar_url == null) return; - - image_cache.request_paintable (avatar_url, on_cache_response); - } - - void on_invalidated () { + void on_invalidated (API.Account? account = null) { if (account == null) { avatar.text = "d"; avatar.show_initials = false; - } - else { + } else { avatar.text = account.display_name; avatar.show_initials = true; image_cache.request_paintable (account.avatar, on_cache_response); From 451be367bd1045b13626e6f7f6397027ed011e52 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Wed, 2 Aug 2023 03:38:48 +0300 Subject: [PATCH 04/76] chore(Timelines): use construct --- src/Views/Bookmarks.vala | 12 ++++-------- src/Views/Explore.vala | 6 +++--- src/Views/Favorites.vala | 10 +++------- src/Views/Hashtags.vala | 10 ++++------ src/Views/Home.vala | 10 ++++------ src/Views/Local.vala | 4 +--- 6 files changed, 19 insertions(+), 33 deletions(-) diff --git a/src/Views/Bookmarks.vala b/src/Views/Bookmarks.vala index 9b37be05d..813098204 100644 --- a/src/Views/Bookmarks.vala +++ b/src/Views/Bookmarks.vala @@ -1,11 +1,7 @@ public class Tuba.Views.Bookmarks : Views.Timeline { - - public Bookmarks () { - Object ( - url: "/api/v1/bookmarks", - label: _("Bookmarks"), - icon: "tuba-bookmarks-symbolic" - ); + construct { + url = "/api/v1/bookmarks"; + label = _("Bookmarks"); + icon = "tuba-bookmarks-symbolic"; } - } diff --git a/src/Views/Explore.vala b/src/Views/Explore.vala index d095eaa59..d300df7e4 100644 --- a/src/Views/Explore.vala +++ b/src/Views/Explore.vala @@ -1,10 +1,10 @@ public class Tuba.Views.Explore : Views.TabbedBase { - public Explore () { - Object ( label: _("Explore") ); + construct { + label = _("Explore"); add_timeline_tab (_("Posts"), "tuba-chat-symbolic", "/api/v1/trends/statuses", typeof (API.Status)); add_timeline_tab (_("Hashtags"), "tuba-hashtag-symbolic", "/api/v1/trends/tags", typeof (API.Tag)); add_timeline_tab (_("News"), "tuba-newspaper-symbolic", "/api/v1/trends/links", typeof (API.PreviewCard)); add_timeline_tab (_("For You"), "tuba-people-symbolic", "/api/v2/suggestions", typeof (API.Suggestion)); - } + } } diff --git a/src/Views/Favorites.vala b/src/Views/Favorites.vala index 97c0206de..662662f5a 100644 --- a/src/Views/Favorites.vala +++ b/src/Views/Favorites.vala @@ -1,10 +1,6 @@ public class Tuba.Views.Favorites : Views.Timeline { - - public Favorites () { - Object ( - url: "/api/v1/favourites", - label: _("Favorites") - ); + construct { + url = "/api/v1/favourites"; + label = _("Favorites"); } - } diff --git a/src/Views/Hashtags.vala b/src/Views/Hashtags.vala index fc2dcd27c..dc9635e80 100644 --- a/src/Views/Hashtags.vala +++ b/src/Views/Hashtags.vala @@ -1,10 +1,8 @@ public class Tuba.Views.Hashtags : Views.Timeline { - public Hashtags () { - Object ( - url: "/api/v1/followed_tags", - label: _("Hashtags"), - icon: "tuba-hashtag-symbolic" - ); + construct { + url = "/api/v1/followed_tags"; + label = _("Hashtags"); + icon = "tuba-hashtag-symbolic"; accepts = typeof (API.Tag); } } diff --git a/src/Views/Home.vala b/src/Views/Home.vala index a164a72d6..0406fc715 100644 --- a/src/Views/Home.vala +++ b/src/Views/Home.vala @@ -1,10 +1,8 @@ public class Tuba.Views.Home : Views.Timeline { - public Home () { - Object ( - url: "/api/v1/timelines/home", - label: _("Home"), - icon: "tuba-home-symbolic" - ); + construct { + url = "/api/v1/timelines/home"; + label = _("Home"); + icon = "tuba-home-symbolic"; } public override string? get_stream_url () { diff --git a/src/Views/Local.vala b/src/Views/Local.vala index 36a578abe..d41c09947 100644 --- a/src/Views/Local.vala +++ b/src/Views/Local.vala @@ -1,6 +1,5 @@ public class Tuba.Views.Local : Views.Federated { - - public Local () { + construct { label = _("Local"); icon = "tuba-network-server-symbolic"; } @@ -16,5 +15,4 @@ public class Tuba.Views.Local : Views.Federated { ? @"$(account.instance)/api/v1/streaming/?stream=public:local&access_token=$(account.access_token)" : null; } - } From 6abafbd4e2f316e4305d593ff51026b13c453f73 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Wed, 2 Aug 2023 03:39:55 +0300 Subject: [PATCH 05/76] feat(Main): do not use lambdas for search button --- src/Views/Main.vala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Views/Main.vala b/src/Views/Main.vala index fc8714965..b36a086ff 100644 --- a/src/Views/Main.vala +++ b/src/Views/Main.vala @@ -17,9 +17,7 @@ public class Tuba.Views.Main : Views.TabbedBase { var search_button = new Button (); search_button.icon_name = "tuba-loupe-large-symbolic"; search_button.tooltip_text = _("Search"); - search_button.clicked.connect ((source) => { - app.main_window.open_view (new Views.Search ()); - }); + search_button.clicked.connect (open_search); header.pack_end (search_button); var sidebar_button = new ToggleButton (); @@ -65,4 +63,7 @@ public class Tuba.Views.Main : Views.TabbedBase { } + void open_search () { + app.main_window.open_view (new Views.Search ()); + } } From f9b70871b9c0445022aea2c5c4bdc8b719273b3f Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Wed, 2 Aug 2023 03:40:58 +0300 Subject: [PATCH 06/76] fix(LWW): infinite loop, statements --- src/Widgets/LabelWithWidgets.vala | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Widgets/LabelWithWidgets.vala b/src/Widgets/LabelWithWidgets.vala index 1225bf28a..b6257a767 100644 --- a/src/Widgets/LabelWithWidgets.vala +++ b/src/Widgets/LabelWithWidgets.vala @@ -11,7 +11,6 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce private Gtk.Widget[] widgets = {}; private int[] widget_heights = {}; private int[] widget_widths = {}; - private int last_for_size = 0; public Gtk.Label label; @@ -99,9 +98,9 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce int width = natural_size.width; int height = natural_size.height; - if (widget_widths.length > 0) { - int old_width = widget_widths[i]; - int old_height = widget_heights[i]; + int old_width = widget_widths[i]; + int old_height = widget_heights[i]; + if (old_width > 0 || old_height > 0) { if (old_width != width || old_height != height) { widget_widths[i] = width; widget_heights[i] = height; @@ -170,9 +169,8 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce var width = widget_widths[i]; var height = widget_heights[i]; - Pango.Rectangle ink_rect; Pango.Rectangle logical_rect; - run_iter.get_run_extents (out ink_rect, out logical_rect); + run_iter.get_run_extents (null, out logical_rect); int offset_x; int offset_y; @@ -181,8 +179,8 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce var allocation = Gtk.Allocation () { x = pango_pixels (logical_rect.x) + offset_x, y = pango_pixels (logical_rect.y) + offset_y, - height = width, - width = height + height = height, + width = width }; widget.allocate_size (allocation, -1); i++; @@ -218,18 +216,15 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce this.allocate_shapes (); this.label.measure ( orientation, - (orientation == Gtk.Orientation.VERTICAL && for_size == -1) ? last_for_size : for_size, + for_size, out minimum, out natural, out minimum_baseline, out natural_baseline ); - if (orientation == Gtk.Orientation.HORIZONTAL) - last_for_size = for_size; } private void update_label () { - last_for_size = 0; var old_label = label.label; var old_ellipsize = label.ellipsize == Pango.EllipsizeMode.END; var new_ellipsize = this.ellipsize; From a1575629df3ccdd0288639f8a757ac84e433eed0 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Wed, 2 Aug 2023 03:42:30 +0300 Subject: [PATCH 07/76] feat(Base): do not use signals for shown status we can use the setter instead --- src/Views/Base.vala | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Views/Base.vala b/src/Views/Base.vala index 6d9854c48..4504d135c 100644 --- a/src/Views/Base.vala +++ b/src/Views/Base.vala @@ -8,13 +8,28 @@ public class Tuba.Views.Base : Box { public string? icon { get; set; default = null; } public string label { get; set; default = ""; } public bool needs_attention { get; set; default = false; } - public bool current { get; set; default = false; } public bool is_main { get; set; default = false; } public bool allow_nesting { get; set; default = false; } public bool is_sidebar_item { get; set; default = false; } public int badge_number { get; set; default = 0; } protected SimpleActionGroup actions { get; set; default = new SimpleActionGroup (); } + private bool _current = false; + public bool current { + get { + return _current; + } + + set { + _current = value; + if (value) { + on_shown (); + } else { + on_hidden (); + } + } + } + [GtkChild] protected unowned Adw.HeaderBar header; [GtkChild] protected unowned Button back_button; @@ -73,15 +88,6 @@ public class Tuba.Views.Base : Box { status_button.label = _("Reload"); base_status = new StatusMessage () { loading = true }; - notify["current"].connect (() => { - if (current) - on_shown (); - else - on_hidden (); - }); - - // scrolled.get_style_context ().add_class (Dialogs.MainWindow.ZOOM_CLASS); - scroll_to_top.clicked.connect (on_scroll_to_top); } ~Base () { From 585d045174b6073e56b2fea166a6e3f1a00761ac Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Wed, 2 Aug 2023 03:44:55 +0300 Subject: [PATCH 08/76] chore(Base): move badge properties to Timeline only used there --- src/Views/Base.vala | 3 +-- src/Views/Timeline.vala | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Views/Base.vala b/src/Views/Base.vala index 4504d135c..f049f62bb 100644 --- a/src/Views/Base.vala +++ b/src/Views/Base.vala @@ -7,11 +7,9 @@ public class Tuba.Views.Base : Box { public string? icon { get; set; default = null; } public string label { get; set; default = ""; } - public bool needs_attention { get; set; default = false; } public bool is_main { get; set; default = false; } public bool allow_nesting { get; set; default = false; } public bool is_sidebar_item { get; set; default = false; } - public int badge_number { get; set; default = 0; } protected SimpleActionGroup actions { get; set; default = new SimpleActionGroup (); } private bool _current = false; @@ -90,6 +88,7 @@ public class Tuba.Views.Base : Box { scroll_to_top.clicked.connect (on_scroll_to_top); } + ~Base () { message (@"Destroying base $label"); } diff --git a/src/Views/Timeline.vala b/src/Views/Timeline.vala index d20fe5a3d..2b7fe2c76 100644 --- a/src/Views/Timeline.vala +++ b/src/Views/Timeline.vala @@ -7,6 +7,8 @@ public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase public bool is_public { get; construct set; default = false; } public Type accepts { get; set; default = typeof (API.Status); } public bool use_queue { get; set; default = true; } + public int badge_number { get; set; default = 0; } + public bool needs_attention { get; set; default = false; } protected InstanceAccount? account { get; set; default = null; } @@ -254,6 +256,7 @@ public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase } public virtual void on_edit_post (Streamable.Event ev) { + if (accepts != typeof (API.Status)) return; try { var entity = Entity.from_json (accepts, ev.get_node ()); var entity_id = ((API.Status)entity).id; @@ -271,6 +274,7 @@ public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase } public virtual void on_delete_post (Streamable.Event ev) { + if (accepts != typeof (API.Status)) return; try { var status_id = ev.get_string (); From 9c22477deec5334a343f5cf6f7250ac2c64c68e1 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Wed, 2 Aug 2023 03:45:45 +0300 Subject: [PATCH 09/76] fix(Status): remove menu model on destroy --- src/Widgets/Status.vala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index 4d0111fa3..695984fc2 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -230,6 +230,7 @@ public class Tuba.Widgets.Status : ListBoxRow { ~Status () { message ("Destroying Status widget"); if (context_menu != null) { + context_menu.menu_model = null; context_menu.dispose (); } } From f1ea76dee8df14e14e807e471baa76f111302098 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Wed, 2 Aug 2023 03:46:42 +0300 Subject: [PATCH 10/76] chore(EmojiLabel): remove namespaces --- src/Widgets/EmojiLabel.vala | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Widgets/EmojiLabel.vala b/src/Widgets/EmojiLabel.vala index b1e817fe5..dd5f45bd1 100644 --- a/src/Widgets/EmojiLabel.vala +++ b/src/Widgets/EmojiLabel.vala @@ -1,6 +1,3 @@ -using Gtk; -using Gee; - public class Tuba.Widgets.EmojiLabel : Tuba.Widgets.LabelWithWidgets { public Gee.HashMap? instance_emojis { get; set; default = null; } From 9fe87d809f4089ef83947c178a02e8cf703492b0 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Wed, 2 Aug 2023 03:47:04 +0300 Subject: [PATCH 11/76] chore: format --- src/Application.vala | 3 +-- src/Dialogs/Saveable.vala | 2 -- src/Views/StatusStats.vala | 2 +- src/Widgets/Conversation.vala | 2 ++ src/Widgets/Widgetizable.vala | 3 +-- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Application.vala b/src/Application.vala index f1c89b2f8..0f2048b6a 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -196,8 +196,7 @@ namespace Tuba { if (add_account_window == null) new Dialogs.NewAccount (); add_account_window.present (); - } - else { + } else { message ("Presenting MainWindow"); if (main_window == null) { main_window = new Dialogs.MainWindow (this); diff --git a/src/Dialogs/Saveable.vala b/src/Dialogs/Saveable.vala index a0d3f914a..337edc690 100644 --- a/src/Dialogs/Saveable.vala +++ b/src/Dialogs/Saveable.vala @@ -1,10 +1,8 @@ using Gtk; public interface Tuba.Dialogs.Saveable : Window { - protected void construct_saveable (GLib.Settings settings) { settings.bind ("window-w", this, "default-width", SettingsBindFlags.DEFAULT); settings.bind ("window-h", this, "default-height", SettingsBindFlags.DEFAULT); } - } diff --git a/src/Views/StatusStats.vala b/src/Views/StatusStats.vala index a097e1271..e5448ff18 100644 --- a/src/Views/StatusStats.vala +++ b/src/Views/StatusStats.vala @@ -3,7 +3,7 @@ public class Tuba.Views.StatusStats : Views.TabbedBase { Views.ContentBase boosted; public StatusStats (string status_id) { - Object ( label: _("Post Stats") ); + Object (label: _("Post Stats")); favorited = add_timeline_tab ( // translators: title for a list of people that favorited a post diff --git a/src/Widgets/Conversation.vala b/src/Widgets/Conversation.vala index b5aa37991..6086c80bd 100644 --- a/src/Widgets/Conversation.vala +++ b/src/Widgets/Conversation.vala @@ -20,9 +20,11 @@ public class Tuba.Widgets.Conversation : Widgets.Status { target.set_string ("tuba-mail-open-small-symbolic"); this.visibility_indicator.add_css_class ("dim-label"); } + return true; } ); + // this.indicators.child_set_property (this.visibility_indicator, "position", 2); this.actions.destroy (); } diff --git a/src/Widgets/Widgetizable.vala b/src/Widgets/Widgetizable.vala index af8fbd6ff..36a9b1f1d 100644 --- a/src/Widgets/Widgetizable.vala +++ b/src/Widgets/Widgetizable.vala @@ -1,5 +1,4 @@ public interface Tuba.Widgetizable : GLib.Object { - public virtual Gtk.Widget to_widget () throws Oopsie { throw new Tuba.Oopsie.INTERNAL ("Widgetizable didn't provide a Widget!"); } @@ -7,8 +6,8 @@ public interface Tuba.Widgetizable : GLib.Object { public virtual void open () { warning ("Widgetizable didn't provide a way to open it!"); } + public virtual void resolve_open (InstanceAccount account) { this.open (); } - } From c2f1e73a2ef4316c0869d1740c8ec5cbd3fed179 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Thu, 3 Aug 2023 22:19:54 +0300 Subject: [PATCH 12/76] feat(ProfileEdit): use Builder file --- data/gresource.xml | 1 + data/ui/dialogs/profile_edit.ui | 180 +++++++++++++++++++++++ po/POTFILES | 1 + src/Dialogs/ProfileEdit.vala | 245 ++++---------------------------- 4 files changed, 211 insertions(+), 216 deletions(-) create mode 100644 data/ui/dialogs/profile_edit.ui diff --git a/data/gresource.xml b/data/gresource.xml index 68dc0b471..a84fa772f 100644 --- a/data/gresource.xml +++ b/data/gresource.xml @@ -85,6 +85,7 @@ ui/dialogs/compose.ui ui/dialogs/main.ui ui/dialogs/preferences.ui + ui/dialogs/profile_edit.ui ui/menus.ui diff --git a/data/ui/dialogs/profile_edit.ui b/data/ui/dialogs/profile_edit.ui new file mode 100644 index 000000000..d5d18b9b1 --- /dev/null +++ b/data/ui/dialogs/profile_edit.ui @@ -0,0 +1,180 @@ + + + + + + + + + + diff --git a/po/POTFILES b/po/POTFILES index b32f4686e..37c5be03d 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -13,6 +13,7 @@ ./data/ui/dialogs/compose.ui ./data/ui/dialogs/new_account.ui ./data/ui/dialogs/main.ui +./data/ui/dialogs/profile_edit.ui ./data/gtk/help-overlay.ui ./data/gtk/dropdown/expiration_title.ui ./data/gtk/dropdown/expiration.ui diff --git a/src/Dialogs/ProfileEdit.vala b/src/Dialogs/ProfileEdit.vala index e7a2767b5..0dfbd06ae 100644 --- a/src/Dialogs/ProfileEdit.vala +++ b/src/Dialogs/ProfileEdit.vala @@ -1,49 +1,9 @@ +[GtkTemplate (ui = "/dev/geopjr/Tuba/ui/dialogs/profile_edit.ui")] public class Tuba.Dialogs.ProfileEdit : Adw.Window { ~ProfileEdit () { message (@"Destroying ProfileEdit for $(profile.handle)"); } - public class Avatar : Adw.Bin { - Adw.Avatar avatar; - - private string _url = ""; - public string url { - get { - return _url; - } - - set { - _url = value; - image_cache.request_paintable (value, on_cache_response); - } - } - - public string text { - get { return avatar.text; } - set { avatar.text = value; } - } - - public int size { - get { return avatar.size; } - set { avatar.size = value; } - } - - public Gdk.Paintable? custom_image { - get { return avatar.custom_image; } - set { avatar.custom_image = value; } - } - - construct { - avatar = new Adw.Avatar (48, "d", false); - child = avatar; - halign = valign = Gtk.Align.CENTER; - } - - void on_cache_response (bool is_loaded, owned Gdk.Paintable? data) { - custom_image = data; - } - } - public class Field : Adw.ExpanderRow { ~Field () { message ("Destroying ProfileEdit.Field"); @@ -105,16 +65,12 @@ public class Tuba.Dialogs.ProfileEdit : Adw.Window { } } - private Avatar avi { get; set; } - private Adw.EntryRow name_row { get; set; } - private Adw.ExpanderRow bio_row { get; set; } - #if MISSING_GTKSOURCEVIEW - private Gtk.TextView bio_text_view { get; set; } - #else - private GtkSource.View bio_text_view { get; set; } - #endif - private Adw.PreferencesGroup fields_box { get; set; } - private Gtk.Picture background { get; set; } + [GtkChild] unowned Adw.EntryRow name_row; + [GtkChild] unowned Adw.ExpanderRow bio_row; + [GtkChild] unowned GtkSource.View bio_text_view; + [GtkChild] unowned Adw.Avatar avi; + [GtkChild] unowned Gtk.Picture background; + [GtkChild] unowned Adw.PreferencesGroup fields_box; Gtk.FileFilter filter = new Gtk.FileFilter () { name = _("All Supported Files") @@ -125,61 +81,17 @@ public class Tuba.Dialogs.ProfileEdit : Adw.Window { filter.add_mime_type ("image/png"); filter.add_mime_type ("image/gif"); - add_css_class ("profile-editor"); add_binding_action (Gdk.Key.Escape, 0, "window.close", null); - - title = _("Edit Profile"); - modal = true; transient_for = app.main_window; - default_width = 460; - default_height = 520; - - var content_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 24) { - margin_top = 24, - margin_bottom = 24 - }; - - var profile_info_box = new Gtk.ListBox () { - css_classes = { "boxed-list" }, - selection_mode = Gtk.SelectionMode.NONE - }; - - name_row = new Adw.EntryRow () { - input_purpose = Gtk.InputPurpose.FREE_FORM, - title = _("Display Name") - }; - name_row.changed.connect (on_name_row_changed); - - bio_row = new Adw.ExpanderRow () { - // translators: profile bio or description - title = _("Bio"), - expanded = false - }; - - #if MISSING_GTKSOURCEVIEW - bio_text_view = new Gtk.TextView () { - #else - bio_text_view = new GtkSource.View () { - #endif - margin_bottom = 6, - margin_top = 6, - margin_end = 6, - margin_start = 6, - wrap_mode = Gtk.WrapMode.WORD_CHAR, - css_classes = { "background-none" }, - accepts_tab = false - }; - bio_row.add_row (bio_text_view); + bio_text_view.buffer.changed.connect (on_bio_text_changed); - #if !MISSING_GTKSOURCEVIEW - var manager = GtkSource.StyleSchemeManager.get_default (); - var scheme = manager.get_scheme ("adwaita"); - var buffer = bio_text_view.buffer as GtkSource.Buffer; - buffer.style_scheme = scheme; - #endif + var manager = GtkSource.StyleSchemeManager.get_default (); + var scheme = manager.get_scheme ("adwaita"); + var buffer = bio_text_view.buffer as GtkSource.Buffer; + buffer.style_scheme = scheme; - #if LIBSPELLING && !MISSING_GTKSOURCEVIEW + #if LIBSPELLING var adapter = new Spelling.TextBufferAdapter ((GtkSource.Buffer) bio_text_view.buffer, Spelling.Checker.get_default ()); bio_text_view.extra_menu = adapter.get_menu_model (); @@ -191,131 +103,24 @@ public class Tuba.Dialogs.ProfileEdit : Adw.Window { var gspell_view = Gspell.TextView.get_from_gtk_text_view (bio_text_view); gspell_view.basic_setup (); #endif - - var custom_emoji_picker = new Widgets.CustomEmojiChooser (); - var custom_emoji_button = new Gtk.MenuButton () { - icon_name = "tuba-cat-symbolic", - popover = custom_emoji_picker, - tooltip_text = _("Custom Emoji Picker"), - css_classes = { "circular" }, - valign = Gtk.Align.CENTER, - halign = Gtk.Align.CENTER - }; - custom_emoji_picker.emoji_picked.connect (on_bio_emoji_picked); - bio_row.bind_property ("expanded", custom_emoji_button, "sensitive", GLib.BindingFlags.SYNC_CREATE); - // FIXME: add_suffix on libadwaita 1.4 - bio_row.add_action (custom_emoji_button); - - profile_info_box.append (name_row); - profile_info_box.append (bio_row); - - fields_box = new Adw.PreferencesGroup () { - // translators: profile fields, if unsure take a look at Mastodon https://github.com/mastodon/mastodon/blob/main/config/locales/ (under simple_form) - title = _("Fields") - }; - - content_box.append (create_header_avi_widget ()); - content_box.append (profile_info_box); - content_box.append (fields_box); - - var clamp = new Adw.Clamp () { - child = content_box, - tightening_threshold = 100, - valign = Gtk.Align.START - }; - var scroller = new Gtk.ScrolledWindow () { - hexpand = true, - vexpand = true - }; - scroller.child = clamp; - - var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); - var headerbar = new Adw.HeaderBar () { - show_end_title_buttons = false, - show_start_title_buttons = false - }; - - var save_btn = new Gtk.Button.with_label (_("Save")) { - css_classes = { "suggested-action" } - }; - save_btn.clicked.connect (on_save_clicked); - - var close_btn = new Gtk.Button.with_label (_("Close")); - close_btn.clicked.connect (on_close); - - headerbar.pack_start (close_btn); - headerbar.pack_end (save_btn); - - box.append (headerbar); - box.append (scroller); - - content = box; - } - - Gtk.Widget create_header_avi_widget () { - var avi_edit_button = new Gtk.Button.from_icon_name ("document-edit-symbolic") { - css_classes = { "osd", "circular" }, - tooltip_text = _("Edit Profile Picture"), - valign = Gtk.Align.CENTER, - halign = Gtk.Align.CENTER - }; - avi_edit_button.clicked.connect (on_avi_button_clicked); - - avi = new Avatar () { - size = 120 - }; - - var avi_overlay = new Gtk.Overlay () { - child = avi, - valign = Gtk.Align.CENTER, - halign = Gtk.Align.CENTER - }; - avi_overlay.add_overlay (avi_edit_button); - - var background_edit_button = new Gtk.Button.from_icon_name ("document-edit-symbolic") { - css_classes = { "osd", "circular" }, - // translators: if unsure take a look at Mastodon https://github.com/mastodon/mastodon/blob/main/config/locales/ (under simple_form) - tooltip_text = _("Edit Header Picture"), - valign = Gtk.Align.START, - halign = Gtk.Align.START, - margin_start = 6, - margin_top = 6 - }; - background_edit_button.clicked.connect (on_header_button_clicked); - - background = new Gtk.Picture () { - content_fit = Gtk.ContentFit.COVER, - css_classes = { "background-cover" }, - height_request = 128, - }; - - var background_overlay = new Gtk.Overlay () { - vexpand = true, - hexpand = true, - child = background - }; - background_overlay.add_overlay (background_edit_button); - - var images_overlay = new Gtk.Overlay () { - child = background_overlay - }; - images_overlay.add_overlay (avi_overlay); - - return images_overlay; } + [GtkCallback] void on_close () { destroy (); } + [GtkCallback] void on_avi_button_clicked () { choose_file (false); } + [GtkCallback] void on_header_button_clicked () { choose_file (true); } + [GtkCallback] void on_save_clicked () { this.sensitive = false; save.begin ((obj, res) => { @@ -332,6 +137,7 @@ public class Tuba.Dialogs.ProfileEdit : Adw.Window { }); } + [GtkCallback] void on_bio_emoji_picked (string emoji_unicode) { bio_text_view.buffer.insert_at_cursor (emoji_unicode, emoji_unicode.data.length); } @@ -340,6 +146,7 @@ public class Tuba.Dialogs.ProfileEdit : Adw.Window { return wdg.has_css_class ("error"); } + [GtkCallback] void on_name_row_changed () { var valid = name_row.text.length <= 30; Tuba.toggle_css (name_row, !valid, "error"); @@ -357,8 +164,8 @@ public class Tuba.Dialogs.ProfileEdit : Adw.Window { int64 max_value_length; public ProfileEdit (API.Account acc) { profile = acc; - image_cache.request_paintable (acc.header, on_cache_response); - avi.url = acc.avatar; + image_cache.request_paintable (acc.header, on_background_cache_response); + image_cache.request_paintable (acc.avatar, on_avi_cache_response); avi.text = acc.display_name; name_row.text = acc.display_name; bio_text_view.buffer.text = acc.source?.note ?? ""; @@ -392,8 +199,14 @@ public class Tuba.Dialogs.ProfileEdit : Adw.Window { fields_box.add (field); } - void on_cache_response (bool is_loaded, owned Gdk.Paintable? data) { - background.paintable = data; + void on_background_cache_response (bool is_loaded, owned Gdk.Paintable? data) { + if (is_loaded) + background.paintable = data; + } + + void on_avi_cache_response (bool is_loaded, owned Gdk.Paintable? data) { + if (is_loaded) + avi.custom_image = data; } File new_avi; From dabbf74a5436210ea5516c877284551c158a071c Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Fri, 4 Aug 2023 02:18:41 +0300 Subject: [PATCH 13/76] fix(profile_edit): overlay expanding --- data/ui/dialogs/profile_edit.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/ui/dialogs/profile_edit.ui b/data/ui/dialogs/profile_edit.ui index d5d18b9b1..56536d9f8 100644 --- a/data/ui/dialogs/profile_edit.ui +++ b/data/ui/dialogs/profile_edit.ui @@ -55,8 +55,8 @@ - 1 - 1 + center + center 120 From 359217b75af967e9636137346ae058455f588cca Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Tue, 8 Aug 2023 06:18:58 +0300 Subject: [PATCH 14/76] feat!: remove GTK < 4.10 compat --- meson.build | 4 -- src/Dialogs/Composer/AttachmentsPage.vala | 73 ++++++++--------------- src/Dialogs/ProfileEdit.vala | 48 ++++----------- src/Utils/Host.vala | 20 +++---- src/Widgets/Attachment/Item.vala | 60 +++++++------------ 5 files changed, 66 insertions(+), 139 deletions(-) diff --git a/meson.build b/meson.build index 1cfbdce8f..3f2190f9d 100644 --- a/meson.build +++ b/meson.build @@ -75,10 +75,6 @@ if libgtk_dep.version().version_compare('>=4.8.0') add_project_arguments(['--define=GTK_4_8'], language: 'vala') endif -if libgtk_dep.version().version_compare('>=4.10.0') - add_project_arguments(['--define=GTK_4_10'], language: 'vala') -endif - if libspelling.found () add_project_arguments(['--define=LIBSPELLING'], language: 'vala') endif diff --git a/src/Dialogs/Composer/AttachmentsPage.vala b/src/Dialogs/Composer/AttachmentsPage.vala index cdf0174d7..9d705778c 100644 --- a/src/Dialogs/Composer/AttachmentsPage.vala +++ b/src/Dialogs/Composer/AttachmentsPage.vala @@ -368,58 +368,35 @@ public class Tuba.AttachmentsPage : ComposerPage { } void show_file_selector () { - #if GTK_4_10 - var chooser = new FileDialog () { - // translators: Open file - title = _("Open"), - modal = true, - default_filter = filter - }; - chooser.open_multiple.begin (dialog, null, (obj, res) => { - try { - var files = chooser.open_multiple.end (res); - #else + var chooser = new FileDialog () { // translators: Open file - var chooser = new FileChooserNative (_("Open"), dialog, Gtk.FileChooserAction.OPEN, null, null) { - select_multiple = true, - filter = filter, - modal = true - }; - - chooser.response.connect (id => { - switch (id) { - case ResponseType.ACCEPT: - var files = chooser.get_files (); - #endif - - File[] files_to_upload = {}; - var amount_of_files = files.get_n_items (); - for (var i = 0; i < amount_of_files; i++) { - var file = files.get_item (i) as File; - - if (file != null) - files_to_upload += file; - } + title = _("Open"), + modal = true, + default_filter = filter + }; + chooser.open_multiple.begin (dialog, null, (obj, res) => { + try { + var files = chooser.open_multiple.end (res); - upload_files.begin (files_to_upload, (obj, res) => { - upload_files.end (res); - }); + File[] files_to_upload = {}; + var amount_of_files = files.get_n_items (); + for (var i = 0; i < amount_of_files; i++) { + var file = files.get_item (i) as File; - #if GTK_4_10 - } catch (Error e) { - // User dismissing the dialog also ends here so don't make it sound like - // it's an error - warning (@"Couldn't get the result of FileDialog for AttachmentsPage: $(e.message)"); + if (file != null) + files_to_upload += file; } - }); - #else - break; - } - chooser.unref (); - }); - chooser.ref (); - chooser.show (); - #endif + + upload_files.begin (files_to_upload, (obj, res) => { + upload_files.end (res); + }); + + } catch (Error e) { + // User dismissing the dialog also ends here so don't make it sound like + // it's an error + warning (@"Couldn't get the result of FileDialog for AttachmentsPage: $(e.message)"); + } + }); } private async void upload_files (File[] files) { diff --git a/src/Dialogs/ProfileEdit.vala b/src/Dialogs/ProfileEdit.vala index a58002dcf..2febdd673 100644 --- a/src/Dialogs/ProfileEdit.vala +++ b/src/Dialogs/ProfileEdit.vala @@ -217,7 +217,6 @@ public class Tuba.Dialogs.ProfileEdit : Adw.Window { File new_avi; File new_header; void choose_file (bool for_header = false) { - #if GTK_4_10 var chooser = new Gtk.FileDialog () { title = _("Open"), modal = true, @@ -225,50 +224,29 @@ public class Tuba.Dialogs.ProfileEdit : Adw.Window { }; chooser.open.begin (this, null, (obj, res) => { try { - var file = chooser.open.end (res); - #else - var chooser = new Gtk.FileChooserNative (_("Open"), this, Gtk.FileChooserAction.OPEN, null, null) { - select_multiple = false, - filter = filter, - modal = true - }; + var file = chooser.open.end (res); - chooser.response.connect (id => { - switch (id) { - case Gtk.ResponseType.ACCEPT: - var file = chooser.get_file (); - #endif + try { + var texture = Gdk.Texture.from_file (file); - try { - var texture = Gdk.Texture.from_file (file); - - if (for_header) { - new_header = file; - background.paintable = texture; - } else { - new_avi = file; - avi.custom_image = texture; - } + if (for_header) { + new_header = file; + background.paintable = texture; + } else { + new_avi = file; + avi.custom_image = texture; + } - } catch (Error e) { - critical (@"Couldn't construct Texture from file $(e.message)"); - } + } catch (Error e) { + critical (@"Couldn't construct Texture from file $(e.message)"); + } - #if GTK_4_10 } catch (Error e) { // User dismissing the dialog also ends here so don't make it sound like // it's an error warning (@"Couldn't get the result of FileDialog for ProfileEdit: $(e.message)"); } }); - #else - break; - } - chooser.unref (); - }); - chooser.ref (); - chooser.show (); - #endif } public signal void saved (); diff --git a/src/Utils/Host.vala b/src/Utils/Host.vala index 0dfcd9580..313acc1fe 100644 --- a/src/Utils/Host.vala +++ b/src/Utils/Host.vala @@ -18,18 +18,14 @@ public class Tuba.Host { throw new Oopsie.USER ("launch_default_for_uri() failed"); } catch (Error e) { - #if GTK_4_10 - var launcher = new Gtk.UriLauncher (uri); - launcher.launch.begin (app.active_window, null, (obj, res) => { - try { - launcher.launch.end (res); - } catch (Error e) { - warning (@"Error opening uri \"$uri\": $(e.message)"); - } - }); - #else - Gtk.show_uri (app.active_window, uri, Gdk.CURRENT_TIME); - #endif + var launcher = new Gtk.UriLauncher (uri); + launcher.launch.begin (app.active_window, null, (obj, res) => { + try { + launcher.launch.end (res); + } catch (Error e) { + warning (@"Error opening uri \"$uri\": $(e.message)"); + } + }); } return true; } diff --git a/src/Widgets/Attachment/Item.vala b/src/Widgets/Attachment/Item.vala index cb7b55a9f..7dae4f61b 100644 --- a/src/Widgets/Attachment/Item.vala +++ b/src/Widgets/Attachment/Item.vala @@ -33,47 +33,27 @@ public class Tuba.Widgets.Attachment.Item : Adw.Bin { } public static void save_media_as (string url) { - #if GTK_4_10 - var chooser = new FileDialog () { - title = _("Save Attachment"), - modal = true, - initial_name = Path.get_basename (url) - }; - - chooser.save.begin (app.main_window, null, (obj, res) => { - try { - var file = chooser.save.end (res); - if (file != null) { - #else - var chooser = new FileChooserNative (_("Save Attachment"), app.main_window, Gtk.FileChooserAction.SAVE, null, null) { - modal = true - }; - chooser.set_current_name (Path.get_basename (url)); - chooser.response.connect (id => { - switch (id) { - case ResponseType.ACCEPT: - var file = chooser.get_file (); - #endif - message (@"Downloading file: $(url)…"); - download.begin (url, file, (obj, res) => { - download.end (res); - }); - #if GTK_4_10 - } - } catch (Error e) { - // User dismissing the dialog also ends here so don't make it sound like - // it's an error - warning (@"Couldn't get the result of FileDialog for attachment: $(e.message)"); - } - }); - #else - break; + var chooser = new FileDialog () { + title = _("Save Attachment"), + modal = true, + initial_name = Path.get_basename (url) + }; + + chooser.save.begin (app.main_window, null, (obj, res) => { + try { + var file = chooser.save.end (res); + if (file != null) { + message (@"Downloading file: $(url)…"); + download.begin (url, file, (obj, res) => { + download.end (res); + }); } - chooser.unref (); - }); - chooser.ref (); - chooser.show (); - #endif + } catch (Error e) { + // User dismissing the dialog also ends here so don't make it sound like + // it's an error + warning (@"Couldn't get the result of FileDialog for attachment: $(e.message)"); + } + }); } private static async void download (string attachment_url, File file) { From a600f4744716f04b62ed218ce7aec0305899e528 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Tue, 8 Aug 2023 06:20:50 +0300 Subject: [PATCH 15/76] feat!: remove GTK < 4.8 compat --- meson.build | 4 ---- src/Widgets/Attachment/Image.vala | 14 +++++--------- src/Widgets/PreviewCard.vala | 7 ++----- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/meson.build b/meson.build index 3f2190f9d..6ae86ce4b 100644 --- a/meson.build +++ b/meson.build @@ -71,10 +71,6 @@ if not libwebp_dep.found () warning('WebP support might be missing, please install webp-pixbuf-loader.') endif -if libgtk_dep.version().version_compare('>=4.8.0') - add_project_arguments(['--define=GTK_4_8'], language: 'vala') -endif - if libspelling.found () add_project_arguments(['--define=LIBSPELLING'], language: 'vala') endif diff --git a/src/Widgets/Attachment/Image.vala b/src/Widgets/Attachment/Image.vala index 44f414c20..7e5652301 100644 --- a/src/Widgets/Attachment/Image.vala +++ b/src/Widgets/Attachment/Image.vala @@ -26,11 +26,9 @@ public class Tuba.Widgets.Attachment.Image : Widgets.Attachment.Item { } } - #if GTK_4_8 - void update_pic_content_fit () { - pic.set_property ("content-fit", settings.letterbox_media ? 1 : 2); - } - #endif + void update_pic_content_fit () { + pic.content_fit = settings.letterbox_media ? Gtk.ContentFit.CONTAIN : Gtk.ContentFit.COVER; + } construct { pic = new Picture () { @@ -42,10 +40,8 @@ public class Tuba.Widgets.Attachment.Image : Widgets.Attachment.Item { // content_fit = ContentFit.COVER // GTK 4.8 }; - #if GTK_4_8 - update_pic_content_fit (); - settings.notify["letterbox-media"].connect (update_pic_content_fit); - #endif + update_pic_content_fit (); + settings.notify["letterbox-media"].connect (update_pic_content_fit); media_overlay = new Overlay (); media_overlay.child = pic; diff --git a/src/Widgets/PreviewCard.vala b/src/Widgets/PreviewCard.vala index 445fe7ec4..14959f981 100644 --- a/src/Widgets/PreviewCard.vala +++ b/src/Widgets/PreviewCard.vala @@ -13,13 +13,10 @@ public class Tuba.Widgets.PreviewCard : Gtk.Button { if (card_obj.image != null) { var image = new Gtk.Picture () { - width_request = 25 + width_request = 25, + content_fit = Gtk.ContentFit.COVER }; - #if GTK_4_8 - image.set_property ("content-fit", 2); - #endif - image_cache.request_paintable (card_obj.image, (is_loaded, paintable) => { image.paintable = paintable; }); From 1220387b113776c5cf2ac9dac95e685ca57820b3 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Tue, 8 Aug 2023 06:23:57 +0300 Subject: [PATCH 16/76] feat!: remove building without GtkSourceView --- meson.build | 7 +-- src/Application.vala | 5 +- .../Composer/AttachmentsPageAttachment.vala | 26 +++------- src/Dialogs/Composer/EditorPage.vala | 51 +++++++------------ src/Dialogs/Composer/meson.build | 4 +- src/Dialogs/ProfileEdit.vala | 2 +- 6 files changed, 32 insertions(+), 63 deletions(-) diff --git a/meson.build b/meson.build index 6ae86ce4b..47626bb6f 100644 --- a/meson.build +++ b/meson.build @@ -62,7 +62,7 @@ asresources = gnome.compile_resources( libgtk_dep = dependency('gtk4', version: '>=4.0.0', required: true) libadwaita_dep = dependency('libadwaita-1', version: '>=1.2', required: true) -gtksourceview_dep = dependency('gtksourceview-5', required: false, version: '>=5.6.0') +gtksourceview_dep = dependency('gtksourceview-5', required: true, version: '>=5.6.0') libwebp_dep = dependency('libwebp', required: false) gspell = dependency('gspell-4', required: false) libspelling = dependency('libspelling-1', required: false) @@ -79,10 +79,7 @@ if gspell.found () add_project_arguments(['--define=GSPELL'], language: 'vala') endif -if not gtksourceview_dep.found () - warning('Support for lack of gtksourceview-5 is not guaranteed.') - add_project_arguments(['--define=MISSING_GTKSOURCEVIEW'], language: 'vala') -elif gtksourceview_dep.version().version_compare('>=5.7.1') +if gtksourceview_dep.version().version_compare('>=5.7.1') add_project_arguments(['--define=GTKSOURCEVIEW_5_7_1'], language: 'vala') endif diff --git a/src/Application.vala b/src/Application.vala index 0f2048b6a..283c01459 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -119,10 +119,7 @@ namespace Tuba { message (line); } Adw.init (); - - #if !MISSING_GTKSOURCEVIEW - GtkSource.init (); - #endif + GtkSource.init (); settings = new Settings (); streams = new Streams (); diff --git a/src/Dialogs/Composer/AttachmentsPageAttachment.vala b/src/Dialogs/Composer/AttachmentsPageAttachment.vala index 8c4692030..6439a04d5 100644 --- a/src/Dialogs/Composer/AttachmentsPageAttachment.vala +++ b/src/Dialogs/Composer/AttachmentsPageAttachment.vala @@ -99,20 +99,12 @@ public class Tuba.AttachmentsPageAttachment : Widgets.Attachment.Item { return (ALT_MAX_CHARS - text_size).to_string (); } - #if MISSING_GTKSOURCEVIEW - protected Gtk.TextView alt_editor; - #else - protected GtkSource.View alt_editor; - #endif + GtkSource.View alt_editor; Adw.Window dialog; Gtk.Button dialog_save_btn; Gtk.Label dialog_char_counter; protected Adw.Window create_alt_text_input_window () { - #if MISSING_GTKSOURCEVIEW - alt_editor = new Gtk.TextView () { - #else - alt_editor = new GtkSource.View () { - #endif + alt_editor = new GtkSource.View () { vexpand = true, hexpand = true, top_margin = 6, @@ -124,14 +116,12 @@ public class Tuba.AttachmentsPageAttachment : Widgets.Attachment.Item { wrap_mode = Gtk.WrapMode.WORD_CHAR }; - #if !MISSING_GTKSOURCEVIEW - var manager = GtkSource.StyleSchemeManager.get_default (); - var scheme = manager.get_scheme ("adwaita"); - var buffer = alt_editor.buffer as GtkSource.Buffer; - buffer.style_scheme = scheme; - #endif + var manager = GtkSource.StyleSchemeManager.get_default (); + var scheme = manager.get_scheme ("adwaita"); + var buffer = alt_editor.buffer as GtkSource.Buffer; + buffer.style_scheme = scheme; - #if LIBSPELLING && !MISSING_GTKSOURCEVIEW + #if LIBSPELLING var adapter = new Spelling.TextBufferAdapter ((GtkSource.Buffer) alt_editor.buffer, Spelling.Checker.get_default ()); alt_editor.extra_menu = adapter.get_menu_model (); @@ -139,7 +129,7 @@ public class Tuba.AttachmentsPageAttachment : Widgets.Attachment.Item { adapter.enabled = true; #endif - #if GSPELL && (MISSING_GTKSOURCEVIEW || !LIBSPELLING) + #if GSPELL && !LIBSPELLING var gspell_view = Gspell.TextView.get_from_gtk_text_view (alt_editor); gspell_view.basic_setup (); #endif diff --git a/src/Dialogs/Composer/EditorPage.vala b/src/Dialogs/Composer/EditorPage.vala index d5a3d9c23..12ce9a469 100644 --- a/src/Dialogs/Composer/EditorPage.vala +++ b/src/Dialogs/Composer/EditorPage.vala @@ -75,11 +75,7 @@ public class Tuba.EditorPage : ComposerPage { builder.add_string_value (status.sensitive ? status.spoiler_text : ""); } - #if MISSING_GTKSOURCEVIEW - protected TextView editor; - #else - protected GtkSource.View editor; - #endif + protected GtkSource.View editor; protected Label char_counter; public void editor_grab_focus () { @@ -89,9 +85,7 @@ public class Tuba.EditorPage : ComposerPage { protected void install_editor () { recount_chars.connect (() => { remaining_chars = char_limit; - #if !MISSING_GTKSOURCEVIEW - editor.show_completion (); - #endif + editor.show_completion (); }); recount_chars.connect_after (() => { placeholder.visible = remaining_chars == char_limit; @@ -105,11 +99,8 @@ public class Tuba.EditorPage : ComposerPage { } }); - #if MISSING_GTKSOURCEVIEW - editor = new TextView () { - #else - editor = new GtkSource.View () { - #endif + + editor = new GtkSource.View () { vexpand = true, hexpand = true, top_margin = 6, @@ -121,7 +112,7 @@ public class Tuba.EditorPage : ComposerPage { wrap_mode = WrapMode.WORD_CHAR }; - #if LIBSPELLING && !MISSING_GTKSOURCEVIEW + #if LIBSPELLING var adapter = new Spelling.TextBufferAdapter ((GtkSource.Buffer) editor.buffer, Spelling.Checker.get_default ()); editor.extra_menu = adapter.get_menu_model (); @@ -129,7 +120,7 @@ public class Tuba.EditorPage : ComposerPage { adapter.enabled = true; #endif - #if GSPELL && (MISSING_GTKSOURCEVIEW || !LIBSPELLING) + #if GSPELL && !LIBSPELLING var gspell_view = Gspell.TextView.get_from_gtk_text_view (editor); gspell_view.basic_setup (); #endif @@ -144,15 +135,13 @@ public class Tuba.EditorPage : ComposerPage { }); editor.add_controller (keypress_controller); - #if !MISSING_GTKSOURCEVIEW - editor.completion.add_provider (new Tuba.HandleProvider ()); - editor.completion.add_provider (new Tuba.HashtagProvider ()); - editor.completion.add_provider (new Tuba.EmojiProvider ()); - editor.completion.select_on_show = true; - editor.completion.show_icons = true; - editor.completion.page_size = 3; - update_style_scheme (); - #endif + editor.completion.add_provider (new Tuba.HandleProvider ()); + editor.completion.add_provider (new Tuba.HashtagProvider ()); + editor.completion.add_provider (new Tuba.EmojiProvider ()); + editor.completion.select_on_show = true; + editor.completion.show_icons = true; + editor.completion.page_size = 3; + update_style_scheme (); recount_chars.connect (() => { remaining_chars -= editor.buffer.get_char_count (); @@ -167,14 +156,12 @@ public class Tuba.EditorPage : ComposerPage { editor.buffer.changed.connect (validate); } - #if !MISSING_GTKSOURCEVIEW - protected void update_style_scheme () { - var manager = GtkSource.StyleSchemeManager.get_default (); - var scheme = manager.get_scheme ("adwaita"); - var buffer = editor.buffer as GtkSource.Buffer; - buffer.style_scheme = scheme; - } - #endif + protected void update_style_scheme () { + var manager = GtkSource.StyleSchemeManager.get_default (); + var scheme = manager.get_scheme ("adwaita"); + var buffer = editor.buffer as GtkSource.Buffer; + buffer.style_scheme = scheme; + } protected Overlay overlay; protected Label placeholder; diff --git a/src/Dialogs/Composer/meson.build b/src/Dialogs/Composer/meson.build index 42a4b7b45..bab710a6f 100644 --- a/src/Dialogs/Composer/meson.build +++ b/src/Dialogs/Composer/meson.build @@ -7,6 +7,4 @@ sources += files( 'PollPage.vala', ) -if gtksourceview_dep.found () - subdir('Completion') -endif \ No newline at end of file +subdir('Completion') diff --git a/src/Dialogs/ProfileEdit.vala b/src/Dialogs/ProfileEdit.vala index 2febdd673..caaff2729 100644 --- a/src/Dialogs/ProfileEdit.vala +++ b/src/Dialogs/ProfileEdit.vala @@ -100,7 +100,7 @@ public class Tuba.Dialogs.ProfileEdit : Adw.Window { adapter.enabled = true; #endif - #if GSPELL && (MISSING_GTKSOURCEVIEW || !LIBSPELLING) + #if GSPELL && !LIBSPELLING var gspell_view = Gspell.TextView.get_from_gtk_text_view (bio_text_view); gspell_view.basic_setup (); #endif From e1c9657c4c24e2588fa40b07fe8122e6f95d60b3 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Tue, 8 Aug 2023 06:24:17 +0300 Subject: [PATCH 17/76] chore(meson): bump gtk version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 47626bb6f..1899ea9da 100644 --- a/meson.build +++ b/meson.build @@ -60,7 +60,7 @@ asresources = gnome.compile_resources( c_name: 'as', ) -libgtk_dep = dependency('gtk4', version: '>=4.0.0', required: true) +libgtk_dep = dependency('gtk4', version: '>=4.10.0', required: true) libadwaita_dep = dependency('libadwaita-1', version: '>=1.2', required: true) gtksourceview_dep = dependency('gtksourceview-5', required: true, version: '>=5.6.0') libwebp_dep = dependency('libwebp', required: false) From 4cefc8816119e4dde394f9421288a14b006ed12a Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Thu, 10 Aug 2023 10:02:27 +0300 Subject: [PATCH 18/76] feat(Lists): Builder files --- data/gresource.xml | 1 + data/ui/dialogs/list_edit.ui | 78 ++++++++++++++++ po/POTFILES | 1 + src/Dialogs/ListEdit.vala | 170 +++++++++++++++++++++++++++++++++++ src/Dialogs/meson.build | 1 + src/Views/Lists.vala | 162 +-------------------------------- 6 files changed, 252 insertions(+), 161 deletions(-) create mode 100644 data/ui/dialogs/list_edit.ui create mode 100644 src/Dialogs/ListEdit.vala diff --git a/data/gresource.xml b/data/gresource.xml index b3009e601..668991f47 100644 --- a/data/gresource.xml +++ b/data/gresource.xml @@ -84,6 +84,7 @@ ui/widgets/announcement.ui ui/widgets/status.ui ui/widgets/votebox.ui + ui/dialogs/list_edit.ui ui/dialogs/new_account.ui ui/dialogs/compose.ui ui/dialogs/main.ui diff --git a/data/ui/dialogs/list_edit.ui b/data/ui/dialogs/list_edit.ui new file mode 100644 index 000000000..8ca9ba232 --- /dev/null +++ b/data/ui/dialogs/list_edit.ui @@ -0,0 +1,78 @@ + + + + + + tuba-people-symbolic + Members + + + Remove Members + + + + + diff --git a/po/POTFILES b/po/POTFILES index 37c5be03d..9c07f9788 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -14,6 +14,7 @@ ./data/ui/dialogs/new_account.ui ./data/ui/dialogs/main.ui ./data/ui/dialogs/profile_edit.ui +./data/ui/dialogs/list_edit.ui ./data/gtk/help-overlay.ui ./data/gtk/dropdown/expiration_title.ui ./data/gtk/dropdown/expiration.ui diff --git a/src/Dialogs/ListEdit.vala b/src/Dialogs/ListEdit.vala new file mode 100644 index 000000000..8e36ed8d7 --- /dev/null +++ b/src/Dialogs/ListEdit.vala @@ -0,0 +1,170 @@ +[GtkTemplate (ui = "/dev/geopjr/Tuba/ui/dialogs/list_edit.ui")] +public class Tuba.Dialogs.ListEdit : Adw.PreferencesWindow { + ~ListEdit () { + message ("Destroying ListEdit"); + } + + public enum RepliesPolicy { + None, + List, + Followed; + + public string to_string () { + switch (this) { + case List: + return "list"; + case Followed: + return "followed"; + default: + return "none"; + } + } + + public static RepliesPolicy from_string (string policy) { + switch (policy) { + case "list": + return List; + case "followed": + return Followed; + default: + return None; + } + } + } + + private API.List list { get; set; } + private Gee.ArrayList memebers_to_be_removed { get; default=new Gee.ArrayList (); } + public RepliesPolicy replies_policy_active { get; private set; default=RepliesPolicy.None; } + + [GtkChild] unowned Adw.EntryRow title_row; + [GtkChild] unowned Gtk.CheckButton none_radio; + [GtkChild] unowned Gtk.CheckButton list_radio; + [GtkChild] unowned Gtk.CheckButton followed_radio; + [GtkChild] unowned Adw.PreferencesPage members_page; + [GtkChild] unowned Adw.PreferencesGroup members_group; + + public string list_title { + get { + return title_row.text; + } + } + + public ListEdit (API.List t_list) { + list = t_list; + title_row.text = t_list.title; + + update_active_radio_button (RepliesPolicy.from_string (t_list.replies_policy)); + update_members (); + } + + [GtkCallback] + private void on_radio_toggled () { + if (none_radio.active) { + replies_policy_active = RepliesPolicy.None; + return; + } + + if (list_radio.active) { + replies_policy_active = RepliesPolicy.List; + return; + } + + if (followed_radio.active) { + replies_policy_active = RepliesPolicy.Followed; + return; + } + } + + private void update_active_radio_button (RepliesPolicy replies_policy) { + switch (replies_policy) { + case RepliesPolicy.List: + list_radio.active = true; + break; + case RepliesPolicy.Followed: + followed_radio.active = true; + break; + default: + none_radio.active = true; + break; + } + + on_radio_toggled (); + } + + private void update_members () { + new Request.GET (@"/api/v1/lists/$(list.id)/accounts") + .with_account (accounts.active) + .then ((sess, msg, in_stream) => { + var parser = Network.get_parser_from_inputstream (in_stream); + if (Network.get_array_size (parser) > 0) { + this.add (members_page); + + Network.parse_array (msg, parser, node => { + var member = API.Account.from (node); + var avi = new Widgets.Avatar () { + account = member, + size = 32 + }; + + var m_switch = new Gtk.Switch () { + active = true, + state = true, + valign = Gtk.Align.CENTER, + halign = Gtk.Align.CENTER + }; + + m_switch.state_set.connect ((state) => { + if (!state) { + memebers_to_be_removed.add (member.id); + } else if (memebers_to_be_removed.contains (member.id)) { + memebers_to_be_removed.remove (member.id); + } + + return state; + }); + + var member_row = new Adw.ActionRow () { + title = member.full_handle + }; + + member_row.add_prefix (avi); + member_row.add_suffix (m_switch); + + members_group.add (member_row); + }); + } + }) + .exec (); + } + + [GtkCallback] + private bool on_close () { + on_apply (); + hide (); + destroy (); + + return false; + } + + private void on_apply () { + if (list.title != list_title || RepliesPolicy.from_string (list.replies_policy) != replies_policy_active) { + var replies_policy_string = replies_policy_active.to_string (); + new Request.PUT (@"/api/v1/lists/$(list.id)") + .with_account (accounts.active) + .with_param ("title", list_title) + .with_param ("replies_policy", replies_policy_string) + .then (() => { + list.title = list_title; + list.replies_policy = replies_policy_string; + }) + .exec (); + } + + if (memebers_to_be_removed.size > 0) { + var id_array = Request.array2string (memebers_to_be_removed, "account_ids"); + new Request.DELETE (@"/api/v1/lists/$(list.id)/accounts/?$id_array") + .with_account (accounts.active) + .exec (); + } + } +} diff --git a/src/Dialogs/meson.build b/src/Dialogs/meson.build index 0cd1b8ce8..c4aca18df 100644 --- a/src/Dialogs/meson.build +++ b/src/Dialogs/meson.build @@ -1,4 +1,5 @@ sources += files( + 'ListEdit.vala', 'MainWindow.vala', 'NewAccount.vala', 'Preferences.vala', diff --git a/src/Views/Lists.vala b/src/Views/Lists.vala index 07d824eb0..ae3508166 100644 --- a/src/Views/Lists.vala +++ b/src/Views/Lists.vala @@ -78,169 +78,9 @@ public class Tuba.Views.Lists : Views.Timeline { } public Adw.PreferencesWindow create_edit_preferences_window (API.List t_list) { - var edit_preferences_window = new Adw.PreferencesWindow () { - modal = true, - title = _("Edit \"%s\"").printf (t_list.title), + return new Dialogs.ListEdit (t_list) { transient_for = app.main_window }; - var list_settings_page_general = new Adw.PreferencesPage () { - icon_name = "tuba-gear-symbolic", - title = _("General") - }; - var info_group = new Adw.PreferencesGroup () { - title = _("Info") - }; - var title_row = new Adw.EntryRow () { - input_purpose = InputPurpose.FREE_FORM, - title = _("List Name"), - text = t_list.title - }; - info_group.add (title_row); - list_settings_page_general.add (info_group); - - string? replies_policy_active = null; - if (t_list.replies_policy != null) { - var replies_group = new Adw.PreferencesGroup () { - title = _("Replies Policy"), - description = _("Show member replies to") - }; - var none_radio = new CheckButton (); - var none_row = new Adw.ActionRow () { - title = _("Nobody"), - activatable_widget = none_radio - }; - none_row.add_prefix (none_radio); - none_radio.toggled.connect (() => { - if (none_radio.active) - replies_policy_active = "none"; - }); - - var list_radio = new CheckButton (); - list_radio.group = none_radio; - var list_row = new Adw.ActionRow () { - title = _("Other members of the list"), - activatable_widget = list_radio - }; - list_row.add_prefix (list_radio); - list_radio.toggled.connect (() => { - if (list_radio.active) - replies_policy_active = "list"; - }); - - var followed_radio = new CheckButton (); - followed_radio.group = none_radio; - var followed_row = new Adw.ActionRow () { - title = _("Any followed user"), - activatable_widget = followed_radio - }; - followed_row.add_prefix (followed_radio); - followed_radio.toggled.connect (() => { - if (followed_radio.active) - replies_policy_active = "followed"; - }); - - switch (t_list.replies_policy) { - case "none": - none_radio.active = true; - break; - case "followed": - followed_radio.active = true; - break; - default: - list_radio.active = true; - break; - } - - replies_group.add (none_row); - replies_group.add (list_row); - replies_group.add (followed_row); - - list_settings_page_general.add (replies_group); - } - - var to_remove = new Gee.ArrayList (); - new Request.GET (@"/api/v1/lists/$(t_list.id)/accounts") - .with_account (accounts.active) - .then ((sess, msg, in_stream) => { - var parser = Network.get_parser_from_inputstream (in_stream); - if (Network.get_array_size (parser) > 0) { - var list_settings_page_members = new Adw.PreferencesPage () { - icon_name = "tuba-people-symbolic", - title = _("Members") - }; - - var rm_group = new Adw.PreferencesGroup () { - title = _("Remove Members") - }; - - Network.parse_array (msg, parser, node => { - var member = API.Account.from (node); - var avi = new Widgets.Avatar () { - account = member, - size = 32 - }; - var m_switch = new Switch () { - active = true, - state = true, - valign = Align.CENTER, - halign = Align.CENTER - }; - m_switch.state_set.connect ((x) => { - if (!x) { - to_remove.add (member.id); - } else if (to_remove.contains (member.id)) { - to_remove.remove (member.id); - } - - return x; - }); - - var member_row = new Adw.ActionRow () { - title = member.full_handle - }; - member_row.add_prefix (avi); - member_row.add_suffix (m_switch); - - rm_group.add (member_row); - }); - - list_settings_page_members.add (rm_group); - edit_preferences_window.add (list_settings_page_members); - } - }) - .exec (); - - edit_preferences_window.add (list_settings_page_general); - - edit_preferences_window.close_request.connect (() => { - on_apply (t_list, title_row.text, replies_policy_active, to_remove); - edit_preferences_window.hide (); - edit_preferences_window.destroy (); - return false; - }); - - return edit_preferences_window; - } - - public void on_apply (API.List t_list, string title, string? replies_policy, Gee.ArrayList to_remove) { - if (t_list.title != title || t_list.replies_policy != replies_policy) { - this.list.title = title; - this.list.replies_policy = replies_policy; - new Request.PUT (@"/api/v1/lists/$(t_list.id)") - .with_account (accounts.active) - .with_param ("title", title) - .with_param ("replies_policy", replies_policy) - .then (() => {}) - .exec (); - } - - if (to_remove.size > 0) { - var id_array = Request.array2string (to_remove, "account_ids"); - new Request.DELETE (@"/api/v1/lists/$(t_list.id)/accounts/?$id_array") - .with_account (accounts.active) - .then (() => {}) - .exec (); - } } public virtual signal void open () { From 33669d36b0e8cf038a738e9158653e0daee43587 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Thu, 10 Aug 2023 10:18:30 +0300 Subject: [PATCH 19/76] chore: lint --- src/Dialogs/ListEdit.vala | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Dialogs/ListEdit.vala b/src/Dialogs/ListEdit.vala index 8e36ed8d7..2ab378afd 100644 --- a/src/Dialogs/ListEdit.vala +++ b/src/Dialogs/ListEdit.vala @@ -5,15 +5,15 @@ public class Tuba.Dialogs.ListEdit : Adw.PreferencesWindow { } public enum RepliesPolicy { - None, - List, - Followed; + NONE, + LIST, + FOLLOWED; public string to_string () { switch (this) { - case List: + case LIST: return "list"; - case Followed: + case FOLLOWED: return "followed"; default: return "none"; @@ -23,18 +23,18 @@ public class Tuba.Dialogs.ListEdit : Adw.PreferencesWindow { public static RepliesPolicy from_string (string policy) { switch (policy) { case "list": - return List; + return LIST; case "followed": - return Followed; + return FOLLOWED; default: - return None; + return NONE; } } } private API.List list { get; set; } private Gee.ArrayList memebers_to_be_removed { get; default=new Gee.ArrayList (); } - public RepliesPolicy replies_policy_active { get; private set; default=RepliesPolicy.None; } + public RepliesPolicy replies_policy_active { get; private set; default=RepliesPolicy.NONE; } [GtkChild] unowned Adw.EntryRow title_row; [GtkChild] unowned Gtk.CheckButton none_radio; @@ -60,27 +60,27 @@ public class Tuba.Dialogs.ListEdit : Adw.PreferencesWindow { [GtkCallback] private void on_radio_toggled () { if (none_radio.active) { - replies_policy_active = RepliesPolicy.None; + replies_policy_active = RepliesPolicy.NONE; return; } if (list_radio.active) { - replies_policy_active = RepliesPolicy.List; + replies_policy_active = RepliesPolicy.LIST; return; } if (followed_radio.active) { - replies_policy_active = RepliesPolicy.Followed; + replies_policy_active = RepliesPolicy.FOLLOWED; return; } } private void update_active_radio_button (RepliesPolicy replies_policy) { switch (replies_policy) { - case RepliesPolicy.List: + case RepliesPolicy.LIST: list_radio.active = true; break; - case RepliesPolicy.Followed: + case RepliesPolicy.FOLLOWED: followed_radio.active = true; break; default: From 2128db1f9f95ce55a6d046e9852391dd4e62ed02 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Thu, 10 Aug 2023 13:14:56 +0300 Subject: [PATCH 20/76] revert: "chore(Base): move badge properties to Timeline" This reverts commit 585d045174b6073e56b2fea166a6e3f1a00761ac. --- src/Views/Base.vala | 3 ++- src/Views/Timeline.vala | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Views/Base.vala b/src/Views/Base.vala index f049f62bb..4504d135c 100644 --- a/src/Views/Base.vala +++ b/src/Views/Base.vala @@ -7,9 +7,11 @@ public class Tuba.Views.Base : Box { public string? icon { get; set; default = null; } public string label { get; set; default = ""; } + public bool needs_attention { get; set; default = false; } public bool is_main { get; set; default = false; } public bool allow_nesting { get; set; default = false; } public bool is_sidebar_item { get; set; default = false; } + public int badge_number { get; set; default = 0; } protected SimpleActionGroup actions { get; set; default = new SimpleActionGroup (); } private bool _current = false; @@ -88,7 +90,6 @@ public class Tuba.Views.Base : Box { scroll_to_top.clicked.connect (on_scroll_to_top); } - ~Base () { message (@"Destroying base $label"); } diff --git a/src/Views/Timeline.vala b/src/Views/Timeline.vala index 89c735562..737eaae72 100644 --- a/src/Views/Timeline.vala +++ b/src/Views/Timeline.vala @@ -7,8 +7,6 @@ public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase public bool is_public { get; construct set; default = false; } public Type accepts { get; set; default = typeof (API.Status); } public bool use_queue { get; set; default = true; } - public int badge_number { get; set; default = 0; } - public bool needs_attention { get; set; default = false; } protected InstanceAccount? account { get; set; default = null; } @@ -86,8 +84,12 @@ public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase construct_streamable (); stream_event[InstanceAccount.EVENT_NEW_POST].connect (on_new_post); - stream_event[InstanceAccount.EVENT_EDIT_POST].connect (on_edit_post); - stream_event[InstanceAccount.EVENT_DELETE_POST].connect (on_delete_post); + + if (accepts == typeof (API.Status)) { + stream_event[InstanceAccount.EVENT_EDIT_POST].connect (on_edit_post); + stream_event[InstanceAccount.EVENT_DELETE_POST].connect (on_delete_post); + } + settings.notify["show-spoilers"].connect (on_refresh); settings.notify["hide-preview-cards"].connect (on_refresh); settings.notify["enlarge-custom-emojis"].connect (on_refresh); @@ -257,7 +259,6 @@ public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase } public virtual void on_edit_post (Streamable.Event ev) { - if (accepts != typeof (API.Status)) return; try { var entity = Entity.from_json (accepts, ev.get_node ()); var entity_id = ((API.Status)entity).id; @@ -275,7 +276,6 @@ public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase } public virtual void on_delete_post (Streamable.Event ev) { - if (accepts != typeof (API.Status)) return; try { var status_id = ev.get_string (); From 99f1ab352599bbc77c01d90cab7723e7e11b713c Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Thu, 10 Aug 2023 13:47:25 +0300 Subject: [PATCH 21/76] feat(PreviewCard): simplify, builder files --- data/gresource.xml | 1 + data/ui/widgets/preview_card.ui | 74 ++++++++++++++++++++++ src/Widgets/PreviewCard.vala | 109 ++++++++++++-------------------- 3 files changed, 114 insertions(+), 70 deletions(-) create mode 100644 data/ui/widgets/preview_card.ui diff --git a/data/gresource.xml b/data/gresource.xml index 668991f47..4f9a33731 100644 --- a/data/gresource.xml +++ b/data/gresource.xml @@ -84,6 +84,7 @@ ui/widgets/announcement.ui ui/widgets/status.ui ui/widgets/votebox.ui + ui/widgets/preview_card.ui ui/dialogs/list_edit.ui ui/dialogs/new_account.ui ui/dialogs/compose.ui diff --git a/data/ui/widgets/preview_card.ui b/data/ui/widgets/preview_card.ui new file mode 100644 index 000000000..20646eb94 --- /dev/null +++ b/data/ui/widgets/preview_card.ui @@ -0,0 +1,74 @@ + + + + + diff --git a/src/Widgets/PreviewCard.vala b/src/Widgets/PreviewCard.vala index 14959f981..4b8d1b512 100644 --- a/src/Widgets/PreviewCard.vala +++ b/src/Widgets/PreviewCard.vala @@ -1,16 +1,24 @@ +[GtkTemplate (ui = "/dev/geopjr/Tuba/ui/widgets/preview_card.ui")] public class Tuba.Widgets.PreviewCard : Gtk.Button { construct { this.css_classes = {"preview_card", "frame"}; } + [GtkChild] unowned Gtk.Box box; + [GtkChild] unowned Gtk.Label author_label; + [GtkChild] unowned Gtk.Label title_label; + [GtkChild] unowned Gtk.Label description_label; + [GtkChild] unowned Gtk.Label used_times_label; + public PreviewCard (API.PreviewCard card_obj) { var is_video = card_obj.kind == "video"; - Gtk.Widget card_container = new Gtk.Grid (); - - if (is_video) - card_container = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); + if (is_video) { + box.orientation = Gtk.Orientation.VERTICAL; + box.homogeneous = false; + } + Gtk.Widget image_widget; if (card_obj.image != null) { var image = new Gtk.Picture () { width_request = 25, @@ -18,7 +26,8 @@ public class Tuba.Widgets.PreviewCard : Gtk.Button { }; image_cache.request_paintable (card_obj.image, (is_loaded, paintable) => { - image.paintable = paintable; + if (is_loaded) + image.paintable = paintable; }); if (is_video) { @@ -27,41 +36,38 @@ public class Tuba.Widgets.PreviewCard : Gtk.Button { var overlay = new Gtk.Overlay () { vexpand = true, - hexpand = true + hexpand = true, + child = image }; - var icon = new Gtk.Image () { + overlay.add_overlay (new Gtk.Image () { valign = Gtk.Align.CENTER, halign = Gtk.Align.CENTER, css_classes = {"osd", "circular", "attachment-overlay-icon"}, icon_name = "media-playback-start-symbolic", icon_size = Gtk.IconSize.LARGE - }; + }); - overlay.add_overlay (icon); - overlay.child = image; - ((Gtk.Box) card_container).append (overlay); + image_widget = overlay; } else { image.height_request = 70; image.add_css_class ("preview_card_image"); - ((Gtk.Grid) card_container).attach (image, 1, 1); - } - } else if (!is_video) { - ((Gtk.Grid) card_container).attach (new Gtk.Image.from_icon_name ("tuba-paper-symbolic") { + image_widget = image; + } + } else { + image_widget = new Gtk.Image.from_icon_name ( + is_video ? "media-playback-start-symbolic" : "tuba-paper-symbolic" + ) { height_request = 70, width_request = 70, icon_size = Gtk.IconSize.LARGE - }, 1, 1); - } + }; - var body = new Gtk.Box (Gtk.Orientation.VERTICAL, 3) { - margin_top = 12, - margin_bottom = 12, - margin_end = 12, - margin_start = 12, - valign = Gtk.Align.CENTER - }; + box.orientation = Gtk.Orientation.HORIZONTAL; + box.homogeneous = false; + } + box.prepend (image_widget); var author = card_obj.provider_name; if (author == "") { @@ -71,40 +77,16 @@ public class Tuba.Widgets.PreviewCard : Gtk.Button { if (host != null) author = host; } catch {} } - - var author_label = new Gtk.Label (author) { - ellipsize = Pango.EllipsizeMode.END, - halign = Gtk.Align.START, - css_classes = {"dim-label", "caption"}, - tooltip_text = author, - single_line_mode = true - }; - body.append (author_label); + author_label.label = author; if (card_obj.title != "") { - var title_label = new Gtk.Label (card_obj.title) { - ellipsize = Pango.EllipsizeMode.END, - halign = Gtk.Align.FILL, - xalign = 0.0f, - tooltip_text = card_obj.title, - lines = 2, - wrap = true, - wrap_mode = Pango.WrapMode.WORD_CHAR - }; - body.append (title_label); + title_label.label = title_label.tooltip_text = card_obj.title; + title_label.visible = true; } - Gtk.Label? description_label = null; if (card_obj.description != "") { - description_label = new Gtk.Label (card_obj.description) { - ellipsize = Pango.EllipsizeMode.END, - halign = Gtk.Align.FILL, - xalign = 0.0f, - css_classes = {"caption"}, - tooltip_text = card_obj.description, - single_line_mode = true - }; - body.append (description_label); + description_label.label = description_label.tooltip_text = card_obj.description; + description_label.visible = true; } if (card_obj.kind == "link" && card_obj.history != null && card_obj.history.size > 0) { @@ -114,12 +96,13 @@ public class Tuba.Widgets.PreviewCard : Gtk.Button { this.clicked.connect (() => Host.open_uri (card_obj.url)); - if (description_label != null) { + if (description_label.visible) { if (description_label.label.length > 109) description_label.label = description_label.label.replace ("\n", " ").substring (0, 109) + "…"; description_label.single_line_mode = false; description_label.ellipsize = Pango.EllipsizeMode.NONE; description_label.wrap = true; + description_label.wrap_mode = Pango.WrapMode.WORD_CHAR; } var last_history_entry = card_obj.history.get (0); @@ -138,22 +121,8 @@ public class Tuba.Widgets.PreviewCard : Gtk.Button { .printf (total_uses, total_accounts); } - var used_times_label = new Gtk.Label (subtitle) { - halign = Gtk.Align.FILL, - xalign = 0.0f, - css_classes = {"dim-label", "caption"}, - wrap = true - }; - - body.append (used_times_label); + used_times_label.label = subtitle; + used_times_label.visible = true; } - - if (is_video) { - ((Gtk.Box) card_container).append (body); - } else { - ((Gtk.Grid) card_container).attach (body, 2, 1, 2); - } - - this.child = card_container; } } From 03f3f33fced07cff37252618ef52c92471224b35 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Thu, 10 Aug 2023 17:44:56 +0300 Subject: [PATCH 22/76] feat: do not use 'using' assuming the namespace is difficult for newcomers as one needs to have experience not only in all the namespaces but also the entire codebase --- src/API/Entity.vala | 6 +- src/API/List.vala | 3 - src/API/Poll.vala | 11 +-- src/API/SearchResults.vala | 8 +- src/API/Status.vala | 12 +-- src/API/Tag.vala | 4 +- src/Application.vala | 4 +- src/Dialogs/Composer/AttachmentsPage.vala | 26 +++--- .../Completion/CompletionProvider.vala | 8 +- .../Composer/Completion/EmojiProvider.vala | 2 - .../Composer/Completion/HandleProvider.vala | 2 - .../Composer/Completion/HashtagProvider.vala | 2 - src/Dialogs/Composer/Dialog.vala | 5 +- src/Dialogs/Composer/EditorPage.vala | 62 ++++++------- src/Dialogs/Composer/Page.vala | 18 ++-- src/Dialogs/MainWindow.vala | 15 ++- src/Dialogs/NewAccount.vala | 12 +-- src/Dialogs/Preferences.vala | 32 +++---- src/Dialogs/Saveable.vala | 4 +- src/Services/Accounts/AccountStore.vala | 6 +- src/Services/Accounts/InstanceAccount.vala | 9 +- src/Services/Accounts/SecretAccountStore.vala | 10 +- src/Services/Cache/AbstractCache.vala | 10 +- src/Services/Cache/ImageCache.vala | 14 ++- src/Services/Network/Network.vala | 9 +- src/Services/Network/Request.vala | 11 +-- src/Services/Network/Streams.vala | 13 +-- src/Services/Settings.vala | 2 - src/Utils/DateTime.vala | 2 - src/Utils/Host.vala | 5 +- src/Views/Base.vala | 30 +++--- src/Views/ContentBase.vala | 12 +-- src/Views/Lists.vala | 34 ++++--- src/Views/Main.vala | 6 +- src/Views/Notifications.vala | 3 - src/Views/Profile.vala | 52 +++++------ src/Views/Search.vala | 10 +- src/Views/Sidebar.vala | 44 +++++---- src/Views/TabbedBase.vala | 6 +- src/Views/Thread.vala | 2 - src/Views/Timeline.vala | 3 - src/Widgets/Attachment/Box.vala | 12 +-- src/Widgets/Attachment/Image.vala | 11 +-- src/Widgets/Attachment/Item.vala | 48 +++++----- src/Widgets/Avatar.vala | 13 +-- src/Widgets/Conversation.vala | 2 - src/Widgets/Emoji.vala | 7 +- src/Widgets/LockableToggleButton.vala | 4 +- src/Widgets/MarkupView.vala | 6 +- src/Widgets/Notification.vala | 2 - src/Widgets/RelationshipButton.vala | 4 +- src/Widgets/RichLabel.vala | 5 +- src/Widgets/Status.vala | 91 +++++++++---------- src/Widgets/StatusActionButton.vala | 2 - src/Widgets/VoteBox.vala | 22 ++--- src/Widgets/VoteCheckButton.vala | 7 +- tests/Celebrate.test.vala | 2 - tests/DateTime.test.vala | 2 - tests/Html.test.vala | 2 - tests/Tracking.test.vala | 2 - tests/Units.test.vala | 2 - 61 files changed, 314 insertions(+), 461 deletions(-) diff --git a/src/API/Entity.vala b/src/API/Entity.vala index 5046ad7b8..2477bedf7 100644 --- a/src/API/Entity.vala +++ b/src/API/Entity.vala @@ -1,5 +1,3 @@ -using Json; - public class Tuba.Entity : GLib.Object, Widgetizable, Json.Serializable { public virtual bool is_local (InstanceAccount account) { @@ -164,7 +162,7 @@ public class Tuba.Entity : GLib.Object, Widgetizable, Json.Serializable { static Json.Node ser_list (string prop, Value val, ParamSpec spec) { var list = (Gee.ArrayList) val; if (list == null) - return new Json.Node (NodeType.NULL); + return new Json.Node (Json.NodeType.NULL); var arr = new Json.Array (); list.@foreach (e => { @@ -173,7 +171,7 @@ public class Tuba.Entity : GLib.Object, Widgetizable, Json.Serializable { return true; }); - var node = new Json.Node (NodeType.ARRAY); + var node = new Json.Node (Json.NodeType.ARRAY); node.set_array (arr); return node; } diff --git a/src/API/List.vala b/src/API/List.vala index 8192fb2a6..89592811c 100644 --- a/src/API/List.vala +++ b/src/API/List.vala @@ -1,7 +1,4 @@ -using Gtk; - public class Tuba.API.List : Entity, Widgetizable { - public string id { get; set; } public string title { get; set; } public string? replies_policy { get; set; default = null; } diff --git a/src/API/Poll.vala b/src/API/Poll.vala index 7eab52bfd..261499a81 100644 --- a/src/API/Poll.vala +++ b/src/API/Poll.vala @@ -1,6 +1,3 @@ -using Gee; -using Json; - public class Tuba.API.Poll : GLib.Object, Json.Serializable { public string id { get; set; } public string expires_at { get; set; } @@ -8,8 +5,8 @@ public class Tuba.API.Poll : GLib.Object, Json.Serializable { public bool multiple { get; set; } public int64 votes_count { get; set; } public bool voted { get; set; default = true;} - public ArrayList own_votes { get; set; } - public ArrayList? options { get; set; default = null; } + public Gee.ArrayList own_votes { get; set; } + public Gee.ArrayList? options { get; set; default = null; } public Poll (string _id) { id = _id; @@ -49,8 +46,8 @@ public class Tuba.API.Poll : GLib.Object, Json.Serializable { } public static Request vote ( InstanceAccount acc, - ArrayList options, - ArrayList selection, + Gee.ArrayList options, + Gee.ArrayList selection, string id ) { message (@"Voting poll $(id)…"); diff --git a/src/API/SearchResults.vala b/src/API/SearchResults.vala index 19a9c885a..24e38c54b 100644 --- a/src/API/SearchResults.vala +++ b/src/API/SearchResults.vala @@ -1,10 +1,8 @@ -using Gee; - public class Tuba.API.SearchResults : Entity { - public ArrayList accounts { get; set; } - public ArrayList statuses { get; set; } - public ArrayList hashtags { get; set; } + public Gee.ArrayList accounts { get; set; } + public Gee.ArrayList statuses { get; set; } + public Gee.ArrayList hashtags { get; set; } public static SearchResults from (Json.Node node) throws Error { return Entity.from_json (typeof (SearchResults), node) as SearchResults; diff --git a/src/API/Status.vala b/src/API/Status.vala index b155b6355..8a1b40676 100644 --- a/src/API/Status.vala +++ b/src/API/Status.vala @@ -1,5 +1,3 @@ -using Gee; - public class Tuba.API.Status : Entity, Widgetizable { ~Status () { @@ -29,11 +27,11 @@ public class Tuba.API.Status : Entity, Widgetizable { public API.Status? reblog { get; set; default = null; } public API.Status? quote { get; set; default = null; } // public API.Akkoma? akkoma { get; set; default = null; } - public ArrayList? mentions { get; set; default = null; } - public ArrayList? reactions { get; set; default = null; } - public ArrayList? emoji_reactions { get; set; default = null; } + public Gee.ArrayList? mentions { get; set; default = null; } + public Gee.ArrayList? reactions { get; set; default = null; } + public Gee.ArrayList? emoji_reactions { get; set; default = null; } public API.Pleroma.Status? pleroma { get; set; default = null; } - public ArrayList? media_attachments { get; set; default = null; } + public Gee.ArrayList? media_attachments { get; set; default = null; } public API.Poll? poll { get; set; default = null; } public Gee.ArrayList? emojis { get; set; } public API.PreviewCard? card { get; set; default = null; } @@ -76,7 +74,7 @@ public class Tuba.API.Status : Entity, Widgetizable { return res; } - public ArrayList? compat_status_reactions { + public Gee.ArrayList? compat_status_reactions { get { if (emoji_reactions != null) { return emoji_reactions; diff --git a/src/API/Tag.vala b/src/API/Tag.vala index 2ba01de34..1f24304a7 100644 --- a/src/API/Tag.vala +++ b/src/API/Tag.vala @@ -1,5 +1,3 @@ -using Gtk; - public class Tuba.API.Tag : Entity, Widgetizable { public string name { get; set; } @@ -35,7 +33,7 @@ public class Tuba.API.Tag : Entity, Widgetizable { return _("%d per week").printf (used_times); } - public override Widget to_widget () { + public override Gtk.Widget to_widget () { var w = new Adw.ActionRow () { title = @"#$name", activatable = true diff --git a/src/Application.vala b/src/Application.vala index 283c01459..33e8df271 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -1,5 +1,3 @@ -using Gtk; - namespace Tuba { public errordomain Oopsie { USER, @@ -293,7 +291,7 @@ namespace Tuba { application_name = Build.NAME, version = Build.VERSION, support_url = Build.SUPPORT_WEBSITE, - license_type = License.GPL_3_0_ONLY, + license_type = Gtk.License.GPL_3_0_ONLY, copyright = COPYRIGHT, developers = DEVELOPERS, artists = ARTISTS, diff --git a/src/Dialogs/Composer/AttachmentsPage.vala b/src/Dialogs/Composer/AttachmentsPage.vala index 9d705778c..f669c69d5 100644 --- a/src/Dialogs/Composer/AttachmentsPage.vala +++ b/src/Dialogs/Composer/AttachmentsPage.vala @@ -1,5 +1,3 @@ -using Gtk; - public class Tuba.AttachmentsPage : ComposerPage { ~AttachmentsPage () { context_menu.unparent (); @@ -36,7 +34,7 @@ public class Tuba.AttachmentsPage : ComposerPage { public GLib.ListStore attachments; public Adw.ToastOverlay toast_overlay; public bool media_sensitive { get; set; default = false; } - private FileFilter filter = new FileFilter () { + private Gtk.FileFilter filter = new Gtk.FileFilter () { name = _("All Supported Files") }; private Gee.ArrayList supported_mimes = new Gee.ArrayList.wrap (SUPPORTED_MIMES); @@ -52,7 +50,7 @@ public class Tuba.AttachmentsPage : ComposerPage { } } - protected PopoverMenu context_menu { get; set; } + protected Gtk.PopoverMenu context_menu { get; set; } private const GLib.ActionEntry[] ACTION_ENTRIES = { {"paste-from-clipboard", on_clipboard_paste} }; @@ -77,7 +75,7 @@ public class Tuba.AttachmentsPage : ComposerPage { var menu_model = new GLib.Menu (); menu_model.append (_("Paste"), "attachmentspage.paste-from-clipboard"); - context_menu = new PopoverMenu.from_model (menu_model); + context_menu = new Gtk.PopoverMenu.from_model (menu_model); context_menu.set_parent (this); var dnd_controller = new Gtk.DropTarget (typeof (Gdk.FileList), Gdk.DragAction.COPY); @@ -105,13 +103,13 @@ public class Tuba.AttachmentsPage : ComposerPage { private void on_click (int n_press, double x, double y) { if (!show_context_menu (x, y)) return; - click_controller.set_state (EventSequenceState.CLAIMED); + click_controller.set_state (Gtk.EventSequenceState.CLAIMED); } private void on_long_press (double x, double y) { if (!show_context_menu (x, y)) return; - long_press_controller.set_state (EventSequenceState.CLAIMED); + long_press_controller.set_state (Gtk.EventSequenceState.CLAIMED); } private bool show_context_menu (double x, double y) { @@ -207,7 +205,7 @@ public class Tuba.AttachmentsPage : ComposerPage { protected Adw.ViewStack stack; protected Adw.StatusPage empty_state; - protected ListBox list; + protected Gtk.ListBox list; protected Gtk.Button add_media_action_button; public override void dispose () { @@ -219,8 +217,8 @@ public class Tuba.AttachmentsPage : ComposerPage { public override void on_build () { base.on_build (); - var attach_button = new Button.with_label (_("Add Media")) { - halign = Align.CENTER, + var attach_button = new Gtk.Button.with_label (_("Add Media")) { + halign = Gtk.Align.CENTER, sensitive = accounts.active.instance_info.compat_status_max_media_attachments > 0, // Empty state css_classes = { "pill" } @@ -236,8 +234,8 @@ public class Tuba.AttachmentsPage : ComposerPage { }; // Non-empty state - list = new ListBox () { - selection_mode = SelectionMode.NONE + list = new Gtk.ListBox () { + selection_mode = Gtk.SelectionMode.NONE }; list.bind_model (attachments, on_create_list_item); @@ -312,7 +310,7 @@ public class Tuba.AttachmentsPage : ComposerPage { on_attachments_changed (); } - Widget on_create_list_item (Object item) { + Gtk.Widget on_create_list_item (Object item) { var attachment = item as API.Attachment; var attachment_widget = new AttachmentsPageAttachment ( attachment.id, @@ -368,7 +366,7 @@ public class Tuba.AttachmentsPage : ComposerPage { } void show_file_selector () { - var chooser = new FileDialog () { + var chooser = new Gtk.FileDialog () { // translators: Open file title = _("Open"), modal = true, diff --git a/src/Dialogs/Composer/Completion/CompletionProvider.vala b/src/Dialogs/Composer/Completion/CompletionProvider.vala index cab9e7918..88dc1cdcc 100644 --- a/src/Dialogs/Composer/Completion/CompletionProvider.vala +++ b/src/Dialogs/Composer/Completion/CompletionProvider.vala @@ -1,5 +1,3 @@ -using Gtk; - public abstract class Tuba.CompletionProvider: Object, GtkSource.CompletionProvider { public static GLib.ListStore EMPTY = new GLib.ListStore (typeof (Object)); // vala-lint=naming-convention @@ -35,8 +33,8 @@ public abstract class Tuba.CompletionProvider: Object, GtkSource.CompletionProvi } public virtual void activate (GtkSource.CompletionContext context, GtkSource.CompletionProposal proposal) { - TextIter start; - TextIter end; + Gtk.TextIter start; + Gtk.TextIter end; context.get_bounds (out start, out end); var buffer = start.get_buffer (); @@ -58,7 +56,7 @@ public abstract class Tuba.CompletionProvider: Object, GtkSource.CompletionProvi // If it's not capturing, // check if the character before the word // is the trigger - TextIter start; + Gtk.TextIter start; context.get_bounds (out start, null); if (start.backward_char ()) is_trigger (start, start.get_char ()); diff --git a/src/Dialogs/Composer/Completion/EmojiProvider.vala b/src/Dialogs/Composer/Completion/EmojiProvider.vala index b7e900c01..ba04456aa 100644 --- a/src/Dialogs/Composer/Completion/EmojiProvider.vala +++ b/src/Dialogs/Composer/Completion/EmojiProvider.vala @@ -1,5 +1,3 @@ -using Gtk; - public class Tuba.EmojiProvider: Tuba.CompletionProvider { public EmojiProvider () { diff --git a/src/Dialogs/Composer/Completion/HandleProvider.vala b/src/Dialogs/Composer/Completion/HandleProvider.vala index bee8feb62..051751a14 100644 --- a/src/Dialogs/Composer/Completion/HandleProvider.vala +++ b/src/Dialogs/Composer/Completion/HandleProvider.vala @@ -1,5 +1,3 @@ -using Gtk; - public class Tuba.HandleProvider: Tuba.CompletionProvider { public HandleProvider () { diff --git a/src/Dialogs/Composer/Completion/HashtagProvider.vala b/src/Dialogs/Composer/Completion/HashtagProvider.vala index f3b84cfd7..de798d23e 100644 --- a/src/Dialogs/Composer/Completion/HashtagProvider.vala +++ b/src/Dialogs/Composer/Completion/HashtagProvider.vala @@ -1,5 +1,3 @@ -using Gtk; - public class Tuba.HashtagProvider: Tuba.CompletionProvider { public HashtagProvider () { diff --git a/src/Dialogs/Composer/Dialog.vala b/src/Dialogs/Composer/Dialog.vala index af622e4a9..c53a8b652 100644 --- a/src/Dialogs/Composer/Dialog.vala +++ b/src/Dialogs/Composer/Dialog.vala @@ -1,6 +1,3 @@ -using Gtk; -using Gee; - [GtkTemplate (ui = "/dev/geopjr/Tuba/ui/dialogs/compose.ui")] public class Tuba.Dialogs.Compose : Adw.Window { public class BasicStatus : Object { @@ -256,7 +253,7 @@ public class Tuba.Dialogs.Compose : Adw.Window { } [GtkChild] unowned Adw.ViewSwitcherTitle title_switcher; - [GtkChild] unowned Button commit_button; + [GtkChild] unowned Gtk.Button commit_button; [GtkChild] unowned Adw.ViewStack stack; diff --git a/src/Dialogs/Composer/EditorPage.vala b/src/Dialogs/Composer/EditorPage.vala index 12ce9a469..9757f088a 100644 --- a/src/Dialogs/Composer/EditorPage.vala +++ b/src/Dialogs/Composer/EditorPage.vala @@ -1,5 +1,3 @@ -using Gtk; - public class Tuba.EditorPage : ComposerPage { protected int64 char_limit { get; set; default = 500; } @@ -24,9 +22,9 @@ public class Tuba.EditorPage : ComposerPage { install_overlay (status.status); install_visibility (status.visibility); install_languages (status.language); - add_button (new Gtk.Separator (Orientation.VERTICAL)); + add_button (new Gtk.Separator (Gtk.Orientation.VERTICAL)); install_cw (status.spoiler_text); - add_button (new Gtk.Separator (Orientation.VERTICAL)); + add_button (new Gtk.Separator (Gtk.Orientation.VERTICAL)); install_emoji_picker (); validate (); @@ -76,7 +74,7 @@ public class Tuba.EditorPage : ComposerPage { } protected GtkSource.View editor; - protected Label char_counter; + protected Gtk.Label char_counter; public void editor_grab_focus () { editor.grab_focus (); @@ -109,7 +107,7 @@ public class Tuba.EditorPage : ComposerPage { left_margin = 6, pixels_below_lines = 6, accepts_tab = false, - wrap_mode = WrapMode.WORD_CHAR + wrap_mode = Gtk.WrapMode.WORD_CHAR }; #if LIBSPELLING @@ -147,7 +145,7 @@ public class Tuba.EditorPage : ComposerPage { remaining_chars -= editor.buffer.get_char_count (); }); - char_counter = new Label (char_limit.to_string ()) { + char_counter = new Gtk.Label (char_limit.to_string ()) { margin_end = 6, tooltip_text = _("Characters Left"), css_classes = { "heading" } @@ -163,15 +161,15 @@ public class Tuba.EditorPage : ComposerPage { buffer.style_scheme = scheme; } - protected Overlay overlay; - protected Label placeholder; + protected Gtk.Overlay overlay; + protected Gtk.Label placeholder; protected void install_overlay (string t_content) { - overlay = new Overlay (); - placeholder = new Label (_("What's on your mind?")) { - valign = Align.START, - halign = Align.START, - justify = Justification.FILL, + overlay = new Gtk.Overlay (); + placeholder = new Gtk.Label (_("What's on your mind?")) { + valign = Gtk.Align.START, + halign = Gtk.Align.START, + justify = Gtk.Justification.FILL, margin_top = 6, margin_start = 6, wrap = true, @@ -179,7 +177,7 @@ public class Tuba.EditorPage : ComposerPage { }; overlay.add_overlay (placeholder); - overlay.child = new ScrolledWindow () { + overlay.child = new Gtk.ScrolledWindow () { hexpand = true, vexpand = true, child = editor @@ -189,10 +187,10 @@ public class Tuba.EditorPage : ComposerPage { editor.buffer.text = t_content; } - protected EmojiChooser emoji_picker; + protected Gtk.EmojiChooser emoji_picker; protected void install_emoji_picker () { - emoji_picker = new EmojiChooser (); - var emoji_button = new MenuButton () { + emoji_picker = new Gtk.EmojiChooser (); + var emoji_button = new Gtk.MenuButton () { icon_name = "tuba-smile-symbolic", popover = emoji_picker, tooltip_text = _("Emoji Picker") @@ -202,7 +200,7 @@ public class Tuba.EditorPage : ComposerPage { if (accounts.active.instance_emojis?.size > 0) { var custom_emoji_picker = new Widgets.CustomEmojiChooser (); - var custom_emoji_button = new MenuButton () { + var custom_emoji_button = new Gtk.MenuButton () { icon_name = "tuba-cat-symbolic", popover = custom_emoji_picker, tooltip_text = _("Custom Emoji Picker") @@ -217,8 +215,8 @@ public class Tuba.EditorPage : ComposerPage { editor.buffer.insert_at_cursor (emoji_unicode, emoji_unicode.data.length); } - protected ToggleButton cw_button; - protected Entry cw_entry; + protected Gtk.ToggleButton cw_button; + protected Gtk.Entry cw_entry; protected void install_cw (string? cw_text) { cw_entry = new Gtk.Entry () { @@ -235,7 +233,7 @@ public class Tuba.EditorPage : ComposerPage { revealer.add_css_class ("view"); content.prepend (revealer); - cw_button = new ToggleButton () { + cw_button = new Gtk.ToggleButton () { icon_name = "tuba-warning-symbolic", tooltip_text = _("Content Warning") }; @@ -256,8 +254,8 @@ public class Tuba.EditorPage : ComposerPage { - protected DropDown visibility_button; - protected DropDown language_button; + protected Gtk.DropDown visibility_button; + protected Gtk.DropDown language_button; private bool _edit_mode = false; public override bool edit_mode { @@ -272,10 +270,10 @@ public class Tuba.EditorPage : ComposerPage { } protected void install_visibility (string default_visibility = settings.default_post_visibility) { - visibility_button = new DropDown (accounts.active.visibility_list, null) { - expression = new PropertyExpression (typeof (InstanceAccount.Visibility), null, "name"), - factory = new BuilderListItemFactory.from_resource (null, @"$(Build.RESOURCES)gtk/dropdown/icon.ui"), - list_factory = new BuilderListItemFactory.from_resource (null, @"$(Build.RESOURCES)gtk/dropdown/full.ui"), + visibility_button = new Gtk.DropDown (accounts.active.visibility_list, null) { + expression = new Gtk.PropertyExpression (typeof (InstanceAccount.Visibility), null, "name"), + factory = new Gtk.BuilderListItemFactory.from_resource (null, @"$(Build.RESOURCES)gtk/dropdown/icon.ui"), + list_factory = new Gtk.BuilderListItemFactory.from_resource (null, @"$(Build.RESOURCES)gtk/dropdown/full.ui"), tooltip_text = _("Post Privacy"), sensitive = !edit_mode }; @@ -301,10 +299,10 @@ public class Tuba.EditorPage : ComposerPage { store.append (locale); } - language_button = new DropDown (store, null) { - expression = new PropertyExpression (typeof (Tuba.Locale), null, "name"), - factory = new BuilderListItemFactory.from_resource (null, @"$(Build.RESOURCES)gtk/dropdown/language_title.ui"), - list_factory = new BuilderListItemFactory.from_resource (null, @"$(Build.RESOURCES)gtk/dropdown/language.ui"), + language_button = new Gtk.DropDown (store, null) { + expression = new Gtk.PropertyExpression (typeof (Tuba.Locale), null, "name"), + factory = new Gtk.BuilderListItemFactory.from_resource (null, @"$(Build.RESOURCES)gtk/dropdown/language_title.ui"), + list_factory = new Gtk.BuilderListItemFactory.from_resource (null, @"$(Build.RESOURCES)gtk/dropdown/language.ui"), tooltip_text = _("Post Language"), enable_search = true }; diff --git a/src/Dialogs/Composer/Page.vala b/src/Dialogs/Composer/Page.vala index 9f5062e0d..d5673a1dd 100644 --- a/src/Dialogs/Composer/Page.vala +++ b/src/Dialogs/Composer/Page.vala @@ -1,5 +1,3 @@ -using Gtk; - public class Tuba.ComposerPage : Gtk.Box { public string title { get; set; } @@ -11,9 +9,9 @@ public class Tuba.ComposerPage : Gtk.Box { public weak Dialogs.Compose dialog; public Tuba.Dialogs.Compose.BasicStatus status; - ScrolledWindow scroller; - protected Box content; - protected ActionBar bottom_bar; + Gtk.ScrolledWindow scroller; + protected Gtk.Box content; + protected Gtk.ActionBar bottom_bar; private bool _action_bar_on_top = false; public bool action_bar_on_top { @@ -34,18 +32,18 @@ public class Tuba.ComposerPage : Gtk.Box { } construct { - orientation = Orientation.VERTICAL; + orientation = Gtk.Orientation.VERTICAL; - scroller = new ScrolledWindow () { + scroller = new Gtk.ScrolledWindow () { hexpand = true, vexpand = true }; append (scroller); - content = new Box (Orientation.VERTICAL, 0); + content = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); scroller.child = content; - bottom_bar = new ActionBar () { + bottom_bar = new Gtk.ActionBar () { visible = false }; @@ -56,7 +54,7 @@ public class Tuba.ComposerPage : Gtk.Box { } } - protected void add_button (Widget widget) { + protected void add_button (Gtk.Widget widget) { bottom_bar.pack_start (widget); widget.add_css_class ("flat"); diff --git a/src/Dialogs/MainWindow.vala b/src/Dialogs/MainWindow.vala index 8ae81a99b..341b6c3eb 100644 --- a/src/Dialogs/MainWindow.vala +++ b/src/Dialogs/MainWindow.vala @@ -1,6 +1,3 @@ -using Gtk; -using Gdk; - [GtkTemplate (ui = "/dev/geopjr/Tuba/ui/dialogs/main.ui")] public class Tuba.Dialogs.MainWindow: Adw.ApplicationWindow, Saveable { public const string ZOOM_CLASS = "ttl-scalable"; @@ -8,7 +5,7 @@ public class Tuba.Dialogs.MainWindow: Adw.ApplicationWindow, Saveable { [GtkChild] public unowned Adw.Flap flap; [GtkChild] unowned Adw.Leaflet leaflet; [GtkChild] unowned Views.Sidebar sidebar; - [GtkChild] unowned Stack main_stack; + [GtkChild] unowned Gtk.Stack main_stack; [GtkChild] unowned Views.MediaViewer media_viewer; Views.Base? last_view = null; @@ -46,7 +43,7 @@ public class Tuba.Dialogs.MainWindow: Adw.ApplicationWindow, Saveable { media_viewer.scroll_to (pos); } - public void show_media_viewer (string url, string? alt_text, bool video, Paintable? preview, int? pos) { + public void show_media_viewer (string url, string? alt_text, bool video, Gdk.Paintable? preview, int? pos) { if (!is_media_viewer_visible ()) { main_stack.visible_child_name = "media_viewer"; media_viewer.clear.connect (hide_media_viewer); @@ -59,7 +56,7 @@ public class Tuba.Dialogs.MainWindow: Adw.ApplicationWindow, Saveable { } } - public void show_media_viewer_single (string? url, Paintable? paintable) { + public void show_media_viewer_single (string? url, Gdk.Paintable? paintable) { if (paintable == null) return; if (!is_media_viewer_visible ()) { @@ -70,7 +67,7 @@ public class Tuba.Dialogs.MainWindow: Adw.ApplicationWindow, Saveable { media_viewer.set_single_paintable (url, paintable); } - public void show_media_viewer_remote_video (string url, Paintable? preview, string? user_friendly_url = null) { + public void show_media_viewer_remote_video (string url, Gdk.Paintable? preview, string? user_friendly_url = null) { if (!is_media_viewer_visible ()) { main_stack.visible_child_name = "media_viewer"; media_viewer.clear.connect (hide_media_viewer); @@ -91,7 +88,7 @@ public class Tuba.Dialogs.MainWindow: Adw.ApplicationWindow, Saveable { var clamp = new Adw.Clamp () { child = book_widget, tightening_threshold = 100, - valign = Align.START + valign = Gtk.Align.START }; var scroller = new Gtk.ScrolledWindow () { hexpand = true, @@ -204,7 +201,7 @@ public class Tuba.Dialogs.MainWindow: Adw.ApplicationWindow, Saveable { if (leaflet.child_transition_running) return; - Widget unused_child = null; + Gtk.Widget unused_child = null; while ((unused_child = leaflet.get_adjacent_child (Adw.NavigationDirection.FORWARD)) != null) { leaflet.remove (unused_child); unused_child.dispose (); diff --git a/src/Dialogs/NewAccount.vala b/src/Dialogs/NewAccount.vala index f46872ebc..66ca4f8d8 100644 --- a/src/Dialogs/NewAccount.vala +++ b/src/Dialogs/NewAccount.vala @@ -1,5 +1,3 @@ -using Gtk; - [GtkTemplate (ui = "/dev/geopjr/Tuba/ui/dialogs/new_account.ui")] public class Tuba.Dialogs.NewAccount: Adw.Window { const string AUTO_AUTH_DESCRIPTION = _("Allow access to your account in the browser."); @@ -13,15 +11,15 @@ public class Tuba.Dialogs.NewAccount: Adw.Window { protected InstanceAccount account { get; set; default = new InstanceAccount.empty (""); } [GtkChild] unowned Adw.Leaflet deck; - [GtkChild] unowned Box instance_step; - [GtkChild] unowned Box code_step; - [GtkChild] unowned Box done_step; + [GtkChild] unowned Gtk.Box instance_step; + [GtkChild] unowned Gtk.Box code_step; + [GtkChild] unowned Gtk.Box done_step; [GtkChild] unowned Adw.EntryRow instance_entry; - [GtkChild] unowned Label instance_entry_error; + [GtkChild] unowned Gtk.Label instance_entry_error; [GtkChild] unowned Adw.EntryRow code_entry; - [GtkChild] unowned Label code_entry_error; + [GtkChild] unowned Gtk.Label code_entry_error; [GtkChild] unowned Adw.StatusPage auth_page; [GtkChild] unowned Adw.StatusPage done_page; diff --git a/src/Dialogs/Preferences.vala b/src/Dialogs/Preferences.vala index 298b161ff..3cc35a86e 100644 --- a/src/Dialogs/Preferences.vala +++ b/src/Dialogs/Preferences.vala @@ -1,25 +1,23 @@ -using Gtk; - [GtkTemplate (ui = "/dev/geopjr/Tuba/ui/dialogs/preferences.ui")] public class Tuba.Dialogs.Preferences : Adw.PreferencesWindow { [GtkChild] unowned Adw.ComboRow scheme_combo_row; [GtkChild] unowned Adw.ComboRow post_visibility_combo_row; [GtkChild] unowned Adw.ComboRow default_language_combo_row; - [GtkChild] unowned Switch autostart; - [GtkChild] unowned Switch work_in_background; - [GtkChild] unowned SpinButton timeline_page_size; - [GtkChild] unowned Switch live_updates; - [GtkChild] unowned Switch public_live_updates; - [GtkChild] unowned Switch show_spoilers; - [GtkChild] unowned Switch hide_preview_cards; - [GtkChild] unowned Switch larger_font_size; - [GtkChild] unowned Switch larger_line_height; - [GtkChild] unowned Switch scale_emoji_hover; - [GtkChild] unowned Switch strip_tracking; - [GtkChild] unowned Switch letterbox_media; - [GtkChild] unowned Switch media_viewer_expand_pictures; - [GtkChild] unowned Switch enlarge_custom_emojis; + [GtkChild] unowned Gtk.Switch autostart; + [GtkChild] unowned Gtk.Switch work_in_background; + [GtkChild] unowned Gtk.SpinButton timeline_page_size; + [GtkChild] unowned Gtk.Switch live_updates; + [GtkChild] unowned Gtk.Switch public_live_updates; + [GtkChild] unowned Gtk.Switch show_spoilers; + [GtkChild] unowned Gtk.Switch hide_preview_cards; + [GtkChild] unowned Gtk.Switch larger_font_size; + [GtkChild] unowned Gtk.Switch larger_line_height; + [GtkChild] unowned Gtk.Switch scale_emoji_hover; + [GtkChild] unowned Gtk.Switch strip_tracking; + [GtkChild] unowned Gtk.Switch letterbox_media; + [GtkChild] unowned Gtk.Switch media_viewer_expand_pictures; + [GtkChild] unowned Gtk.Switch enlarge_custom_emojis; private bool lang_changed { get; set; default=false; } @@ -105,7 +103,7 @@ public class Tuba.Dialogs.Preferences : Adw.PreferencesWindow { store.append (locale); } - default_language_combo_row.list_factory = new BuilderListItemFactory.from_resource ( + default_language_combo_row.list_factory = new Gtk.BuilderListItemFactory.from_resource ( null, @"$(Build.RESOURCES)gtk/dropdown/language.ui" ); diff --git a/src/Dialogs/Saveable.vala b/src/Dialogs/Saveable.vala index 337edc690..5ef98cfa4 100644 --- a/src/Dialogs/Saveable.vala +++ b/src/Dialogs/Saveable.vala @@ -1,6 +1,4 @@ -using Gtk; - -public interface Tuba.Dialogs.Saveable : Window { +public interface Tuba.Dialogs.Saveable : Gtk.Window { protected void construct_saveable (GLib.Settings settings) { settings.bind ("window-w", this, "default-width", SettingsBindFlags.DEFAULT); settings.bind ("window-h", this, "default-height", SettingsBindFlags.DEFAULT); diff --git a/src/Services/Accounts/AccountStore.vala b/src/Services/Accounts/AccountStore.vala index a533023ef..14cef67e2 100644 --- a/src/Services/Accounts/AccountStore.vala +++ b/src/Services/Accounts/AccountStore.vala @@ -1,11 +1,9 @@ -using Gee; - public abstract class Tuba.AccountStore : GLib.Object { - public ArrayList saved { get; set; default = new ArrayList (); } + public Gee.ArrayList saved { get; set; default = new Gee.ArrayList (); } public InstanceAccount? active { get; set; default = null; } - public signal void changed (ArrayList accounts); + public signal void changed (Gee.ArrayList accounts); public signal void switched (InstanceAccount? account); public bool ensure_active_account () { diff --git a/src/Services/Accounts/InstanceAccount.vala b/src/Services/Accounts/InstanceAccount.vala index 2d8e860ca..2eaece3bc 100644 --- a/src/Services/Accounts/InstanceAccount.vala +++ b/src/Services/Accounts/InstanceAccount.vala @@ -1,6 +1,3 @@ -using GLib; -using Gee; - public class Tuba.InstanceAccount : API.Account, Streamable { public const string EVENT_NEW_POST = "update"; @@ -20,7 +17,7 @@ public class Tuba.InstanceAccount : API.Account, Streamable { public string? backend { set; get; } public API.Instance? instance_info { get; set; } - public ArrayList? instance_emojis { get; set; } + public Gee.ArrayList? instance_emojis { get; set; } public string? instance { get; set; } public string? client_id { get; set; } public string? client_secret { get; set; } @@ -29,7 +26,7 @@ public class Tuba.InstanceAccount : API.Account, Streamable { public GLib.ListStore known_places = new GLib.ListStore (typeof (Place)); - public HashMap type_overrides = new HashMap (); + public Gee.HashMap type_overrides = new Gee.HashMap (); public new string handle_short { owned get { return @"@$username"; } @@ -94,7 +91,7 @@ public class Tuba.InstanceAccount : API.Account, Streamable { } } } - public HashMap visibility = new HashMap (); + public Gee.HashMap visibility = new Gee.HashMap (); public ListStore visibility_list = new ListStore (typeof (Visibility)); public void set_visibility (Visibility obj) { this.visibility[obj.id] = obj; diff --git a/src/Services/Accounts/SecretAccountStore.vala b/src/Services/Accounts/SecretAccountStore.vala index c80bb157c..cc046a74e 100644 --- a/src/Services/Accounts/SecretAccountStore.vala +++ b/src/Services/Accounts/SecretAccountStore.vala @@ -1,18 +1,16 @@ -using Secret; - public class Tuba.SecretAccountStore : AccountStore { const string VERSION = "1"; Secret.Schema schema; - GLib.HashTable schema_attributes; + GLib.HashTable schema_attributes; public override void init () throws GLib.Error { message (@"Using libsecret v$(Secret.MAJOR_VERSION).$(Secret.MINOR_VERSION).$(Secret.MICRO_VERSION)"); - schema_attributes = new GLib.HashTable (str_hash, str_equal); - schema_attributes["login"] = SchemaAttributeType.STRING; - schema_attributes["version"] = SchemaAttributeType.STRING; + schema_attributes = new GLib.HashTable (str_hash, str_equal); + schema_attributes["login"] = Secret.SchemaAttributeType.STRING; + schema_attributes["version"] = Secret.SchemaAttributeType.STRING; schema = new Secret.Schema.newv ( Build.DOMAIN, Secret.SchemaFlags.DONT_MATCH_NAME, diff --git a/src/Services/Cache/AbstractCache.vala b/src/Services/Cache/AbstractCache.vala index 1efedbf13..c79ddf688 100644 --- a/src/Services/Cache/AbstractCache.vala +++ b/src/Services/Cache/AbstractCache.vala @@ -1,11 +1,9 @@ -using Gee; - public class Tuba.AbstractCache : Object { public const string DATA_MIN_REF_COUNT = "refs"; - protected Map items; - protected Map items_in_progress; + protected Gee.Map items; + protected Gee.Map items_in_progress; private uint timeout_source; private int _maintenance_secs = 5; @@ -25,8 +23,8 @@ public class Tuba.AbstractCache : Object { } construct { - items = new HashMap (); - items_in_progress = new HashMap (); + items = new Gee.HashMap (); + items_in_progress = new Gee.HashMap (); setup_maintenance (); } diff --git a/src/Services/Cache/ImageCache.vala b/src/Services/Cache/ImageCache.vala index 01f5996be..9042990bb 100644 --- a/src/Services/Cache/ImageCache.vala +++ b/src/Services/Cache/ImageCache.vala @@ -1,17 +1,15 @@ -using Gdk; - public class Tuba.ImageCache : AbstractCache { - public delegate void OnItemChangedFn (bool is_loaded, owned Paintable? data); + public delegate void OnItemChangedFn (bool is_loaded, owned Gdk.Paintable? data); - protected async Paintable decode (owned Soup.Message msg, owned InputStream in_stream) throws Error { + protected async Gdk.Paintable decode (owned Soup.Message msg, owned InputStream in_stream) throws Error { var code = msg.status_code; if (code != Soup.Status.OK) { var error = msg.reason_phrase; throw new Oopsie.INSTANCE (@"Server returned $error"); } - var pixbuf = yield new Pixbuf.from_stream_async (in_stream); + var pixbuf = yield new Gdk.Pixbuf.from_stream_async (in_stream); return Gdk.Texture.for_pixbuf (pixbuf); } @@ -22,7 +20,7 @@ public class Tuba.ImageCache : AbstractCache { var key = get_key (url); if (contains (key)) { - cb (true, lookup (key) as Paintable); + cb (true, lookup (key) as Gdk.Paintable); return; } @@ -34,7 +32,7 @@ public class Tuba.ImageCache : AbstractCache { download_msg = new Soup.Message ("GET", url); network.queue (download_msg, null, (sess, mess, t_in_stream) => { decode.begin (download_msg, t_in_stream, (obj, async_res) => { - Paintable? paintable = null; + Gdk.Paintable? paintable = null; try { paintable = decode.end (async_res); } @@ -65,7 +63,7 @@ public class Tuba.ImageCache : AbstractCache { //message ("[/]: %s", key); ulong id = 0; id = download_msg.finished.connect (() => { - cb (true, lookup (key) as Paintable); + cb (true, lookup (key) as Gdk.Paintable); download_msg.disconnect (id); }); diff --git a/src/Services/Network/Network.vala b/src/Services/Network/Network.vala index 18e3d1489..16d1e7aab 100644 --- a/src/Services/Network/Network.vala +++ b/src/Services/Network/Network.vala @@ -1,16 +1,11 @@ -using Soup; -using GLib; -using Gdk; -using Json; - public class Tuba.Network : GLib.Object { public signal void started (); public signal void finished (); public delegate void ErrorCallback (int32 code, string reason); - public delegate void SuccessCallback (Session session, Message msg, InputStream in_stream) throws Error; - public delegate void NodeCallback (Json.Node node, Message msg) throws Error; + public delegate void SuccessCallback (Soup.Session session, Soup.Message msg, InputStream in_stream) throws Error; + public delegate void NodeCallback (Json.Node node, Soup.Message msg) throws Error; public delegate void ObjectCallback (Json.Object node) throws Error; public Soup.Session session { get; set; } diff --git a/src/Services/Network/Request.vala b/src/Services/Network/Request.vala index 5bac1b78e..70cf80c43 100644 --- a/src/Services/Network/Request.vala +++ b/src/Services/Network/Request.vala @@ -1,6 +1,3 @@ -using Soup; -using Gee; - public class Tuba.Request : GLib.Object { private Soup.Message _msg; public Soup.Message msg { @@ -31,7 +28,7 @@ public class Tuba.Request : GLib.Object { public weak InstanceAccount? account { get; set; default = null; } Network.SuccessCallback? cb; Network.ErrorCallback? error_cb; - HashMap? pars; + Gee.HashMap? pars; Soup.Multipart? form_data; public GLib.Cancellable cancellable; @@ -118,21 +115,21 @@ public class Tuba.Request : GLib.Object { public Request with_param (string name, string val) { if (pars == null) - pars = new HashMap (); + pars = new Gee.HashMap (); pars[name] = val; return this; } public Request with_form_data (string name, string val) { if (form_data == null) - form_data = new Soup.Multipart (FORM_MIME_TYPE_MULTIPART); + form_data = new Soup.Multipart (Soup.FORM_MIME_TYPE_MULTIPART); form_data.append_form_string (name, val); return this; } public Request with_form_data_file (string name, string mime, Bytes buffer) { if (form_data == null) - form_data = new Soup.Multipart (FORM_MIME_TYPE_MULTIPART); + form_data = new Soup.Multipart (Soup.FORM_MIME_TYPE_MULTIPART); form_data.append_form_file (name, mime.replace ("/", "."), mime, buffer); return this; } diff --git a/src/Services/Network/Streams.vala b/src/Services/Network/Streams.vala index 3b4883c7c..2d0ac84f6 100644 --- a/src/Services/Network/Streams.vala +++ b/src/Services/Network/Streams.vala @@ -1,6 +1,3 @@ -using Soup; -using Gee; - public class Tuba.Streams : Object { protected HashTable connections { @@ -45,9 +42,9 @@ public class Tuba.Streams : Object { // } protected class Connection : Object { - public ArrayList subscribers; - protected WebsocketConnection socket; - protected Message msg; + public Gee.ArrayList subscribers; + protected Soup.WebsocketConnection socket; + protected Soup.Message msg; protected bool closing = false; protected int timeout = 1; @@ -60,8 +57,8 @@ public class Tuba.Streams : Object { } public Connection (string url) { - this.subscribers = new ArrayList (); - this.msg = new Message ("GET", url); + this.subscribers = new Gee.ArrayList (); + this.msg = new Soup.Message ("GET", url); } public bool start () { diff --git a/src/Services/Settings.vala b/src/Services/Settings.vala index 4ca56e4cc..951cb61b2 100644 --- a/src/Services/Settings.vala +++ b/src/Services/Settings.vala @@ -1,5 +1,3 @@ -using GLib; - public class Tuba.Settings : GLib.Settings { public string active_account { get; set; } diff --git a/src/Utils/DateTime.vala b/src/Utils/DateTime.vala index 21ecc35f9..f4ba82ea0 100644 --- a/src/Utils/DateTime.vala +++ b/src/Utils/DateTime.vala @@ -1,5 +1,3 @@ -using GLib; - public class Tuba.DateTime { public static string humanize_left (string iso8601) { diff --git a/src/Utils/Host.vala b/src/Utils/Host.vala index 313acc1fe..a828a977f 100644 --- a/src/Utils/Host.vala +++ b/src/Utils/Host.vala @@ -1,6 +1,3 @@ -using GLib; -using Gdk; - public class Tuba.Host { // Open a URI in the user's default application @@ -31,7 +28,7 @@ public class Tuba.Host { } public static void copy (string str) { - Display display = Display.get_default (); + Gdk.Display display = Gdk.Display.get_default (); if (display == null) return; display.get_clipboard ().set_text (str); diff --git a/src/Views/Base.vala b/src/Views/Base.vala index 4504d135c..e63b6b018 100644 --- a/src/Views/Base.vala +++ b/src/Views/Base.vala @@ -1,7 +1,5 @@ -using Gtk; - [GtkTemplate (ui = "/dev/geopjr/Tuba/ui/views/base.ui")] -public class Tuba.Views.Base : Box { +public class Tuba.Views.Base : Gtk.Box { // translators: Shown when there are 0 results public static string STATUS_EMPTY = _("Nothing to see here"); // vala-lint=naming-convention @@ -31,21 +29,21 @@ public class Tuba.Views.Base : Box { } [GtkChild] protected unowned Adw.HeaderBar header; - [GtkChild] protected unowned Button back_button; + [GtkChild] protected unowned Gtk.Button back_button; - [GtkChild] protected unowned ScrolledWindow scrolled; - [GtkChild] protected unowned Overlay scrolled_overlay; - [GtkChild] protected unowned Button scroll_to_top; - [GtkChild] protected unowned Box view; + [GtkChild] protected unowned Gtk.ScrolledWindow scrolled; + [GtkChild] protected unowned Gtk.Overlay scrolled_overlay; + [GtkChild] protected unowned Gtk.Button scroll_to_top; + [GtkChild] protected unowned Gtk.Box view; [GtkChild] protected unowned Adw.Clamp clamp; - [GtkChild] protected unowned Box column_view; - [GtkChild] protected unowned Stack states; - [GtkChild] protected unowned Box content_box; - [GtkChild] protected unowned Button status_button; - [GtkChild] unowned Stack status_stack; - [GtkChild] unowned Label status_title_label; - [GtkChild] unowned Label status_message_label; - [GtkChild] unowned Spinner status_spinner; + [GtkChild] protected unowned Gtk.Box column_view; + [GtkChild] protected unowned Gtk.Stack states; + [GtkChild] protected unowned Gtk.Box content_box; + [GtkChild] protected unowned Gtk.Button status_button; + [GtkChild] unowned Gtk.Stack status_stack; + [GtkChild] unowned Gtk.Label status_title_label; + [GtkChild] unowned Gtk.Label status_message_label; + [GtkChild] unowned Gtk.Spinner status_spinner; public class StatusMessage : Object { public string title = STATUS_EMPTY; diff --git a/src/Views/ContentBase.vala b/src/Views/ContentBase.vala index 0d1abbda7..7cd511ffe 100644 --- a/src/Views/ContentBase.vala +++ b/src/Views/ContentBase.vala @@ -1,9 +1,7 @@ -using Gtk; - public class Tuba.Views.ContentBase : Views.Base { public GLib.ListStore model; - protected ListBox content; + protected Gtk.ListBox content; private bool bottom_reached_locked = false; protected signal void reached_close_to_top (); @@ -15,8 +13,8 @@ public class Tuba.Views.ContentBase : Views.Base { model = new GLib.ListStore (typeof (Widgetizable)); model.items_changed.connect (on_content_changed); - content = new ListBox () { - selection_mode = SelectionMode.NONE, + content = new Gtk.ListBox () { + selection_mode = Gtk.SelectionMode.NONE, css_classes = { "content", "ttl-content" } }; content_box.append (content); @@ -65,7 +63,7 @@ public class Tuba.Views.ContentBase : Views.Base { } - public virtual Widget on_create_model_widget (Object obj) { + public virtual Gtk.Widget on_create_model_widget (Object obj) { var obj_widgetable = obj as Widgetizable; if (obj_widgetable == null) Process.exit (0); @@ -87,7 +85,7 @@ public class Tuba.Views.ContentBase : Views.Base { }, Priority.LOW); } - public virtual void on_content_item_activated (ListBoxRow row) { + public virtual void on_content_item_activated (Gtk.ListBoxRow row) { Signal.emit_by_name (row, "open"); } diff --git a/src/Views/Lists.vala b/src/Views/Lists.vala index ae3508166..b9f95240a 100644 --- a/src/Views/Lists.vala +++ b/src/Views/Lists.vala @@ -1,26 +1,24 @@ -using Gtk; - public class Tuba.Views.Lists : Views.Timeline { public class Row : Adw.ActionRow { public API.List? list; - Button delete_button; - Button edit_button; + Gtk.Button delete_button; + Gtk.Button edit_button; construct { - var action_box = new Box (Orientation.HORIZONTAL, 6); + var action_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6); - edit_button = new Button () { + edit_button = new Gtk.Button () { icon_name = "document-edit-symbolic", - valign = Align.CENTER, - halign = Align.CENTER, + valign = Gtk.Align.CENTER, + halign = Gtk.Align.CENTER, css_classes = { "flat", "circular" } }; - delete_button = new Button () { + delete_button = new Gtk.Button () { icon_name = "tuba-trash-symbolic", - valign = Align.CENTER, - halign = Align.CENTER, + valign = Gtk.Align.CENTER, + halign = Gtk.Align.CENTER, css_classes = { "flat", "circular", "error" } }; @@ -96,7 +94,7 @@ public class Tuba.Views.Lists : Views.Timeline { get { return false; } } - public override Widget on_create_model_widget (Object obj) { + public override Gtk.Widget on_create_model_widget (Object obj) { var widget = base.on_create_model_widget (obj); var widget_row = widget as Row; @@ -137,25 +135,25 @@ public class Tuba.Views.Lists : Views.Timeline { .exec (); } - public void on_action_bar_activate (EntryBuffer buffer) { + public void on_action_bar_activate (Gtk.EntryBuffer buffer) { if (buffer.length > 0) create_list (buffer.text); buffer.set_text ("".data); } - Entry child_entry = new Entry () { - input_purpose = InputPurpose.FREE_FORM, + Gtk.Entry child_entry = new Gtk.Entry () { + input_purpose = Gtk.InputPurpose.FREE_FORM, placeholder_text = _("New list title") }; construct { - var add_action_bar = new ActionBar () { + var add_action_bar = new Gtk.ActionBar () { css_classes = { "ttl-box-no-shadow" } }; - var child_box = new Box (Orientation.HORIZONTAL, 6); + var child_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6); - var add_button = new Button.with_label (_("Add list")) { + var add_button = new Gtk.Button.with_label (_("Add list")) { sensitive = false }; diff --git a/src/Views/Main.vala b/src/Views/Main.vala index b36a086ff..f74b20a2c 100644 --- a/src/Views/Main.vala +++ b/src/Views/Main.vala @@ -1,5 +1,3 @@ -using Gtk; - public class Tuba.Views.Main : Views.TabbedBase { public Main () { @@ -14,13 +12,13 @@ public class Tuba.Views.Main : Views.TabbedBase { base.build_header (); back_button.hide (); - var search_button = new Button (); + var search_button = new Gtk.Button (); search_button.icon_name = "tuba-loupe-large-symbolic"; search_button.tooltip_text = _("Search"); search_button.clicked.connect (open_search); header.pack_end (search_button); - var sidebar_button = new ToggleButton (); + var sidebar_button = new Gtk.ToggleButton (); header.pack_start (sidebar_button); sidebar_button.icon_name = "tuba-dock-left-symbolic"; diff --git a/src/Views/Notifications.vala b/src/Views/Notifications.vala index a6cfd9a2e..b26018c49 100644 --- a/src/Views/Notifications.vala +++ b/src/Views/Notifications.vala @@ -1,6 +1,3 @@ -using Gtk; -using Gdk; - public class Tuba.Views.Notifications : Views.Timeline, AccountHolder, Streamable { protected InstanceAccount? last_account = null; diff --git a/src/Views/Profile.vala b/src/Views/Profile.vala index bdcef7225..4b49c4306 100644 --- a/src/Views/Profile.vala +++ b/src/Views/Profile.vala @@ -1,5 +1,3 @@ -using Gtk; - public class Tuba.Views.Profile : Views.Timeline { public API.Account profile { get; construct set; } @@ -9,7 +7,7 @@ public class Tuba.Views.Profile : Views.Timeline { public string source { get; set; default = "statuses"; } protected Cover cover; - protected MenuButton menu_button; + protected Gtk.MenuButton menu_button; protected SimpleAction media_action; protected SimpleAction replies_action; @@ -68,7 +66,7 @@ public class Tuba.Views.Profile : Views.Timeline { return GLib.Source.REMOVE; } - public override Widget on_create_model_widget (Object obj) { + public override Gtk.Widget on_create_model_widget (Object obj) { var widget = base.on_create_model_widget (obj); var widget_status = widget as Widgets.Status; @@ -86,15 +84,15 @@ public class Tuba.Views.Profile : Views.Timeline { } [GtkTemplate (ui = "/dev/geopjr/Tuba/ui/views/profile_header.ui")] - protected class Cover : Box { + protected class Cover : Gtk.Box { [GtkChild] unowned Widgets.Background background; - [GtkChild] unowned Label cover_badge; - [GtkChild] unowned Image cover_bot_badge; - [GtkChild] unowned Box cover_badge_box; - [GtkChild] public unowned ListBox info; + [GtkChild] unowned Gtk.Label cover_badge; + [GtkChild] unowned Gtk.Image cover_bot_badge; + [GtkChild] unowned Gtk.Box cover_badge_box; + [GtkChild] public unowned Gtk.ListBox info; [GtkChild] unowned Widgets.EmojiLabel display_name; - [GtkChild] unowned Label handle; + [GtkChild] unowned Gtk.Label handle; [GtkChild] unowned Widgets.Avatar avatar; [GtkChild] unowned Widgets.MarkupView note; [GtkChild] public unowned Widgets.RelationshipButton rsbtn; @@ -239,9 +237,9 @@ public class Tuba.Views.Profile : Views.Timeline { } } - protected void build_profile_stats (ListBox info) { + protected void build_profile_stats (Gtk.ListBox info) { var row = new Gtk.ListBoxRow (); - var box = new Box (Orientation.HORIZONTAL, 0) { + var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0) { homogeneous = true }; @@ -265,14 +263,14 @@ public class Tuba.Views.Profile : Views.Timeline { info.append (row); } - protected Button build_profile_stats_button (string btn_label) { - var btn = new Button.with_label (btn_label) { + protected Gtk.Button build_profile_stats_button (string btn_label) { + var btn = new Gtk.Button.with_label (btn_label) { css_classes = { "flat", "ttl-profile-stat-button" } }; - var child_label = btn.child as Label; + var child_label = btn.child as Gtk.Label; child_label.wrap = true; - child_label.justify = Justification.CENTER; + child_label.justify = Gtk.Justification.CENTER; return btn; } @@ -288,8 +286,8 @@ public class Tuba.Views.Profile : Views.Timeline { protected override void build_header () { base.build_header (); - menu_button = new MenuButton (); - var menu_builder = new Builder.from_resource (@"$(Build.RESOURCES)ui/menus.ui"); + menu_button = new Gtk.MenuButton (); + var menu_builder = new Gtk.Builder.from_resource (@"$(Build.RESOURCES)ui/menus.ui"); var menu = "profile-menu"; menu_button.menu_model = menu_builder.get_object (menu) as MenuModel; menu_button.popover.width_request = 250; @@ -522,25 +520,25 @@ public class Tuba.Views.Profile : Views.Timeline { network.on_error); } - public class RowButton : Button { + public class RowButton : Gtk.Button { public bool remove { get; set; default = false; } } public Adw.Window create_ar_list_dialog () { - var spinner = new Spinner () { + var spinner = new Gtk.Spinner () { spinning = true, - halign = Align.CENTER, - valign = Align.CENTER, + halign = Gtk.Align.CENTER, + valign = Gtk.Align.CENTER, vexpand = true, hexpand = true, width_request = 32, height_request = 32 }; - var box = new Box (Orientation.VERTICAL, 6); + var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 6); var headerbar = new Adw.HeaderBar (); var toast_overlay = new Adw.ToastOverlay () { vexpand = true, - valign = Align.CENTER + valign = Gtk.Align.CENTER }; toast_overlay.child = spinner; @@ -598,8 +596,8 @@ public class Tuba.Views.Profile : Views.Timeline { tooltip_text = is_already ? _("Remove \"%s\" from \"%s\"").printf (profile.handle, list.title) : _("Add \"%s\" to \"%s\"").printf (profile.handle, list.title), - halign = Align.CENTER, - valign = Align.CENTER, + halign = Gtk.Align.CENTER, + valign = Gtk.Align.CENTER, css_classes = { "flat", "circular" } }; add_button.remove = is_already; @@ -621,7 +619,7 @@ public class Tuba.Views.Profile : Views.Timeline { preferences_page.add (preferences_group); toast_overlay.child = preferences_page; - toast_overlay.valign = Align.FILL; + toast_overlay.valign = Gtk.Align.FILL; } else { toast_overlay.child = no_lists_page; } diff --git a/src/Views/Search.vala b/src/Views/Search.vala index 78ca07f15..dc684ec46 100644 --- a/src/Views/Search.vala +++ b/src/Views/Search.vala @@ -1,11 +1,9 @@ -using Gtk; - public class Tuba.Views.Search : Views.TabbedBase { public string query { get; set; default = ""; } - protected SearchBar bar; + protected Gtk.SearchBar bar; protected Adw.Clamp bar_clamp; - protected SearchEntry entry; + protected Gtk.SearchEntry entry; Views.ContentBase all_tab; Views.ContentBase accounts_tab; @@ -15,13 +13,13 @@ public class Tuba.Views.Search : Views.TabbedBase { public Search () { Object (label: _("Search")); - bar = new SearchBar () { + bar = new Gtk.SearchBar () { search_mode_enabled = true }; prepend (bar); reorder_child_after (bar, header); - entry = new SearchEntry () { + entry = new Gtk.SearchEntry () { width_chars = 25, text = query }; diff --git a/src/Views/Sidebar.vala b/src/Views/Sidebar.vala index 0e6be80c9..4efd85290 100644 --- a/src/Views/Sidebar.vala +++ b/src/Views/Sidebar.vala @@ -1,16 +1,14 @@ -using Gtk; - [GtkTemplate (ui = "/dev/geopjr/Tuba/ui/views/sidebar/view.ui")] -public class Tuba.Views.Sidebar : Box, AccountHolder { +public class Tuba.Views.Sidebar : Gtk.Box, AccountHolder { - [GtkChild] unowned ToggleButton accounts_button; - [GtkChild] unowned Stack mode; - [GtkChild] unowned ListBox items; - [GtkChild] unowned ListBox saved_accounts; + [GtkChild] unowned Gtk.ToggleButton accounts_button; + [GtkChild] unowned Gtk.Stack mode; + [GtkChild] unowned Gtk.ListBox items; + [GtkChild] unowned Gtk.ListBox saved_accounts; [GtkChild] unowned Widgets.Avatar avatar; [GtkChild] unowned Widgets.EmojiLabel title; - [GtkChild] unowned Label subtitle; + [GtkChild] unowned Gtk.Label subtitle; [GtkChild] unowned Adw.HeaderBar sb_header; private bool _show_window_controls = false; @@ -27,8 +25,8 @@ public class Tuba.Views.Sidebar : Box, AccountHolder { protected InstanceAccount? account { get; set; default = null; } protected GLib.ListStore app_items; - protected SliceListModel account_items; - protected FlattenListModel item_model; + protected Gtk.SliceListModel account_items; + protected Gtk.FlattenListModel item_model; public static Place KEYBOARD_SHORTCUTS = new Place () { // vala-lint=naming-convention @@ -65,12 +63,12 @@ public class Tuba.Views.Sidebar : Box, AccountHolder { app_items.append (KEYBOARD_SHORTCUTS); app_items.append (ABOUT); - account_items = new SliceListModel (null, 0, 15); + account_items = new Gtk.SliceListModel (null, 0, 15); var models = new GLib.ListStore (typeof (Object)); models.append (account_items); models.append (app_items); - item_model = new FlattenListModel (models); + item_model = new Gtk.FlattenListModel (models); items.bind_model (item_model, on_item_create); items.set_header_func (on_item_header_update); @@ -160,11 +158,11 @@ public class Tuba.Views.Sidebar : Box, AccountHolder { // Item [GtkTemplate (ui = "/dev/geopjr/Tuba/ui/views/sidebar/item.ui")] - protected class ItemRow : ListBoxRow { + protected class ItemRow : Gtk.ListBoxRow { public Place place; - [GtkChild] unowned Image icon; - [GtkChild] unowned Label label; + [GtkChild] unowned Gtk.Image icon; + [GtkChild] unowned Gtk.Label label; // [GtkChild] unowned Label badge; public ItemRow (Place place) { @@ -181,11 +179,11 @@ public class Tuba.Views.Sidebar : Box, AccountHolder { } } - Widget on_item_create (Object obj) { + Gtk.Widget on_item_create (Object obj) { return new ItemRow (obj as Place); } - [GtkCallback] void on_item_activated (ListBoxRow _row) { + [GtkCallback] void on_item_activated (Gtk.ListBoxRow _row) { var row = _row as ItemRow; if (row.place.open_func != null) row.place.open_func (app.main_window); @@ -195,14 +193,14 @@ public class Tuba.Views.Sidebar : Box, AccountHolder { flap.reveal_flap = false; } - void on_item_header_update (ListBoxRow _row, ListBoxRow? _before) { + void on_item_header_update (Gtk.ListBoxRow _row, Gtk.ListBoxRow? _before) { var row = _row as ItemRow; var before = _before as ItemRow; row.set_header (null); if (row.place.separated && before != null && !before.place.separated) { - row.set_header (new Separator (Orientation.HORIZONTAL)); + row.set_header (new Gtk.Separator (Gtk.Orientation.HORIZONTAL)); } } @@ -215,7 +213,7 @@ public class Tuba.Views.Sidebar : Box, AccountHolder { public InstanceAccount? account; [GtkChild] unowned Widgets.Avatar avatar; - [GtkChild] unowned Button forget; + [GtkChild] unowned Gtk.Button forget; private Binding switcher_display_name; private Binding switcher_handle; @@ -280,16 +278,16 @@ public class Tuba.Views.Sidebar : Box, AccountHolder { } - void on_account_header_update (ListBoxRow _row, ListBoxRow? _before) { + void on_account_header_update (Gtk.ListBoxRow _row, Gtk.ListBoxRow? _before) { var row = _row as AccountRow; row.set_header (null); if (row.account == null && _before != null) - row.set_header (new Separator (Orientation.HORIZONTAL)); + row.set_header (new Gtk.Separator (Gtk.Orientation.HORIZONTAL)); } - [GtkCallback] void on_account_activated (ListBoxRow _row) { + [GtkCallback] void on_account_activated (Gtk.ListBoxRow _row) { var row = _row as AccountRow; if (row.account != null) accounts.activate (row.account); diff --git a/src/Views/TabbedBase.vala b/src/Views/TabbedBase.vala index daf8c2628..891219b52 100644 --- a/src/Views/TabbedBase.vala +++ b/src/Views/TabbedBase.vala @@ -1,5 +1,3 @@ -using Gtk; - public class Tuba.Views.TabbedBase : Views.Base { static int id_counter = 0; @@ -14,12 +12,12 @@ public class Tuba.Views.TabbedBase : Views.Base { construct { base_status = null; - var states_box = states.get_parent () as Box; + var states_box = states.get_parent () as Gtk.Box; if (states_box != null) states_box.remove (states); view.remove_css_class ("ttl-view"); - var scrolled_overlay_box = scrolled_overlay.get_parent () as Box; + var scrolled_overlay_box = scrolled_overlay.get_parent () as Gtk.Box; if (scrolled_overlay_box != null) scrolled_overlay_box.remove (scrolled_overlay); insert_child_after (states, header); diff --git a/src/Views/Thread.vala b/src/Views/Thread.vala index 69694d343..fc4857a3f 100644 --- a/src/Views/Thread.vala +++ b/src/Views/Thread.vala @@ -1,5 +1,3 @@ -using Gtk; - public class Tuba.Views.Thread : Views.ContentBase, AccountHolder { protected InstanceAccount? account { get; set; } diff --git a/src/Views/Timeline.vala b/src/Views/Timeline.vala index 737eaae72..ea31f422a 100644 --- a/src/Views/Timeline.vala +++ b/src/Views/Timeline.vala @@ -1,6 +1,3 @@ -using Gtk; -using Gdk; - public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase { public string url { get; construct set; } diff --git a/src/Widgets/Attachment/Box.vala b/src/Widgets/Attachment/Box.vala index 59ceaf074..83a65109a 100644 --- a/src/Widgets/Attachment/Box.vala +++ b/src/Widgets/Attachment/Box.vala @@ -1,11 +1,7 @@ -using Gtk; -using GLib; -using Gee; - public class Tuba.Widgets.Attachment.Box : Adw.Bin { - ArrayList? _list = null; - public ArrayList? list { + Gee.ArrayList? _list = null; + public Gee.ArrayList? list { get { return _list; } @@ -54,12 +50,12 @@ public class Tuba.Widgets.Attachment.Box : Adw.Bin { visible = false; hexpand = true; - box = new FlowBox () { + box = new Gtk.FlowBox () { homogeneous = true, activate_on_single_click = true, column_spacing = 6, row_spacing = 6, - selection_mode = SelectionMode.NONE + selection_mode = Gtk.SelectionMode.NONE }; spoiler_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0) { diff --git a/src/Widgets/Attachment/Image.vala b/src/Widgets/Attachment/Image.vala index 7e5652301..5368d0e15 100644 --- a/src/Widgets/Attachment/Image.vala +++ b/src/Widgets/Attachment/Image.vala @@ -1,12 +1,9 @@ -using Gtk; -using Gdk; - public class Tuba.Widgets.Attachment.Image : Widgets.Attachment.Item { const string[] ALLOWED_TYPES = {"IMAGE", "VIDEO", "GIFV", "AUDIO"}; const string[] VIDEO_TYPES = {"GIFV", "VIDEO", "AUDIO"}; protected Gtk.Picture pic; - protected Overlay media_overlay; + protected Gtk.Overlay media_overlay; private bool _spoiler = false; public bool spoiler { @@ -31,7 +28,7 @@ public class Tuba.Widgets.Attachment.Image : Widgets.Attachment.Item { } construct { - pic = new Picture () { + pic = new Gtk.Picture () { hexpand = true, vexpand = true, can_shrink = true, @@ -43,7 +40,7 @@ public class Tuba.Widgets.Attachment.Image : Widgets.Attachment.Item { update_pic_content_fit (); settings.notify["letterbox-media"].connect (update_pic_content_fit); - media_overlay = new Overlay (); + media_overlay = new Gtk.Overlay (); media_overlay.child = pic; button.child = media_overlay; @@ -75,7 +72,7 @@ public class Tuba.Widgets.Attachment.Image : Widgets.Attachment.Item { } } - protected virtual void on_cache_response (bool is_loaded, owned Paintable? data) { + protected virtual void on_cache_response (bool is_loaded, owned Gdk.Paintable? data) { pic.paintable = data; } diff --git a/src/Widgets/Attachment/Item.vala b/src/Widgets/Attachment/Item.vala index 7dae4f61b..16b8e1904 100644 --- a/src/Widgets/Attachment/Item.vala +++ b/src/Widgets/Attachment/Item.vala @@ -1,11 +1,9 @@ -using Gtk; - public class Tuba.Widgets.Attachment.Item : Adw.Bin { public API.Attachment entity { get; set; default = null; } - protected GestureClick gesture_click_controller { get; set; } - protected GestureLongPress gesture_lp_controller { get; set; } - protected PopoverMenu context_menu { get; set; } + protected Gtk.GestureClick gesture_click_controller { get; set; } + protected Gtk.GestureLongPress gesture_lp_controller { get; set; } + protected Gtk.PopoverMenu context_menu { get; set; } private const GLib.ActionEntry[] ACTION_ENTRIES = { {"copy-url", copy_url}, {"open-in-browser", open_in_browser}, @@ -13,9 +11,9 @@ public class Tuba.Widgets.Attachment.Item : Adw.Bin { }; private GLib.SimpleActionGroup actions; - protected Overlay overlay; - protected Button button; - protected Button alt_btn; + protected Gtk.Overlay overlay; + protected Gtk.Button button; + protected Gtk.Button alt_btn; protected Gtk.Box badge_box; protected ulong alt_btn_clicked_id; protected string media_kind; @@ -33,7 +31,7 @@ public class Tuba.Widgets.Attachment.Item : Adw.Bin { } public static void save_media_as (string url) { - var chooser = new FileDialog () { + var chooser = new Gtk.FileDialog () { title = _("Save Attachment"), modal = true, initial_name = Path.get_basename (url) @@ -85,15 +83,15 @@ public class Tuba.Widgets.Attachment.Item : Adw.Bin { notify["entity"].connect (on_rebind); add_css_class ("flat"); - button = new Button () { + button = new Gtk.Button () { css_classes = { "frame" }, - overflow = Overflow.HIDDEN + overflow = Gtk.Overflow.HIDDEN }; button.clicked.connect (on_click); create_context_menu (); - gesture_click_controller = new GestureClick (); - gesture_lp_controller = new GestureLongPress (); + gesture_click_controller = new Gtk.GestureClick (); + gesture_lp_controller = new Gtk.GestureLongPress (); add_controller (gesture_click_controller); add_controller (gesture_lp_controller); gesture_click_controller.button = Gdk.BUTTON_SECONDARY; @@ -102,13 +100,13 @@ public class Tuba.Widgets.Attachment.Item : Adw.Bin { gesture_click_controller.pressed.connect (on_secondary_click); gesture_lp_controller.pressed.connect (on_secondary_click); - badge_box = new Gtk.Box (Orientation.HORIZONTAL, 1) { - valign = Align.END, - halign = Align.START, + badge_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 1) { + valign = Gtk.Align.END, + halign = Gtk.Align.START, css_classes = { "linked", "ttl-status-badge" } }; - alt_btn = new Button.with_label ("ALT") { + alt_btn = new Gtk.Button.with_label ("ALT") { tooltip_text = _("View Alt Text"), css_classes = { "heading", "flat" } }; @@ -120,7 +118,7 @@ public class Tuba.Widgets.Attachment.Item : Adw.Bin { badge_box.append (alt_btn); - overlay = new Overlay () { + overlay = new Gtk.Overlay () { css_classes = { "attachment" } }; overlay.child = button; @@ -134,23 +132,23 @@ public class Tuba.Widgets.Attachment.Item : Adw.Bin { } protected Adw.Window create_alt_text_window (string alt_text, bool show = false) { - var alt_label = new Label (alt_text) { + var alt_label = new Gtk.Label (alt_text) { wrap = true }; var clamp = new Adw.Clamp () { child = alt_label, tightening_threshold = 100, - valign = Align.START + valign = Gtk.Align.START }; - var scrolledwindow = new ScrolledWindow () { + var scrolledwindow = new Gtk.ScrolledWindow () { child = clamp, vexpand = true, hexpand = true }; - var box = new Gtk.Box (Orientation.VERTICAL, 6); + var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 6); var headerbar = new Adw.HeaderBar (); var window = new Adw.Window () { modal = true, @@ -176,7 +174,7 @@ public class Tuba.Widgets.Attachment.Item : Adw.Bin { menu_model.append (_("Copy URL"), "attachment.copy-url"); menu_model.append (_("Save Media"), "attachment.save-as"); - context_menu = new PopoverMenu.from_model (menu_model); + context_menu = new Gtk.PopoverMenu.from_model (menu_model); context_menu.set_parent (this); } @@ -198,8 +196,8 @@ public class Tuba.Widgets.Attachment.Item : Adw.Bin { } protected virtual void on_secondary_click () { - gesture_click_controller.set_state (EventSequenceState.CLAIMED); - gesture_lp_controller.set_state (EventSequenceState.CLAIMED); + gesture_click_controller.set_state (Gtk.EventSequenceState.CLAIMED); + gesture_lp_controller.set_state (Gtk.EventSequenceState.CLAIMED); if (app.main_window.is_media_viewer_visible ()) return; context_menu.popup (); diff --git a/src/Widgets/Avatar.vala b/src/Widgets/Avatar.vala index c368f1fc7..d1224c16b 100644 --- a/src/Widgets/Avatar.vala +++ b/src/Widgets/Avatar.vala @@ -1,8 +1,4 @@ -using Gtk; -using Gdk; - -public class Tuba.Widgets.Avatar : Button { - +public class Tuba.Widgets.Avatar : Gtk.Button { public API.Account? account { set { on_invalidated (value); @@ -14,7 +10,7 @@ public class Tuba.Widgets.Avatar : Button { set { avatar.size = value; } } - public Paintable? custom_image { + public Gdk.Paintable? custom_image { get { return avatar.custom_image; } } @@ -39,7 +35,7 @@ public class Tuba.Widgets.Avatar : Button { construct { child = new Adw.Avatar (48, null, true); - halign = valign = Align.CENTER; + halign = valign = Gtk.Align.CENTER; css_classes = { "flat", "circular", "image-button", "ttl-flat-button" }; on_invalidated (); @@ -56,8 +52,7 @@ public class Tuba.Widgets.Avatar : Button { } } - void on_cache_response (bool is_loaded, owned Paintable? data) { + void on_cache_response (bool is_loaded, owned Gdk.Paintable? data) { avatar.custom_image = data; } - } diff --git a/src/Widgets/Conversation.vala b/src/Widgets/Conversation.vala index 6086c80bd..8066f7adb 100644 --- a/src/Widgets/Conversation.vala +++ b/src/Widgets/Conversation.vala @@ -1,5 +1,3 @@ -using Gtk; - public class Tuba.Widgets.Conversation : Widgets.Status { public API.Conversation conversation { get; construct set; } diff --git a/src/Widgets/Emoji.vala b/src/Widgets/Emoji.vala index 895838f1c..35c625fc5 100644 --- a/src/Widgets/Emoji.vala +++ b/src/Widgets/Emoji.vala @@ -1,9 +1,6 @@ -using Gtk; -using Gdk; - public class Tuba.Widgets.Emoji : Adw.Bin { - protected Image image; + protected Gtk.Image image; public string? shortcode { get; set; } public int pixel_size { get { return image.pixel_size; } @@ -33,7 +30,7 @@ public class Tuba.Widgets.Emoji : Adw.Bin { }); } - void on_cache_response (bool is_loaded, owned Paintable? data) { + void on_cache_response (bool is_loaded, owned Gdk.Paintable? data) { if (image != null) image.paintable = data; } diff --git a/src/Widgets/LockableToggleButton.vala b/src/Widgets/LockableToggleButton.vala index 787ebfda4..1addd729f 100644 --- a/src/Widgets/LockableToggleButton.vala +++ b/src/Widgets/LockableToggleButton.vala @@ -1,11 +1,9 @@ -using Gtk; - // This button prevents changes to its "active" property while it's locked. // // This widget is intended to be used with Statuses where their properties // can be used to drive network requests. -public abstract class Tuba.LockableToggleButton : ToggleButton { +public abstract class Tuba.LockableToggleButton : Gtk.ToggleButton { uint _locks = 0; public bool locked { diff --git a/src/Widgets/MarkupView.vala b/src/Widgets/MarkupView.vala index 8bf32b05b..25816af80 100644 --- a/src/Widgets/MarkupView.vala +++ b/src/Widgets/MarkupView.vala @@ -1,6 +1,4 @@ -using Gtk; - -public class Tuba.Widgets.MarkupView : Box { +public class Tuba.Widgets.MarkupView : Gtk.Box { public delegate void NodeFn (Xml.Node* node); public delegate void NodeHandlerFn (MarkupView view, Xml.Node* node); @@ -38,7 +36,7 @@ public class Tuba.Widgets.MarkupView : Box { } construct { - orientation = Orientation.VERTICAL; + orientation = Gtk.Orientation.VERTICAL; spacing = 12; } diff --git a/src/Widgets/Notification.vala b/src/Widgets/Notification.vala index 9388ddd60..03770428e 100644 --- a/src/Widgets/Notification.vala +++ b/src/Widgets/Notification.vala @@ -1,5 +1,3 @@ -using Gtk; - public class Tuba.Widgets.Notification : Widgets.Status { public API.Notification notification { get; construct set; } diff --git a/src/Widgets/RelationshipButton.vala b/src/Widgets/RelationshipButton.vala index c6e8ea054..6852d5118 100644 --- a/src/Widgets/RelationshipButton.vala +++ b/src/Widgets/RelationshipButton.vala @@ -1,6 +1,4 @@ -using Gtk; - -public class Tuba.Widgets.RelationshipButton : Button { +public class Tuba.Widgets.RelationshipButton : Gtk.Button { public API.Relationship? rs { get; set; } protected SourceFunc? fn = null; diff --git a/src/Widgets/RichLabel.vala b/src/Widgets/RichLabel.vala index 184fa6310..792ad90b1 100644 --- a/src/Widgets/RichLabel.vala +++ b/src/Widgets/RichLabel.vala @@ -1,12 +1,9 @@ -using Gtk; -using Gee; - public class Tuba.Widgets.RichLabel : Adw.Bin { Widgets.EmojiLabel widget; // TODO: We can parse tags and extract resolvable URIs now - public weak ArrayList? mentions; + public weak Gee.ArrayList? mentions; public string label { get { return widget.content; } diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index 695984fc2..1e1cbf87c 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -1,8 +1,5 @@ -using Gtk; -using Gdk; - [GtkTemplate (ui = "/dev/geopjr/Tuba/ui/widgets/status.ui")] -public class Tuba.Widgets.Status : ListBoxRow { +public class Tuba.Widgets.Status : Gtk.ListBoxRow { API.Status? _bound_status = null; public API.Status? status { @@ -62,56 +59,56 @@ public class Tuba.Widgets.Status : ListBoxRow { public Dialogs.Compose.SuccessCallback? reply_cb; - [GtkChild] protected unowned Box status_box; - [GtkChild] protected unowned Box avatar_side; - [GtkChild] protected unowned Box title_box; - [GtkChild] protected unowned Box content_side; - [GtkChild] protected unowned FlowBox name_flowbox; - [GtkChild] public unowned MenuButton menu_button; + [GtkChild] protected unowned Gtk.Box status_box; + [GtkChild] protected unowned Gtk.Box avatar_side; + [GtkChild] protected unowned Gtk.Box title_box; + [GtkChild] protected unowned Gtk.Box content_side; + [GtkChild] protected unowned Gtk.FlowBox name_flowbox; + [GtkChild] public unowned Gtk.MenuButton menu_button; - [GtkChild] protected unowned Image header_icon; + [GtkChild] protected unowned Gtk.Image header_icon; [GtkChild] protected unowned Widgets.RichLabel header_label; - [GtkChild] protected unowned Button header_button; - [GtkChild] public unowned Image thread_line_top; - [GtkChild] public unowned Image thread_line_bottom; + [GtkChild] protected unowned Gtk.Button header_button; + [GtkChild] public unowned Gtk.Image thread_line_top; + [GtkChild] public unowned Gtk.Image thread_line_bottom; [GtkChild] public unowned Widgets.Avatar avatar; - [GtkChild] public unowned Overlay avatar_overlay; - [GtkChild] protected unowned Button name_button; + [GtkChild] public unowned Gtk.Overlay avatar_overlay; + [GtkChild] protected unowned Gtk.Button name_button; [GtkChild] protected unowned Widgets.RichLabel name_label; - [GtkChild] protected unowned Label handle_label; - [GtkChild] protected unowned Box indicators; - [GtkChild] protected unowned Label date_label; - [GtkChild] protected unowned Image pin_indicator; - [GtkChild] protected unowned Image edited_indicator; - [GtkChild] protected unowned Image visibility_indicator; - - [GtkChild] protected unowned Box content_column; - [GtkChild] protected unowned Stack spoiler_stack; - [GtkChild] protected unowned Box content_box; + [GtkChild] protected unowned Gtk.Label handle_label; + [GtkChild] protected unowned Gtk.Box indicators; + [GtkChild] protected unowned Gtk.Label date_label; + [GtkChild] protected unowned Gtk.Image pin_indicator; + [GtkChild] protected unowned Gtk.Image edited_indicator; + [GtkChild] protected unowned Gtk.Image visibility_indicator; + + [GtkChild] protected unowned Gtk.Box content_column; + [GtkChild] protected unowned Gtk.Stack spoiler_stack; + [GtkChild] protected unowned Gtk.Box content_box; [GtkChild] public unowned Widgets.MarkupView content; [GtkChild] protected unowned Widgets.Attachment.Box attachments; - [GtkChild] protected unowned Button spoiler_button; - [GtkChild] protected unowned Label spoiler_label; - [GtkChild] protected unowned Label spoiler_label_rev; - [GtkChild] protected unowned Box spoiler_status_con; + [GtkChild] protected unowned Gtk.Button spoiler_button; + [GtkChild] protected unowned Gtk.Label spoiler_label; + [GtkChild] protected unowned Gtk.Label spoiler_label_rev; + [GtkChild] protected unowned Gtk.Box spoiler_status_con; - [GtkChild] public unowned FlowBox emoji_reactions; - [GtkChild] public unowned Box actions; - [GtkChild] public unowned Box fr_actions; + [GtkChild] public unowned Gtk.FlowBox emoji_reactions; + [GtkChild] public unowned Gtk.Box actions; + [GtkChild] public unowned Gtk.Box fr_actions; - [GtkChild] public unowned Button accept_fr_button; - [GtkChild] public unowned Button decline_fr_button; + [GtkChild] public unowned Gtk.Button accept_fr_button; + [GtkChild] public unowned Gtk.Button decline_fr_button; [GtkChild] public unowned Widgets.VoteBox poll; - protected Button reply_button; + protected Gtk.Button reply_button; protected Adw.ButtonContent reply_button_content; protected StatusActionButton reblog_button; protected StatusActionButton favorite_button; protected StatusActionButton bookmark_button; - protected PopoverMenu context_menu { get; set; } + protected Gtk.PopoverMenu context_menu { get; set; } private const GLib.ActionEntry[] ACTION_ENTRIES = { {"copy-url", copy_url}, {"open-in-browser", open_in_browser} @@ -129,7 +126,7 @@ public class Tuba.Widgets.Status : ListBoxRow { if (value == null) return; var i = 0; - FlowBoxChild? fb_child = null; + Gtk.FlowBoxChild? fb_child = null; while ((fb_child = emoji_reactions.get_child_at_index (i)) != null) { emoji_reactions.remove (fb_child); i = i + 1; @@ -138,19 +135,19 @@ public class Tuba.Widgets.Status : ListBoxRow { foreach (API.EmojiReaction p in value) { if (p.count <= 0) return; - var badge_button = new Button () { + var badge_button = new Gtk.Button () { // translators: the variable is the emoji or its name if it's custom tooltip_text = _("React with %s").printf (p.name) }; - var badge = new Box (Orientation.HORIZONTAL, 6); + var badge = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6); if (p.url != null) { badge.append (new Widgets.Emoji (p.url)); } else { - badge.append (new Label (p.name)); + badge.append (new Gtk.Label (p.name)); } - badge.append (new Label (p.count.to_string ())); + badge.append (new Gtk.Label (p.count.to_string ())); badge_button.child = badge; if (p.me == true) { @@ -290,7 +287,7 @@ public class Tuba.Widgets.Status : ListBoxRow { menu_model.append (_("Delete"), "status.delete-status"); } - context_menu = new PopoverMenu.from_model (menu_model); + context_menu = new Gtk.PopoverMenu.from_model (menu_model); } private void copy_url () { @@ -633,7 +630,7 @@ public class Tuba.Widgets.Status : ListBoxRow { } protected virtual void append_actions () { - reply_button = new Button (); + reply_button = new Gtk.Button (); reply_button_content = new Adw.ButtonContent () { css_classes = { "ttl-status-action-reply" }, tooltip_text = _("Reply") @@ -687,7 +684,7 @@ public class Tuba.Widgets.Status : ListBoxRow { for (var w = actions.get_first_child (); w != null; w = w.get_next_sibling ()) { w.add_css_class ("flat"); w.add_css_class ("circular"); - w.halign = Align.START; + w.halign = Gtk.Align.START; w.hexpand = true; } @@ -746,7 +743,7 @@ public class Tuba.Widgets.Status : ListBoxRow { var bottom_info = new Gtk.FlowBox () { max_children_per_line = 150, margin_top = 6, - selection_mode = SelectionMode.NONE + selection_mode = Gtk.SelectionMode.NONE }; // Insert it after the post content @@ -782,7 +779,7 @@ public class Tuba.Widgets.Status : ListBoxRow { } // Adds *separator* between all *flowbox* children - private void add_separators_to_expanded_bottom (FlowBox flowbox, string separator = "·") { + private void add_separators_to_expanded_bottom (Gtk.FlowBox flowbox, string separator = "·") { var i = 0; var child = flowbox.get_child_at_index (i); while (child != null) { diff --git a/src/Widgets/StatusActionButton.vala b/src/Widgets/StatusActionButton.vala index 20d1a7399..e2458ec2c 100644 --- a/src/Widgets/StatusActionButton.vala +++ b/src/Widgets/StatusActionButton.vala @@ -1,5 +1,3 @@ -using Gtk; - public class Tuba.StatusActionButton : LockableToggleButton { public Request req { get; set; default = null; } diff --git a/src/Widgets/VoteBox.vala b/src/Widgets/VoteBox.vala index 33cd697f5..2497c6a73 100644 --- a/src/Widgets/VoteBox.vala +++ b/src/Widgets/VoteBox.vala @@ -1,17 +1,13 @@ -using Gtk; -using Gdk; -using Gee; - [GtkTemplate (ui = "/dev/geopjr/Tuba/ui/widgets/votebox.ui")] -public class Tuba.Widgets.VoteBox: Box { - [GtkChild] protected unowned ListBox poll_box; - [GtkChild] protected unowned Button button_vote; - [GtkChild] protected unowned Label people_label; - [GtkChild] protected unowned Label expires_label; +public class Tuba.Widgets.VoteBox : Gtk.Box { + [GtkChild] protected unowned Gtk.ListBox poll_box; + [GtkChild] protected unowned Gtk.Button button_vote; + [GtkChild] protected unowned Gtk.Label people_label; + [GtkChild] protected unowned Gtk.Label expires_label; public API.Poll? poll { get; set;} public API.Status? status_parent { get; set; } - protected ArrayList selected_index = new ArrayList (); + protected Gee.ArrayList selected_index = new Gee.ArrayList (); construct { button_vote.set_label (_("Vote")); @@ -37,7 +33,7 @@ public class Tuba.Widgets.VoteBox: Box { Widgets.VoteCheckButton group_radio_option = null; //clear all existing entries - Widget entry = poll_box.get_first_child (); + Gtk.Widget entry = poll_box.get_first_child (); while (entry != null) { poll_box.remove (entry); entry = poll_box.get_first_child (); @@ -82,7 +78,7 @@ public class Tuba.Widgets.VoteBox: Box { foreach (int own_vote in poll.own_votes) { if (own_vote == row_number) { - row.add_suffix (new Image.from_icon_name ("tuba-check-round-outline-symbolic")); + row.add_suffix (new Gtk.Image.from_icon_name ("tuba-check-round-outline-symbolic")); } } @@ -115,7 +111,7 @@ public class Tuba.Widgets.VoteBox: Box { foreach (int own_vote in poll.own_votes) { if (own_vote == row_number) { check_option.set_active (true); - row.add_suffix (new Image.from_icon_name ("tuba-check-round-outline-symbolic")); + row.add_suffix (new Gtk.Image.from_icon_name ("tuba-check-round-outline-symbolic")); if (!selected_index.contains (p.title)) { selected_index.add (p.title); } diff --git a/src/Widgets/VoteCheckButton.vala b/src/Widgets/VoteCheckButton.vala index 120dd6ef6..cca13122f 100644 --- a/src/Widgets/VoteCheckButton.vala +++ b/src/Widgets/VoteCheckButton.vala @@ -1,11 +1,8 @@ -using Gtk; -using Gdk; - -public class Tuba.Widgets.VoteCheckButton : CheckButton { +public class Tuba.Widgets.VoteCheckButton : Gtk.CheckButton { public string poll_title { get; set;} construct { - valign = Align.CENTER; + valign = Gtk.Align.CENTER; css_classes = { "selection-mode" }; } } diff --git a/tests/Celebrate.test.vala b/tests/Celebrate.test.vala index caa5745b0..093ec0a0d 100644 --- a/tests/Celebrate.test.vala +++ b/tests/Celebrate.test.vala @@ -1,5 +1,3 @@ -using GLib; - struct TestCelebrate { public DateTime date; public string[] css_classes; diff --git a/tests/DateTime.test.vala b/tests/DateTime.test.vala index 5ec573cdc..4be993cf9 100644 --- a/tests/DateTime.test.vala +++ b/tests/DateTime.test.vala @@ -1,5 +1,3 @@ -using GLib; - struct TestDate { public string iso8601; public string left; diff --git a/tests/Html.test.vala b/tests/Html.test.vala index 6fe4e46b3..c6ca4a326 100644 --- a/tests/Html.test.vala +++ b/tests/Html.test.vala @@ -1,5 +1,3 @@ -using GLib; - struct TestContent { public string original; public string sanitized; diff --git a/tests/Tracking.test.vala b/tests/Tracking.test.vala index b11e3c8f2..64b66abe0 100644 --- a/tests/Tracking.test.vala +++ b/tests/Tracking.test.vala @@ -1,5 +1,3 @@ -using GLib; - struct TestUrl { public string original; public string result; diff --git a/tests/Units.test.vala b/tests/Units.test.vala index f8309b461..bb7b61703 100644 --- a/tests/Units.test.vala +++ b/tests/Units.test.vala @@ -1,5 +1,3 @@ -using GLib; - struct TestUnits { public int64 original; public string result; From 998a2f46d22f3bb7b04ac85bccb30fe9c69b41b5 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Fri, 11 Aug 2023 18:32:37 +0300 Subject: [PATCH 23/76] feat: move more timelines to construct --- src/Views/Announcements.vala | 10 ++++------ src/Views/Conversations.vala | 10 ++++------ src/Views/Federated.vala | 15 ++++++--------- src/Views/FollowRequests.vala | 19 +++++++++---------- src/Views/Hashtag.vala | 1 - src/Views/Lists.vala | 14 +++++--------- src/Views/Main.vala | 7 +++---- src/Views/Notifications.vala | 21 +++++++++------------ src/Views/Search.vala | 4 ++-- src/Views/StatusStats.vala | 6 ++++-- src/Widgets/Attachment/Image.vala | 1 - 11 files changed, 46 insertions(+), 62 deletions(-) diff --git a/src/Views/Announcements.vala b/src/Views/Announcements.vala index c1d513ebb..6faf49a1c 100644 --- a/src/Views/Announcements.vala +++ b/src/Views/Announcements.vala @@ -1,10 +1,8 @@ public class Tuba.Views.Announcements : Views.Timeline { - public Announcements () { - Object ( - url: "/api/v1/announcements?with_dismissed=true", - label: _("Announcements"), - icon: "tuba-lightbulb-symbolic" - ); + construct { + url = "/api/v1/announcements?with_dismissed=true"; + label = _("Announcements"); + icon = "tuba-lightbulb-symbolic"; accepts = typeof (API.Announcement); } } diff --git a/src/Views/Conversations.vala b/src/Views/Conversations.vala index f6e4a15e6..39eb77990 100644 --- a/src/Views/Conversations.vala +++ b/src/Views/Conversations.vala @@ -1,10 +1,8 @@ public class Tuba.Views.Conversations : Views.Timeline { - public Conversations () { - Object ( - url: "/api/v1/conversations", - label: _("Conversations"), - icon: "tuba-mail-symbolic" - ); + construct { + url = "/api/v1/conversations"; + label = _("Conversations"); + icon = "tuba-mail-symbolic"; accepts = typeof (API.Conversation); stream_event[InstanceAccount.EVENT_CONVERSATION].connect (on_new_post); } diff --git a/src/Views/Federated.vala b/src/Views/Federated.vala index ebdfccda8..bc10d9e17 100644 --- a/src/Views/Federated.vala +++ b/src/Views/Federated.vala @@ -1,13 +1,10 @@ public class Tuba.Views.Federated : Views.Timeline { - - public Federated () { - Object ( - url: "/api/v1/timelines/public", - is_public: true, - label: _("Federated"), - icon: "tuba-globe-symbolic" - ); - } + construct { + url = "/api/v1/timelines/public"; + label = _("Federated"); + icon = "tuba-globe-symbolic"; + is_public = true; + } public override string? get_stream_url () { return account != null diff --git a/src/Views/FollowRequests.vala b/src/Views/FollowRequests.vala index 8655006c6..c257b28e6 100644 --- a/src/Views/FollowRequests.vala +++ b/src/Views/FollowRequests.vala @@ -1,9 +1,8 @@ public class Tuba.Views.FollowRequests : Views.Timeline { - - public FollowRequests () { + construct { + url = "tuba-address-book-new-symbolic"; label = _("Follow Requests"); - icon = "tuba-address-book-new-symbolic"; - url = "/api/v1/follow_requests"; + icon = "/api/v1/follow_requests"; accepts = typeof (API.Account); } @@ -30,9 +29,9 @@ public class Tuba.Views.FollowRequests : Views.Timeline { var relationship = Entity.from_json (typeof (API.Relationship), node) as API.Relationship; if (relationship.followed_by == true) { uint indx; - var found = model.find (widget, out indx); - if (found) - model.remove (indx); + var found = model.find (widget, out indx); + if (found) + model.remove (indx); } else { widget_status.fr_actions.sensitive = true; } @@ -46,9 +45,9 @@ public class Tuba.Views.FollowRequests : Views.Timeline { .with_account (accounts.active) .then ((sess, msg) => { uint indx; - var found = model.find (widget, out indx); - if (found) - model.remove (indx); + var found = model.find (widget, out indx); + if (found) + model.remove (indx); }) .exec (); } diff --git a/src/Views/Hashtag.vala b/src/Views/Hashtag.vala index 8e4871b68..88f33d0e5 100644 --- a/src/Views/Hashtag.vala +++ b/src/Views/Hashtag.vala @@ -1,5 +1,4 @@ public class Tuba.Views.Hashtag : Views.Timeline { - bool t_following = false; string t_tag = ""; public Hashtag (string tag, bool? following = null, string? url_basename = null) { diff --git a/src/Views/Lists.vala b/src/Views/Lists.vala index b9f95240a..7f3dca81e 100644 --- a/src/Views/Lists.vala +++ b/src/Views/Lists.vala @@ -113,15 +113,6 @@ public class Tuba.Views.Lists : Views.Timeline { model.remove (indx); } - public Lists () { - Object ( - url: "/api/v1/lists", - label: _("Lists"), - icon: "tuba-list-compact-symbolic" - ); - accepts = typeof (API.List); - } - public void create_list (string list_name) { new Request.POST ("/api/v1/lists") .with_account (accounts.active) @@ -147,6 +138,11 @@ public class Tuba.Views.Lists : Views.Timeline { }; construct { + url = "/api/v1/lists"; + label = _("Lists"); + icon = "tuba-list-compact-symbolic"; + accepts = typeof (API.List); + var add_action_bar = new Gtk.ActionBar () { css_classes = { "ttl-box-no-shadow" } }; diff --git a/src/Views/Main.vala b/src/Views/Main.vala index f74b20a2c..07e9dd974 100644 --- a/src/Views/Main.vala +++ b/src/Views/Main.vala @@ -1,12 +1,11 @@ public class Tuba.Views.Main : Views.TabbedBase { - - public Main () { - Object (is_main: true); + construct { + is_main = true; add_tab (new Views.Home ()); add_tab (new Views.Notifications ()); add_tab (new Views.Conversations ()); - } + } public override void build_header () { base.build_header (); diff --git a/src/Views/Notifications.vala b/src/Views/Notifications.vala index b26018c49..0cabe95e9 100644 --- a/src/Views/Notifications.vala +++ b/src/Views/Notifications.vala @@ -1,20 +1,17 @@ public class Tuba.Views.Notifications : Views.Timeline, AccountHolder, Streamable { - protected InstanceAccount? last_account = null; - private Binding badge_number_binding; - public Notifications () { - Object ( - url: "/api/v1/notifications", - label: _("Notifications"), - icon: "tuba-bell-symbolic", - badge_number: 0, - needs_attention: false - ); - accepts = typeof (API.Notification); + + construct { + url = "/api/v1/notifications"; + label = _("Notifications"); + icon = "tuba-bell-symbolic"; + accepts = typeof (API.Notification); + badge_number = 0; + needs_attention = true; stream_event[InstanceAccount.EVENT_NOTIFICATION].connect (on_new_post); - } + } ~Notifications () { warning ("Destroying Notifications"); diff --git a/src/Views/Search.vala b/src/Views/Search.vala index dc684ec46..b1cf6f1a0 100644 --- a/src/Views/Search.vala +++ b/src/Views/Search.vala @@ -10,8 +10,8 @@ public class Tuba.Views.Search : Views.TabbedBase { Views.ContentBase statuses_tab; Views.ContentBase hashtags_tab; - public Search () { - Object (label: _("Search")); + construct { + label = _("Search"); bar = new Gtk.SearchBar () { search_mode_enabled = true diff --git a/src/Views/StatusStats.vala b/src/Views/StatusStats.vala index e5448ff18..ea236c45a 100644 --- a/src/Views/StatusStats.vala +++ b/src/Views/StatusStats.vala @@ -2,9 +2,11 @@ public class Tuba.Views.StatusStats : Views.TabbedBase { Views.ContentBase favorited; Views.ContentBase boosted; - public StatusStats (string status_id) { - Object (label: _("Post Stats")); + construct { + label = _("Post Stats"); + } + public StatusStats (string status_id) { favorited = add_timeline_tab ( // translators: title for a list of people that favorited a post _("Favorited By"), diff --git a/src/Widgets/Attachment/Image.vala b/src/Widgets/Attachment/Image.vala index 5368d0e15..1789d26e6 100644 --- a/src/Widgets/Attachment/Image.vala +++ b/src/Widgets/Attachment/Image.vala @@ -34,7 +34,6 @@ public class Tuba.Widgets.Attachment.Image : Widgets.Attachment.Item { can_shrink = true, keep_aspect_ratio = true, css_classes = {"attachment-picture"} - // content_fit = ContentFit.COVER // GTK 4.8 }; update_pic_content_fit (); From 7dec8b2b24858c86f43399a5118010f63ff797d4 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 12 Aug 2023 01:00:46 +0300 Subject: [PATCH 24/76] fix(FollowRequests): url <-> icon --- src/Views/FollowRequests.vala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Views/FollowRequests.vala b/src/Views/FollowRequests.vala index c257b28e6..e75c55057 100644 --- a/src/Views/FollowRequests.vala +++ b/src/Views/FollowRequests.vala @@ -1,8 +1,8 @@ public class Tuba.Views.FollowRequests : Views.Timeline { construct { - url = "tuba-address-book-new-symbolic"; + url = "/api/v1/follow_requests"; label = _("Follow Requests"); - icon = "/api/v1/follow_requests"; + icon = "tuba-address-book-new-symbolic"; accepts = typeof (API.Account); } From c62a26065a525f7c71f3158c2174cc022e2e9533 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 12 Aug 2023 01:03:47 +0300 Subject: [PATCH 25/76] feat(Status): move reactions to a sep widget --- data/ui/widgets/status.ui | 9 ------ src/Widgets/Status.vala | 38 ++---------------------- src/Widgets/Status/ReactionsRow.vala | 43 ++++++++++++++++++++++++++++ src/Widgets/Status/meson.build | 3 ++ src/Widgets/meson.build | 1 + 5 files changed, 50 insertions(+), 44 deletions(-) create mode 100644 src/Widgets/Status/ReactionsRow.vala create mode 100644 src/Widgets/Status/meson.build diff --git a/data/ui/widgets/status.ui b/data/ui/widgets/status.ui index 1be6fef7b..1ac8652d7 100644 --- a/data/ui/widgets/status.ui +++ b/data/ui/widgets/status.ui @@ -312,15 +312,6 @@ - - - 0 - 6 - 6 - - 100 - - 6 diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index 1e1cbf87c..81fa76f4a 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -93,7 +93,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { [GtkChild] protected unowned Gtk.Label spoiler_label_rev; [GtkChild] protected unowned Gtk.Box spoiler_status_con; - [GtkChild] public unowned Gtk.FlowBox emoji_reactions; [GtkChild] public unowned Gtk.Box actions; [GtkChild] public unowned Gtk.Box fr_actions; @@ -120,45 +119,14 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { public bool is_conversation_open { get; set; default = false; } + protected Adw.Bin emoji_reactions; public Gee.ArrayList? reactions { get { return status.formal.compat_status_reactions; } set { if (value == null) return; - var i = 0; - Gtk.FlowBoxChild? fb_child = null; - while ((fb_child = emoji_reactions.get_child_at_index (i)) != null) { - emoji_reactions.remove (fb_child); - i = i + 1; - } - - foreach (API.EmojiReaction p in value) { - if (p.count <= 0) return; - - var badge_button = new Gtk.Button () { - // translators: the variable is the emoji or its name if it's custom - tooltip_text = _("React with %s").printf (p.name) - }; - var badge = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6); - - if (p.url != null) { - badge.append (new Widgets.Emoji (p.url)); - } else { - badge.append (new Gtk.Label (p.name)); - } - - badge.append (new Gtk.Label (p.count.to_string ())); - badge_button.child = badge; - - if (p.me == true) { - badge_button.add_css_class ("accent"); - } - - // emoji_reactions.append(badge_button); // GTK >= 4.5 - emoji_reactions.insert (badge_button, -1); - } - - emoji_reactions.visible = value.size > 0; + emoji_reactions = new ReactionsRow (value); + content_column.insert_child_after (emoji_reactions, spoiler_stack); } } diff --git a/src/Widgets/Status/ReactionsRow.vala b/src/Widgets/Status/ReactionsRow.vala new file mode 100644 index 000000000..18ef4fd72 --- /dev/null +++ b/src/Widgets/Status/ReactionsRow.vala @@ -0,0 +1,43 @@ +// TODO: Move reaction logic from Accouncement.vala + +public class Tuba.Widgets.ReactionsRow : Adw.Bin { + Gtk.FlowBox reaction_box = new Gtk.FlowBox () { + column_spacing = 6, + row_spacing = 6, + // Lower values leave space between items + max_children_per_line = 100 + }; + + construct { + this.child = reaction_box; + } + + public ReactionsRow (Gee.ArrayList reactions) { + foreach (API.EmojiReaction p in reactions) { + if (p.count <= 0) continue; + + var badge_button = new Gtk.Button () { + // translators: the variable is the emoji or its name if it's custom + tooltip_text = _("React with %s").printf (p.name) + }; + var badge = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6); + + if (p.url != null) { + badge.append (new Widgets.Emoji (p.url)); + } else { + badge.append (new Gtk.Label (p.name)); + } + + badge.append (new Gtk.Label (p.count.to_string ())); + badge_button.child = badge; + + if (p.me == true) { + badge_button.add_css_class ("accent"); + } + + reaction_box.append(badge_button); + } + + reaction_box.visible = reactions.size > 0; + } +} diff --git a/src/Widgets/Status/meson.build b/src/Widgets/Status/meson.build new file mode 100644 index 000000000..d960954ab --- /dev/null +++ b/src/Widgets/Status/meson.build @@ -0,0 +1,3 @@ +sources += files( + 'ReactionsRow.vala' +) diff --git a/src/Widgets/meson.build b/src/Widgets/meson.build index 2a3f48389..fc9400a49 100644 --- a/src/Widgets/meson.build +++ b/src/Widgets/meson.build @@ -22,3 +22,4 @@ sources += files( ) subdir('Attachment') +subdir('Status') From 80ed4ac6e33a9219ae48e99309be79516c5df461 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 12 Aug 2023 01:04:19 +0300 Subject: [PATCH 26/76] feat(Status): move follow request buttons to a sep widget --- data/ui/widgets/status.ui | 36 -------------------- src/Views/FollowRequests.vala | 24 +++++++------- src/Widgets/Status.vala | 10 ++---- src/Widgets/Status/FollowRequestRow.vala | 42 ++++++++++++++++++++++++ src/Widgets/Status/meson.build | 1 + 5 files changed, 58 insertions(+), 55 deletions(-) create mode 100644 src/Widgets/Status/FollowRequestRow.vala diff --git a/data/ui/widgets/status.ui b/data/ui/widgets/status.ui index 1ac8652d7..955c1fdec 100644 --- a/data/ui/widgets/status.ui +++ b/data/ui/widgets/status.ui @@ -320,42 +320,6 @@ - - - 0 - 1 - 6 - - - Decline - Decline - tuba-cross-large-symbolic - center - - - - - - Accept - Accept - tuba-check-round-outline-symbolic - center - - - - - - diff --git a/src/Views/FollowRequests.vala b/src/Views/FollowRequests.vala index e75c55057..103faf5bf 100644 --- a/src/Views/FollowRequests.vala +++ b/src/Views/FollowRequests.vala @@ -11,18 +11,19 @@ public class Tuba.Views.FollowRequests : Views.Timeline { var widget_status = widget as Widgets.Status; if (widget_status != null) { - widget_status.fr_actions.visible = true; - widget_status.decline_fr_button.clicked.connect (() => on_decline (widget_status, obj as Widgetizable)); - widget_status.accept_fr_button.clicked.connect (() => on_accept (widget_status, obj as Widgetizable)); + var fr_row = new Widgets.FollowRequestRow (widget_status.kind_instigator.id); + fr_row.declined.connect ((fr_row, req) => on_decline (fr_row, req, obj as Widgetizable)); + fr_row.accepted.connect ((fr_row, req) => on_accept (fr_row, req, obj as Widgetizable)); + + widget_status.content_column.append (fr_row); } return widget; } - public void on_accept (Widgets.Status widget_status, Widgetizable widget) { - widget_status.fr_actions.sensitive = false; - new Request.POST (@"/api/v1/follow_requests/$(widget_status.kind_instigator.id)/authorize") - .with_account (accounts.active) + public void on_accept (Widgets.FollowRequestRow fr_row, Request req, Widgetizable widget) { + fr_row.sensitive = false; + req .then ((sess, msg, in_stream) => { var parser = Network.get_parser_from_inputstream (in_stream); var node = network.parse_node (parser); @@ -33,16 +34,15 @@ public class Tuba.Views.FollowRequests : Views.Timeline { if (found) model.remove (indx); } else { - widget_status.fr_actions.sensitive = true; + fr_row.sensitive = true; } }) .exec (); } - public void on_decline (Widgets.Status widget_status, Widgetizable widget) { - widget_status.fr_actions.sensitive = false; - new Request.POST (@"/api/v1/follow_requests/$(widget_status.kind_instigator.id)/reject") - .with_account (accounts.active) + public void on_decline (Widgets.FollowRequestRow fr_row, Request req, Widgetizable widget) { + fr_row.sensitive = false; + req .then ((sess, msg) => { uint indx; var found = model.find (widget, out indx); diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index 81fa76f4a..8378626ca 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -21,6 +21,7 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { public API.Account? kind_instigator { get; set; default = null; } private Gtk.Button? quoted_status_btn { get; set; default = null; } + // TODO: remove everything but polls, attachments and content private bool _is_quote = false; public bool is_quote { get { return _is_quote; } @@ -28,7 +29,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { _is_quote = value; menu_button.visible = !value; emoji_reactions.visible = !value; - fr_actions.visible = !value; actions.visible = !value; if (quoted_status_btn != null) quoted_status_btn.visible = !value; @@ -83,7 +83,8 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { [GtkChild] protected unowned Gtk.Image edited_indicator; [GtkChild] protected unowned Gtk.Image visibility_indicator; - [GtkChild] protected unowned Gtk.Box content_column; + // TODO: move to function + [GtkChild] public unowned Gtk.Box content_column; [GtkChild] protected unowned Gtk.Stack spoiler_stack; [GtkChild] protected unowned Gtk.Box content_box; [GtkChild] public unowned Widgets.MarkupView content; @@ -94,11 +95,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { [GtkChild] protected unowned Gtk.Box spoiler_status_con; [GtkChild] public unowned Gtk.Box actions; - [GtkChild] public unowned Gtk.Box fr_actions; - - [GtkChild] public unowned Gtk.Button accept_fr_button; - [GtkChild] public unowned Gtk.Button decline_fr_button; - [GtkChild] public unowned Widgets.VoteBox poll; protected Gtk.Button reply_button; diff --git a/src/Widgets/Status/FollowRequestRow.vala b/src/Widgets/Status/FollowRequestRow.vala new file mode 100644 index 000000000..5542beb13 --- /dev/null +++ b/src/Widgets/Status/FollowRequestRow.vala @@ -0,0 +1,42 @@ +public class Tuba.Widgets.FollowRequestRow : Gtk.Box { + public signal void declined (FollowRequestRow fr_row, Request req); + public signal void accepted (FollowRequestRow fr_row, Request req); + + public string id { get; set; } + + Gtk.Button decline_fr_button = new Gtk.Button.from_icon_name ("tuba-cross-large-symbolic") { + tooltip_text = _("Decline"), + halign = Gtk.Align.CENTER, + css_classes = { "flat", "circular", "error" } + }; + + Gtk.Button accept_fr_button = new Gtk.Button.from_icon_name ("tuba-check-round-outline-symbolic") { + tooltip_text = _("Accept"), + halign = Gtk.Align.CENTER, + css_classes = { "flat", "circular", "success" } + }; + + construct { + this.add_css_class ("ttl-post-actions"); + this.spacing = 0; + this.homogeneous = true; + + this.append (decline_fr_button); + this.append (accept_fr_button); + + decline_fr_button.clicked.connect (on_decline); + accept_fr_button.clicked.connect (on_accept); + } + + void on_decline () { + declined (this, new Request.POST (@"/api/v1/follow_requests/$id/reject").with_account (accounts.active)); + } + + void on_accept () { + accepted (this, new Request.POST (@"/api/v1/follow_requests/$id/authorize").with_account (accounts.active)); + } + + public FollowRequestRow (string t_id) { + id = t_id; + } +} diff --git a/src/Widgets/Status/meson.build b/src/Widgets/Status/meson.build index d960954ab..47225928c 100644 --- a/src/Widgets/Status/meson.build +++ b/src/Widgets/Status/meson.build @@ -1,3 +1,4 @@ sources += files( + 'FollowRequestRow.vala', 'ReactionsRow.vala' ) From 43010bb57da5c6b826d815d7e51e6cfd4fbb64e8 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 12 Aug 2023 01:04:36 +0300 Subject: [PATCH 27/76] fix(Notificatons): attention badge --- src/Views/Notifications.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Views/Notifications.vala b/src/Views/Notifications.vala index 0cabe95e9..e0c9cf194 100644 --- a/src/Views/Notifications.vala +++ b/src/Views/Notifications.vala @@ -8,7 +8,7 @@ public class Tuba.Views.Notifications : Views.Timeline, AccountHolder, Streamabl icon = "tuba-bell-symbolic"; accepts = typeof (API.Notification); badge_number = 0; - needs_attention = true; + needs_attention = false; stream_event[InstanceAccount.EVENT_NOTIFICATION].connect (on_new_post); } From b82c5e56dbc4180bec8874ada1381d6f9a3a669f Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 12 Aug 2023 01:05:23 +0300 Subject: [PATCH 28/76] chore: lint --- src/Widgets/Status/ReactionsRow.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Widgets/Status/ReactionsRow.vala b/src/Widgets/Status/ReactionsRow.vala index 18ef4fd72..a045e51b0 100644 --- a/src/Widgets/Status/ReactionsRow.vala +++ b/src/Widgets/Status/ReactionsRow.vala @@ -35,7 +35,7 @@ public class Tuba.Widgets.ReactionsRow : Adw.Bin { badge_button.add_css_class ("accent"); } - reaction_box.append(badge_button); + reaction_box.append (badge_button); } reaction_box.visible = reactions.size > 0; From 5a98425940a2c8e46d223e1d7e7ad4e99d6c5bc5 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Thu, 24 Aug 2023 01:15:53 +0300 Subject: [PATCH 29/76] feat(VoteBox): don't use lambdas --- src/Widgets/VoteBox.vala | 47 +++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/src/Widgets/VoteBox.vala b/src/Widgets/VoteBox.vala index aeeb6d434..b78910d24 100644 --- a/src/Widgets/VoteBox.vala +++ b/src/Widgets/VoteBox.vala @@ -11,18 +11,28 @@ public class Tuba.Widgets.VoteBox : Gtk.Box { construct { button_vote.set_label (_("Vote")); - button_vote.clicked.connect ((button) =>{ - Request voting = API.Poll.vote (accounts.active, poll.options, selected_index, poll.id); - voting.then ((sess, mess, in_stream) => { - var parser = Network.get_parser_from_inputstream (in_stream); - status_parent.poll = API.Poll.from_json (typeof (API.Poll), network.parse_node (parser)); - }) - .on_error ((code, reason) => {}).exec (); - }); + button_vote.clicked.connect (on_vote_button_clicked); notify["poll"].connect (update); button_vote.sensitive = false; } + private void on_vote_button_clicked (Gtk.Button button) { + button.sensitive = false; + API.Poll.vote (accounts.active, poll.options, selected_index, poll.id) + .then ((sess, mess, in_stream) => { + var parser = Network.get_parser_from_inputstream (in_stream); + status_parent.poll = API.Poll.from_json (typeof (API.Poll), network.parse_node (parser)); + + button.sensitive = true; + }) + .on_error ((code, reason) => { + var dlg = app.inform (_("Error"), reason); + dlg.present (); + button.sensitive = true; + }) + .exec (); + } + public string generate_css_style (int percentage) { return @".ttl-poll-$(percentage).ttl-poll-winner { background: linear-gradient(to right, alpha(@accent_bg_color, .5) $(percentage)%, transparent 0%); } .ttl-poll-$(percentage) { background: linear-gradient(to right, alpha(@view_fg_color, .1) $(percentage)%, transparent 0%); }"; // vala-lint=line-length } @@ -108,15 +118,7 @@ public class Tuba.Widgets.VoteBox : Gtk.Box { } check_option.poll_title = p.title; - check_option.toggled.connect ((radio) => { - var radio_votebutton = radio as Widgets.VoteCheckButton; - if (selected_index.contains (radio_votebutton.poll_title)) { - selected_index.remove (radio_votebutton.poll_title); - } else { - selected_index.add (radio_votebutton.poll_title); - } - button_vote.sensitive = selected_index.size > 0; - }); + check_option.toggled.connect (on_check_option_toggeled); foreach (int own_vote in poll.own_votes) { if (own_vote == row_number) { @@ -146,4 +148,15 @@ public class Tuba.Widgets.VoteBox : Gtk.Box { ? DateTime.humanize_ago (poll.expires_at) : DateTime.humanize_left (poll.expires_at); } + + private void on_check_option_toggeled (Gtk.CheckButton radio) { + var radio_votebutton = radio as Widgets.VoteCheckButton; + if (selected_index.contains (radio_votebutton.poll_title)) { + selected_index.remove (radio_votebutton.poll_title); + } else { + selected_index.add (radio_votebutton.poll_title); + } + + button_vote.sensitive = selected_index.size > 0; + } } From 3e39bac70ccb8acf64f21965dcd8410fa882516c Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Thu, 24 Aug 2023 01:16:56 +0300 Subject: [PATCH 30/76] feat(VoteBox): cleanup --- src/Widgets/VoteBox.vala | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/Widgets/VoteBox.vala b/src/Widgets/VoteBox.vala index b78910d24..e50ec816a 100644 --- a/src/Widgets/VoteBox.vala +++ b/src/Widgets/VoteBox.vala @@ -42,21 +42,16 @@ public class Tuba.Widgets.VoteBox : Gtk.Box { int64 winner_p = 0; Widgets.VoteCheckButton group_radio_option = null; - //clear all existing entries + // Clear all existing entries Gtk.Widget entry = poll_box.get_first_child (); while (entry != null) { poll_box.remove (entry); entry = poll_box.get_first_child (); } - //Reset button visibility - button_vote.set_visible (false); - if (!poll.expired && !poll.voted) { - button_vote.set_visible (true); - } - // if (poll.expired) { - // poll_box.sensitive = false; - // } + // Reset button visibility + button_vote.visible = !poll.expired && !poll.voted; + if (poll.expired || poll.voted) { foreach (API.PollOption p in poll.options) { if (p.votes_count > winner_p) { @@ -65,20 +60,20 @@ public class Tuba.Widgets.VoteBox : Gtk.Box { } } - //creates the entries of poll + // Create the entries of poll foreach (API.PollOption p in poll.options) { var row = new Adw.ActionRow () { css_classes = { "ttl-poll-row" }, - use_markup = false + use_markup = false, + title = p.title }; - //if it is own poll + // If it is own poll if (poll.expired || poll.voted) { // If multiple, Checkbox else radioButton var percentage = poll.votes_count > 0 ? ((double)p.votes_count / poll.votes_count) * 100 : 0.0; var provider = new Gtk.CssProvider (); - #if GTK_4_12 provider.load_from_string (generate_css_style ((int) percentage)); #else @@ -98,15 +93,15 @@ public class Tuba.Widgets.VoteBox : Gtk.Box { foreach (int own_vote in poll.own_votes) { if (own_vote == row_number) { - row.add_suffix (new Gtk.Image.from_icon_name ("tuba-check-round-outline-symbolic")); + row.add_suffix (new Gtk.Image.from_icon_name ("tuba-check-round-outline-symbolic") { + tooltip_text = _("Voted") + }); } } row.subtitle = "%.1f%%".printf (percentage); - row.title = p.title; poll_box.append (row); } else { - row.title = p.title; var check_option = new Widgets.VoteCheckButton (); if (!poll.multiple) { @@ -122,8 +117,11 @@ public class Tuba.Widgets.VoteBox : Gtk.Box { foreach (int own_vote in poll.own_votes) { if (own_vote == row_number) { - check_option.set_active (true); - row.add_suffix (new Gtk.Image.from_icon_name ("tuba-check-round-outline-symbolic")); + check_option.active = true; + row.add_suffix (new Gtk.Image.from_icon_name ("tuba-check-round-outline-symbolic"){ + tooltip_text = _("Voted") + }); + if (!selected_index.contains (p.title)) { selected_index.add (p.title); } @@ -131,7 +129,7 @@ public class Tuba.Widgets.VoteBox : Gtk.Box { } if (poll.expired || poll.voted) { - check_option.set_sensitive (false); + check_option.sensitive = false; } row.add_prefix (check_option); @@ -139,6 +137,7 @@ public class Tuba.Widgets.VoteBox : Gtk.Box { poll_box.append (row); } + row_number++; } From 618649758b2a313432a18244dcda8438bdf01fb7 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Thu, 24 Aug 2023 01:18:36 +0300 Subject: [PATCH 31/76] feat(LWW): avoid calling get_text on every allocate_shapes label's text only gets updated when the label itself does, no reason to call it every time --- src/Widgets/LabelWithWidgets.vala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Widgets/LabelWithWidgets.vala b/src/Widgets/LabelWithWidgets.vala index b6257a767..b341ac6ee 100644 --- a/src/Widgets/LabelWithWidgets.vala +++ b/src/Widgets/LabelWithWidgets.vala @@ -25,6 +25,7 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce } } + private string _label_text = ""; private string _text = ""; public string text { get { @@ -124,7 +125,7 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce int index = 0; for (var i = 0; i < widget_widths.length; i++) { - index = label.get_text ().index_of (OBJECT_REPLACEMENT_CHARACTER, index); + index = _label_text.index_of (OBJECT_REPLACEMENT_CHARACTER, index); if (index < 0) break; var width = widget_widths[i]; @@ -254,6 +255,7 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce _text = new_label; label.label = _text; + _label_text = label.get_text (); invalidate_child_widgets (); } } From 5a316ffa1f6af5d9f53937d4546309ea737dc490 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Thu, 24 Aug 2023 01:21:35 +0300 Subject: [PATCH 32/76] feat(LWW): avoid allocating children if there are none --- src/Widgets/LabelWithWidgets.vala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Widgets/LabelWithWidgets.vala b/src/Widgets/LabelWithWidgets.vala index b341ac6ee..ccd12dbcf 100644 --- a/src/Widgets/LabelWithWidgets.vala +++ b/src/Widgets/LabelWithWidgets.vala @@ -149,6 +149,8 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce } private void allocate_children () { + if (widgets.length == 0) return; + var run_iter = label.get_layout ().get_iter (); int i = 0; From 782bcef5e93f6eafc212d6d91ddf34973a81523f Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Thu, 24 Aug 2023 01:22:27 +0300 Subject: [PATCH 33/76] feat(LWW): avoid keeping track of 3 different arrays for children instead use a struct --- src/Widgets/LabelWithWidgets.vala | 70 ++++++++++++++----------------- 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/src/Widgets/LabelWithWidgets.vala b/src/Widgets/LabelWithWidgets.vala index ccd12dbcf..3e466cab3 100644 --- a/src/Widgets/LabelWithWidgets.vala +++ b/src/Widgets/LabelWithWidgets.vala @@ -8,10 +8,13 @@ // `LabelWithWidgets.with_label_and_widgets` to construct it with the desired text and widgets. public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Accessible { - private Gtk.Widget[] widgets = {}; - private int[] widget_heights = {}; - private int[] widget_widths = {}; + struct LWWWidget { + public Gtk.Widget widget; + public int height; + public int width; + } + private LWWWidget[] widgets = {}; public Gtk.Label label; private string _placeholder = ""; @@ -78,7 +81,7 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce ~LabelWithWidgets () { label.unparent (); foreach (var child in widgets) { - child.unparent (); + child.widget.unparent (); } } @@ -92,25 +95,24 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce } for (var i = 0; i < widgets.length; i++) { - Gtk.Widget child = widgets[i]; Gtk.Requisition size; Gtk.Requisition natural_size; - child.get_preferred_size (out size, out natural_size); + widgets[i].widget.get_preferred_size (out size, out natural_size); int width = natural_size.width; int height = natural_size.height; - int old_width = widget_widths[i]; - int old_height = widget_heights[i]; + int old_width = widgets[i].width; + int old_height = widgets[i].height; if (old_width > 0 || old_height > 0) { if (old_width != width || old_height != height) { - widget_widths[i] = width; - widget_heights[i] = height; + widgets[i].width = width; + widgets[i].height = height; child_size_changed = true; } } else { - widget_widths[i] = width; - widget_heights[i] = height; + widgets[i].width = width; + widgets[i].height = height; child_size_changed = true; } @@ -124,17 +126,15 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce var attrs = new Pango.AttrList (); int index = 0; - for (var i = 0; i < widget_widths.length; i++) { + for (var i = 0; i < widgets.length; i++) { index = _label_text.index_of (OBJECT_REPLACEMENT_CHARACTER, index); if (index < 0) break; - var width = widget_widths[i]; - var height = widget_heights[i]; var logical_rect = Pango.Rectangle () { x = 0, - y = - (height - (height / 4)) * Pango.SCALE, - width = width * Pango.SCALE, - height = height * Pango.SCALE + y = - (widgets[i].height - (widgets[i].height / 4)) * Pango.SCALE, + width = widgets[i].width * Pango.SCALE, + height = widgets[i].height * Pango.SCALE }; var shape = Pango.AttrShape.new (logical_rect, logical_rect); @@ -168,10 +168,6 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce if (has_shape_attr) { if (i < widgets.length) { - var widget = widgets[i]; - var width = widget_widths[i]; - var height = widget_heights[i]; - Pango.Rectangle logical_rect; run_iter.get_run_extents (null, out logical_rect); @@ -182,10 +178,10 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce var allocation = Gtk.Allocation () { x = pango_pixels (logical_rect.x) + offset_x, y = pango_pixels (logical_rect.y) + offset_y, - height = height, - width = width + height = widgets[i].height, + width = widgets[i].width }; - widget.allocate_size (allocation, -1); + widgets[i].widget.allocate_size (allocation, -1); i++; } else { break; @@ -263,12 +259,12 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce } public void append_child (Gtk.Widget child) { - widgets += child; - widget_widths += 0; - widget_heights += 0; - + widgets += LWWWidget() { + widget = child, + width = 0, + height = 0 + }; child.set_parent (this); - invalidate_child_widgets (); } @@ -284,23 +280,19 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce public void set_children (Gtk.Widget[] t_widgets) { foreach (var child in widgets) { - child.unparent (); - child.destroy (); + child.widget.unparent (); + child.widget.destroy (); } - widgets = {}; - widget_widths = {}; - widget_heights = {}; - foreach (unowned Gtk.Widget widget in t_widgets) { append_child (widget); } } private void invalidate_child_widgets () { - for (var i = 0; i < widget_widths.length; i++) { - widget_widths[i] = 0; - widget_heights[i] = 0; + for (var i = 0; i < widgets.length; i++) { + widgets[i].width = 0; + widgets[i].height = 0; } this.allocate_shapes (); this.queue_resize (); From 62ffa838b119a4386daac90dc84933a3f86f52b3 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Thu, 24 Aug 2023 01:29:34 +0300 Subject: [PATCH 34/76] chore: lint --- src/Widgets/LabelWithWidgets.vala | 2 +- src/Widgets/VoteBox.vala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Widgets/LabelWithWidgets.vala b/src/Widgets/LabelWithWidgets.vala index 3e466cab3..2d4d04b92 100644 --- a/src/Widgets/LabelWithWidgets.vala +++ b/src/Widgets/LabelWithWidgets.vala @@ -259,7 +259,7 @@ public class Tuba.Widgets.LabelWithWidgets : Gtk.Widget, Gtk.Buildable, Gtk.Acce } public void append_child (Gtk.Widget child) { - widgets += LWWWidget() { + widgets += LWWWidget () { widget = child, width = 0, height = 0 diff --git a/src/Widgets/VoteBox.vala b/src/Widgets/VoteBox.vala index e50ec816a..41485a577 100644 --- a/src/Widgets/VoteBox.vala +++ b/src/Widgets/VoteBox.vala @@ -16,7 +16,7 @@ public class Tuba.Widgets.VoteBox : Gtk.Box { button_vote.sensitive = false; } - private void on_vote_button_clicked (Gtk.Button button) { + private void on_vote_button_clicked (Gtk.Button button) { button.sensitive = false; API.Poll.vote (accounts.active, poll.options, selected_index, poll.id) .then ((sess, mess, in_stream) => { @@ -118,7 +118,7 @@ public class Tuba.Widgets.VoteBox : Gtk.Box { foreach (int own_vote in poll.own_votes) { if (own_vote == row_number) { check_option.active = true; - row.add_suffix (new Gtk.Image.from_icon_name ("tuba-check-round-outline-symbolic"){ + row.add_suffix (new Gtk.Image.from_icon_name ("tuba-check-round-outline-symbolic") { tooltip_text = _("Voted") }); From 3992c3a9d811e28508f9f90bdd070219539fc07e Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Fri, 25 Aug 2023 06:08:50 +0300 Subject: [PATCH 35/76] feat(Status): split actions using a generic 'action' request for fav, unfav, boost, unboost, bookmark, unbookmark is kind-of limiting already but could break at a feature mastoapi change. It's better to treat each one as its own request as boosting already takes an extra optional param for visibility. The old 'action' function is still used internally --- src/API/Status.vala | 49 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/API/Status.vala b/src/API/Status.vala index 8a1b40676..a0911ac87 100644 --- a/src/API/Status.vala +++ b/src/API/Status.vala @@ -184,12 +184,59 @@ public class Tuba.API.Status : Entity, Widgetizable { return result; } - public Request action (string action) { + private Request action (string action) { var req = new Request.POST (@"/api/v1/statuses/$(formal.id)/$action").with_account (accounts.active); req.priority = Soup.MessagePriority.HIGH; return req; } + public Request favourite_req () { + return action ("favourite"); + } + + public Request unfavourite_req () { + return action ("unfavourite"); + } + + public Request bookmark_req () { + return action ("bookmark"); + } + + public Request unbookmark_req () { + return action ("unbookmark"); + } + + public enum ReblogVisibility { + PUBLIC, + UNLISTED, + PRIVATE; + + public string to_string () { + switch (this) { + case PUBLIC: + return "public"; + case UNLISTED: + return "unlisted"; + case PRIVATE: + return "private"; + default: + return ""; + } + } + } + + public Request reblog_req (ReblogVisibility? visibility = null) { + var req = action ("reblog"); + if (visibility != null) + req.with_form_data ("visibility", visibility.to_string ()); + + return req; + } + + public Request unreblog_req () { + return action ("unreblog"); + } + public Request annihilate () { return new Request.DELETE (@"/api/v1/statuses/$id") .with_account (accounts.active); From 63d0be0c55265b0cd3d2555ae112d6a62270eb95 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Fri, 25 Aug 2023 06:11:20 +0300 Subject: [PATCH 36/76] feat(EntityCache): force insert option this allows us to force update entities when we need to update them --- src/Services/Cache/EntityCache.vala | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Services/Cache/EntityCache.vala b/src/Services/Cache/EntityCache.vala index f1ed8b678..2e978afc3 100644 --- a/src/Services/Cache/EntityCache.vala +++ b/src/Services/Cache/EntityCache.vala @@ -10,7 +10,7 @@ public class Tuba.EntityCache : AbstractCache { return null; } - public Entity lookup_or_insert (owned Json.Node node, owned Type type) { + public Entity lookup_or_insert (owned Json.Node node, owned Type type, bool force = false) { Entity entity = null; var id = get_node_cache_id (node); @@ -21,11 +21,9 @@ public class Tuba.EntityCache : AbstractCache { } catch (Error e) { warning (@"Error getting Entity from json: $(e.message)"); } - } - else { - + } else { // Entity can be reused from cache - if (contains (id)) { + if (!force && contains (id)) { entity = lookup (get_key (id)) as Entity; message (@"Reused: $id"); } From 32d20f612fedda0e4e828cf7c18d9f9eb872f9ef Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Fri, 25 Aug 2023 06:11:57 +0300 Subject: [PATCH 37/76] chore: construct object --- src/Widgets/Status/FollowRequestRow.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Widgets/Status/FollowRequestRow.vala b/src/Widgets/Status/FollowRequestRow.vala index 5542beb13..9ebe1b7c9 100644 --- a/src/Widgets/Status/FollowRequestRow.vala +++ b/src/Widgets/Status/FollowRequestRow.vala @@ -37,6 +37,6 @@ public class Tuba.Widgets.FollowRequestRow : Gtk.Box { } public FollowRequestRow (string t_id) { - id = t_id; + Object (id: t_id); } } From dbec45b230bfcb5cf794dc8417a1c64508b76ee2 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Fri, 25 Aug 2023 06:24:17 +0300 Subject: [PATCH 38/76] feat(Status): ActionRow let's decouple the actions from the status widget. Also get rid of LockableToggleButton. A toggle button wasn't the right widget for the job, we need to have control over the active state of the button but also handle clicked events ourselves. Network requests happen on click and the active state of the buttons can change multiple times (changes at the beginning and resets on error) before it finishes. Toggle button's toggle signal emits every time the active state changes which the locks had to take care of to avoid doing network requests when in the middle of one or during binding. Additionally, it's better to treat each button as unique instead of assuming they do the same thing but with a different entpoint. They are similar but not identical, boost will have different options eventually (visibility, quote), reply triggers a dialog and bookmark doesn't have a counter --- src/Widgets/LockableToggleButton.vala | 36 ----- src/Widgets/Status.vala | 117 ++------------ src/Widgets/Status/ActionsRow.vala | 217 ++++++++++++++++++++++++++ src/Widgets/Status/meson.build | 1 + src/Widgets/StatusActionButton.vala | 182 ++++++--------------- src/Widgets/meson.build | 1 - 6 files changed, 278 insertions(+), 276 deletions(-) delete mode 100644 src/Widgets/LockableToggleButton.vala create mode 100644 src/Widgets/Status/ActionsRow.vala diff --git a/src/Widgets/LockableToggleButton.vala b/src/Widgets/LockableToggleButton.vala deleted file mode 100644 index 1addd729f..000000000 --- a/src/Widgets/LockableToggleButton.vala +++ /dev/null @@ -1,36 +0,0 @@ -// This button prevents changes to its "active" property while it's locked. -// -// This widget is intended to be used with Statuses where their properties -// can be used to drive network requests. - -public abstract class Tuba.LockableToggleButton : Gtk.ToggleButton { - - uint _locks = 0; - public bool locked { - get { return this._locks > 0; } - } - - construct { - this.toggled.connect (on_toggled); - } - - protected void set_locked (bool is_locked) { - if (is_locked) - _locks++; - else - _locks--; - } - - protected virtual bool can_change () { - return true; - } - - protected void on_toggled () { - if (!locked && can_change ()) { - commit_change (); - } - } - - protected abstract void commit_change (); - -} diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index 8378626ca..27f70447b 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -97,12 +97,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { [GtkChild] public unowned Gtk.Box actions; [GtkChild] public unowned Widgets.VoteBox poll; - protected Gtk.Button reply_button; - protected Adw.ButtonContent reply_button_content; - protected StatusActionButton reblog_button; - protected StatusActionButton favorite_button; - protected StatusActionButton bookmark_button; - protected Gtk.PopoverMenu context_menu { get; set; } private const GLib.ActionEntry[] ACTION_ENTRIES = { {"copy-url", copy_url}, @@ -145,8 +139,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { if (settings.scale_emoji_hover) add_css_class ("lww-scale-emoji-hover"); - rebuild_actions (); - settings.notify["larger-font-size"].connect (settings_updated); settings.notify["larger-line-height"].connect (settings_updated); settings.notify["scale-emoji-hover"].connect (settings_updated); @@ -166,12 +158,10 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { name_button.clicked.connect (() => name_label.on_activate_link (status.formal.account.handle)); - show_view_stats_action (); - reblog_button.content.notify["label"].connect (show_view_stats_action); - favorite_button.content.notify["label"].connect (show_view_stats_action); + stats_simple_action.set_enabled (false); } - private bool has_stats { get { return reblog_button.content.label != "" || favorite_button.content.label != ""; } } + private bool has_stats { get { return status.formal.reblogs_count != 0 || status.formal.favourites_count != 0; } } private void show_view_stats_action () { stats_simple_action.set_enabled (has_stats); } @@ -475,6 +465,10 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { private Gtk.Button prev_card; const string[] ALLOWED_CARD_TYPES = { "link", "video" }; protected virtual void bind () { + var action_row = new ActionsRow (this.status.formal); + action_row.reply.connect (on_reply_button_clicked); + content_column.append (action_row); + this.content.instance_emojis = status.formal.emojis_map; this.content.content = status.formal.content; @@ -526,38 +520,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { name_label.instance_emojis = status.formal.account.emojis_map; name_label.label = title_text; - // Actions - reblog_button.bind (status.formal); - bookmark_button.bind (status.formal); - favorite_button.bind (status.formal); - favorite_button.update_button_content (status.formal.favourites_count); - - reply_button.set_child (reply_button_content); - reply_button_content.icon_name = status.formal.in_reply_to_id != null - ? "tuba-reply-all-symbolic" - : "tuba-reply-sender-symbolic"; - if (status.formal.replies_count > 0) { - reply_button_content.margin_start = 12; - reply_button_content.margin_end = 9; - reply_button_content.label = status.formal.replies_count.to_string (); - } else { - reply_button_content.margin_start = 0; - reply_button_content.margin_end = 0; - reply_button_content.label = ""; - } - - if (!status.can_be_boosted) { - reblog_button.sensitive = false; - reblog_button.tooltip_text = _("This post can't be boosted"); - reblog_button.content.icon_name = t_visibility.icon_name; - } - else { - reblog_button.sensitive = true; - reblog_button.tooltip_text = _("Boost"); - reblog_button.content.icon_name = "tuba-media-playlist-repeat-symbolic"; - reblog_button.update_button_content (status.formal.reblogs_count); - } - if (status.id == "") { actions.destroy (); date_label.destroy (); @@ -578,6 +540,10 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { content_box.append (prev_card); } catch {} } + + show_view_stats_action (); + status.formal.notify["reblogs-count"].connect (show_view_stats_action); + status.formal.notify["favourites-count"].connect (show_view_stats_action); } void open_card_url () { @@ -593,69 +559,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { new Dialogs.Compose.reply (status.formal, on_reply); } - protected virtual void append_actions () { - reply_button = new Gtk.Button (); - reply_button_content = new Adw.ButtonContent () { - css_classes = { "ttl-status-action-reply" }, - tooltip_text = _("Reply") - }; - reply_button.clicked.connect (on_reply_button_clicked); - actions.append (reply_button); - - reblog_button = new StatusActionButton () { - prop_name = "reblogged", - action_on = "reblog", - action_off = "unreblog", - css_classes = { "ttl-status-action-reblog" }, - tooltip_text = _("Boost") - }; - actions.append (reblog_button); - - favorite_button = new StatusActionButton () { - prop_name = "favourited", - action_on = "favourite", - action_off = "unfavourite", - icon_name = "tuba-unstarred-symbolic", - icon_toggled_name = "tuba-starred-symbolic", - css_classes = { "ttl-status-action-star" }, - tooltip_text = _("Favorite") - }; - actions.append (favorite_button); - - bookmark_button = new StatusActionButton () { - prop_name = "bookmarked", - action_on = "bookmark", - action_off = "unbookmark", - icon_name = "tuba-bookmarks-symbolic", - icon_toggled_name = "tuba-bookmarks-filled-symbolic", - css_classes = { "ttl-status-action-bookmark" }, - tooltip_text = _("Bookmark") - }; - actions.append (bookmark_button); - } - - void rebuild_actions () { - for (var w = actions.get_first_child (); w != null; w = w.get_next_sibling ()) - actions.remove (w); - - append_actions (); - - // var menu_button = new MenuButton (); //TODO: Status menu - // menu_button.icon_name = "tuba-view-more-symbolic"; - // menu_button.get_first_child ().add_css_class ("flat"); - // actions.append (menu_button); - - for (var w = actions.get_first_child (); w != null; w = w.get_next_sibling ()) { - w.add_css_class ("flat"); - w.add_css_class ("circular"); - w.halign = Gtk.Align.START; - w.hexpand = true; - } - - var w = actions.get_last_child (); - w.hexpand = false; - } - [GtkCallback] public void toggle_spoiler () { reveal_spoiler = !reveal_spoiler; } diff --git a/src/Widgets/Status/ActionsRow.vala b/src/Widgets/Status/ActionsRow.vala new file mode 100644 index 000000000..39ba74a57 --- /dev/null +++ b/src/Widgets/Status/ActionsRow.vala @@ -0,0 +1,217 @@ +public class Tuba.Widgets.ActionsRow : Gtk.Box { + public signal void reply (Gtk.Button btn); + public API.Status status { get; set; } + + StatusActionButton reply_button; + StatusActionButton reblog_button; + StatusActionButton favorite_button; + StatusActionButton bookmark_button; + + public ActionsRow (API.Status t_status) { + Object (status: t_status); + + bind (); + } + + Binding[] bindings = {}; + public void bind () { + if (bindings.length != 0) return; + + bindings += this.status.bind_property ("replies-count", reply_button, "amount", GLib.BindingFlags.SYNC_CREATE); + bindings += this.status.bind_property ("in-reply-to-id", reply_button, "default_icon_name", BindingFlags.SYNC_CREATE, (b, src, ref target) => { + target.set_string (src.get_string () != null ? "tuba-reply-all-symbolic" : "tuba-reply-sender-symbolic"); + return true; + }); + + bindings += this.status.bind_property ("can-be-boosted", reblog_button, "sensitive", BindingFlags.SYNC_CREATE, (b, src, ref target) => { + bool src_val = src.get_boolean (); + target.set_boolean (src_val); + + if (src_val) { + reblog_button.tooltip_text = _("Boost"); + reblog_button.default_icon_name = "tuba-media-playlist-repeat-symbolic"; + } else { + reblog_button.tooltip_text = _("This post can't be boosted"); + reblog_button.default_icon_name = accounts.active.visibility[this.status.visibility].icon_name; + } + + return true; + }); + bindings += this.status.bind_property ("reblogged", reblog_button, "active", GLib.BindingFlags.SYNC_CREATE); + bindings += this.status.bind_property ("reblogs-count", reblog_button, "amount", GLib.BindingFlags.SYNC_CREATE); + + bindings += this.status.bind_property ("favourited", favorite_button, "active", GLib.BindingFlags.SYNC_CREATE); + bindings += this.status.bind_property ("favourites-count", favorite_button, "amount", GLib.BindingFlags.SYNC_CREATE); + + bindings += this.status.bind_property ("bookmarked", bookmark_button, "active", GLib.BindingFlags.SYNC_CREATE); + } + + public void unbind () { + foreach (var binding in bindings) { + binding.unbind (); + } + + bindings = {}; + } + + construct { + this.add_css_class ("ttl-post-actions"); + this.spacing = 6; + + reply_button = new StatusActionButton.with_icon_name ("tuba-reply-sender-symbolic") { + active = false, + css_classes = { "ttl-status-action-reply", "flat", "circular" }, + halign = Gtk.Align.START, + hexpand = true, + tooltip_text = _("Reply") + }; + reply_button.clicked.connect (on_reply_button_clicked); + this.append (reply_button); + + reblog_button = new StatusActionButton.with_icon_name ("tuba-media-playlist-repeat-symbolic") { + css_classes = { "ttl-status-action-reblog", "flat", "circular" }, + halign = Gtk.Align.START, + hexpand = true, + tooltip_text = _("Boost") + }; + reblog_button.clicked.connect (on_boost_button_clicked); + this.append (reblog_button); + + favorite_button = new StatusActionButton.with_icon_name ("tuba-unstarred-symbolic") { + active_icon_name = "tuba-starred-symbolic", + css_classes = { "ttl-status-action-star", "flat", "circular" }, + halign = Gtk.Align.START, + hexpand = true, + tooltip_text = _("Favorite") + }; + favorite_button.clicked.connect (on_favorite_button_clicked); + this.append (favorite_button); + + bookmark_button = new StatusActionButton.with_icon_name ("tuba-bookmarks-symbolic") { + active_icon_name = "tuba-bookmarks-filled-symbolic", + css_classes = { "ttl-status-action-bookmark", "flat", "circular" }, + halign = Gtk.Align.START, + hexpand = false, + tooltip_text = _("Bookmark") + }; + bookmark_button.clicked.connect (on_bookmark_button_clicked); + this.append (bookmark_button); + } + + private void on_reply_button_clicked (Gtk.Button btn) { + reply (btn); + } + + private void on_bookmark_button_clicked (Gtk.Button btn) { + var status_btn = btn as StatusActionButton; + if (status_btn.working) return; + + status_btn.block_clicked (); + status_btn.active = !status_btn.active; + + string action; + Request req; + if (status_btn.active) { + action = "bookmark"; + req = this.status.bookmark_req (); + } else { + action = "unbookmark"; + req = this.status.unbookmark_req (); + } + + message (@"Performing status action '$action'…"); + mastodon_action (status_btn, req, action); + } + + private void on_favorite_button_clicked (Gtk.Button btn) { + var status_btn = btn as StatusActionButton; + if (status_btn.working) return; + + status_btn.block_clicked (); + status_btn.active = !status_btn.active; + + string action; + Request req; + if (status_btn.active) { + action = "favorite"; + req = this.status.favourite_req (); + } else { + action = "unfavorite"; + req = this.status.unfavourite_req (); + } + status_btn.amount += status_btn.active ? 1 : -1; + + message (@"Performing status action '$action'…"); + mastodon_action (status_btn, req, action, "favourites-count"); + } + + private void on_boost_button_clicked (Gtk.Button btn) { + var status_btn = btn as StatusActionButton; + if (status_btn.working) return; + + status_btn.block_clicked (); + status_btn.active = !status_btn.active; + + string action; + Request req; + if (status_btn.active) { + action = "reblog"; + req = this.status.reblog_req (); + } else { + action = "unreblog"; + req = this.status.unreblog_req (); + } + status_btn.amount += status_btn.active ? 1 : -1; + + message (@"Performing status action '$action'…"); + mastodon_action (status_btn, req, action, "reblogs-count"); + } + + private void mastodon_action (StatusActionButton status_btn, Request req, string action, string? count_property = null) { + req.await.begin ((o, res) => { + try { + req.await.end (res); + + if (count_property != null) { + int64 status_property_count; + this.status.get (count_property, out status_property_count); + this.status.set (count_property, status_property_count + (status_btn.active ? 1 : -1)); + } + + // Not reliable, it sometimes returns wrong info. + // But it should be the desired one, as it updated the whole object. + // + // var msg = req.await.end (res); + + // var parser = Network.get_parser_from_inputstream (msg.response_body); + // var node = network.parse_node (parser); + // var e = entity_cache.lookup_or_insert (node, typeof (API.Status), true); + + // if (count_property != null) { + // int64 e_property_count; + // int64 status_property_count; + // ((API.Status) e).get (count_property, out e_property_count); + // this.status.get (count_property, out status_property_count); + // if (e_property_count == status_property_count) { + // ((API.Status) e).set (count_property, e_property_count + (status_btn.active ? 1 : -1)); + // } + // } + + // this.status.patch (e); + message (@"Status action '$action' complete"); + } catch (Error e) { + warning (@"Couldn't perform action \"$action\" on a Status:"); + warning (e.message); + + var dlg = app.inform (_("Network Error"), e.message); + dlg.present (); + + if (count_property != null) + status_btn.amount += status_btn.active ? -1 : 1; + status_btn.active = !status_btn.active; + } + + status_btn.unblock_clicked (); + }); + } +} diff --git a/src/Widgets/Status/meson.build b/src/Widgets/Status/meson.build index 47225928c..b32460832 100644 --- a/src/Widgets/Status/meson.build +++ b/src/Widgets/Status/meson.build @@ -1,4 +1,5 @@ sources += files( + 'ActionsRow.vala', 'FollowRequestRow.vala', 'ReactionsRow.vala' ) diff --git a/src/Widgets/StatusActionButton.vala b/src/Widgets/StatusActionButton.vala index e2458ec2c..5eac798d8 100644 --- a/src/Widgets/StatusActionButton.vala +++ b/src/Widgets/StatusActionButton.vala @@ -1,167 +1,85 @@ -public class Tuba.StatusActionButton : LockableToggleButton { - - public Request req { get; set; default = null; } - - public Object object { get; set; default = null; } - public string prop_name { get; set; } - public string action_on { get; construct set; } - public string action_off { get; construct set; } - public string icon_toggled_name { get; set; default = null; } - public string default_icon_name { get; set; default = null; } - public bool increase_fav { get; set; default = false; } - public bool increase_reblog { get; set; default = false; } +public class Tuba.StatusActionButton : Gtk.Button { public Adw.ButtonContent content { get; set; } - public new string icon_name { + + private string _default_icon_name = ""; + public string default_icon_name { get { - return content.icon_name; + return _default_icon_name; } set { - content.icon_name = value; + _default_icon_name = value; + update_button_style (); } } - construct { - content = new Adw.ButtonContent (); - this.child = content; - } + public string? active_icon_name { get; construct set; default = null; } + + public bool working { get; private set; default = false; } - ~StatusActionButton () { - if (object != null) { - object.notify[prop_name].disconnect (on_object_notify); + private int64 _amount = 0; + public int64 amount { + get { return _amount; } + set { + _amount = value; + update_button_content (value); } } - public void bind (Object obj) { - this.object = obj; - active = get_value (); - set_class_enabled (active); - set_toggled_icon (active); - object.notify[prop_name].connect (on_object_notify); - } + private bool _active = false; + public bool active { + get { + return _active; + } - public void update_button_content (int64 new_value) { - if (new_value == 0) { - content.label = ""; - content.margin_start = 0; - content.margin_end = 0; - } else { - content.label = Tuba.Units.shorten (new_value); - content.margin_start = 12; - content.margin_end = 9; + set { + _active = value; + update_button_style (value); } } - protected void set_class_enabled (bool is_active = true) { - if (is_active) { + private void update_button_style (bool value = active) { + if (value) { + remove_css_class ("flat"); add_css_class ("enabled"); + content.icon_name = active_icon_name ?? default_icon_name; } else { + add_css_class ("flat"); remove_css_class ("enabled"); + content.icon_name = default_icon_name; } } - protected void set_toggled_icon (bool is_active = true) { - if (icon_toggled_name != null) { - if (content.icon_name != null && this.default_icon_name == null) { - default_icon_name = content.icon_name; - } - if (is_active) { - content.icon_name = icon_toggled_name; - } else { - content.icon_name = default_icon_name; - } - } - } + private void update_button_content (int64 new_value) { + if (new_value == 0) { + content.label = ""; + content.margin_start = 0; + content.margin_end = 0; - protected void on_object_notify (ParamSpec pspec) { - if (locked) return; + } - set_locked (true); - var val = get_value (); - active = val; - set_locked (false); - } - - protected void set_value (bool state) { - var val = Value (Type.BOOLEAN); - val.set_boolean (state); - object.set_property (prop_name, val); - active = val.get_boolean (); + content.label = Tuba.Units.shorten (new_value); + content.margin_start = 12; + content.margin_end = 9; } - protected bool get_value () { - var val = Value (Type.BOOLEAN); - object.get_property (prop_name, ref val); - return val.get_boolean (); + public void block_clicked () { + working = true; } - protected override bool can_change () { - if (object == null) { - warning ("No object to operate on. Did you forget to bind the status?"); - return false; - } - - if (req != null) - return false; // Don't send another request if there's one already pending - - return active != get_value (); // Ignore if this got triggered while unchanged. + public void unblock_clicked () { + working = false; } - protected void update_stats (API.Status obj, string action) { - int64 new_value = 0; - if (action == "favourite") { - obj.favourites_count++; - new_value = obj.favourites_count; - } else if (action == "unfavourite") { - obj.favourites_count--; - new_value = obj.favourites_count; - } else if (action == "reblog") { - obj.reblogs_count++; - new_value = obj.reblogs_count; - } else if (action == "unreblog") { - obj.reblogs_count--; - new_value = obj.reblogs_count; - } - - update_button_content (new_value); + construct { + content = new Adw.ButtonContent (); + this.child = content; } - protected override void commit_change () { - var entity = object as API.Status; - var action = !active ? action_off : action_on; - req = entity.action (action); - - set_locked (true); - set_class_enabled (active); - set_toggled_icon (active); - update_stats (entity, action); - - message (@"Performing status action '$action'…"); - req.await.begin ((o, res) => { - try { - var msg = req.await.end (res); - var parser = Network.get_parser_from_inputstream (msg.response_body); - var node = network.parse_node (parser); - - var jobj = node.get_object (); - var received_value = jobj.get_boolean_member (prop_name); - set_value (received_value); - message (@"Status action '$action' complete"); - } catch (Error e) { - warning (@"Couldn't perform action \"$action\" on a Status:"); - warning (e.message); - update_stats (entity, active ? action_off : action_on); - var dlg = app.inform (_("Network Error"), e.message); - dlg.present (); - set_class_enabled (!active); - set_toggled_icon (!active); - active = !active; - } - - req = null; - set_locked (false); - }); + public StatusActionButton.with_icon_name (string icon_name) { + Object ( + default_icon_name: icon_name + ); } - } diff --git a/src/Widgets/meson.build b/src/Widgets/meson.build index fc9400a49..4adfe3e6c 100644 --- a/src/Widgets/meson.build +++ b/src/Widgets/meson.build @@ -8,7 +8,6 @@ sources += files( 'Emoji.vala', 'EmojiLabel.vala', 'LabelWithWidgets.vala', - 'LockableToggleButton.vala', 'MarkupView.vala', 'Notification.vala', 'PreviewCard.vala', From eafd8b2438ca47df03a6a326ac0958e6645d78f6 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 05:42:15 +0300 Subject: [PATCH 39/76] fix(ActionsRow): unbind on destruction --- src/Widgets/Status/ActionsRow.vala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Widgets/Status/ActionsRow.vala b/src/Widgets/Status/ActionsRow.vala index 39ba74a57..a42051620 100644 --- a/src/Widgets/Status/ActionsRow.vala +++ b/src/Widgets/Status/ActionsRow.vala @@ -13,6 +13,10 @@ public class Tuba.Widgets.ActionsRow : Gtk.Box { bind (); } + ~ActionsRow () { + unbind (); + } + Binding[] bindings = {}; public void bind () { if (bindings.length != 0) return; From c1ba829836285b5eb7f98848572ceee9f6d9db76 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 05:52:48 +0300 Subject: [PATCH 40/76] fix: only set paintable on is_loaded --- src/Dialogs/Composer/AttachmentsPageAttachment.vala | 3 ++- src/Dialogs/Composer/Completion/EmojiProvider.vala | 3 ++- src/Dialogs/Composer/Completion/HandleProvider.vala | 3 ++- src/Views/MediaViewer.vala | 2 +- src/Views/Profile.vala | 3 ++- src/Widgets/Attachment/Image.vala | 3 ++- src/Widgets/Avatar.vala | 3 ++- src/Widgets/BookWyrmPage.vala | 3 ++- src/Widgets/Emoji.vala | 2 +- 9 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/Dialogs/Composer/AttachmentsPageAttachment.vala b/src/Dialogs/Composer/AttachmentsPageAttachment.vala index 6439a04d5..43c6d28ff 100644 --- a/src/Dialogs/Composer/AttachmentsPageAttachment.vala +++ b/src/Dialogs/Composer/AttachmentsPageAttachment.vala @@ -73,7 +73,8 @@ public class Tuba.AttachmentsPageAttachment : Widgets.Attachment.Item { } protected virtual void on_cache_response (bool is_loaded, owned Gdk.Paintable? data) { - pic.paintable = data; + if (is_loaded) + pic.paintable = data; } public virtual signal void remove_from_model () {} diff --git a/src/Dialogs/Composer/Completion/EmojiProvider.vala b/src/Dialogs/Composer/Completion/EmojiProvider.vala index ba04456aa..f24fde1b0 100644 --- a/src/Dialogs/Composer/Completion/EmojiProvider.vala +++ b/src/Dialogs/Composer/Completion/EmojiProvider.vala @@ -47,7 +47,8 @@ public class Tuba.EmojiProvider: Tuba.CompletionProvider { case GtkSource.CompletionColumn.ICON: var image = new Gtk.Image (); image_cache.request_paintable (emoji.url, (is_loaded, paintable) => { - image.paintable = paintable; + if (is_loaded) + image.paintable = paintable; }); cell.set_widget (image); break; diff --git a/src/Dialogs/Composer/Completion/HandleProvider.vala b/src/Dialogs/Composer/Completion/HandleProvider.vala index 051751a14..5d0922387 100644 --- a/src/Dialogs/Composer/Completion/HandleProvider.vala +++ b/src/Dialogs/Composer/Completion/HandleProvider.vala @@ -48,7 +48,8 @@ public class Tuba.HandleProvider: Tuba.CompletionProvider { var avatar = new Adw.Avatar (32, null, true); avatar.name = account.display_name; image_cache.request_paintable (account.avatar, (is_loaded, paintable) => { - avatar.custom_image = paintable; + if (is_loaded) + avatar.custom_image = paintable; }); cell.set_widget (avatar); break; diff --git a/src/Views/MediaViewer.vala b/src/Views/MediaViewer.vala index a83bee521..22c142da0 100644 --- a/src/Views/MediaViewer.vala +++ b/src/Views/MediaViewer.vala @@ -531,8 +531,8 @@ public class Tuba.Views.MediaViewer : Gtk.Box { if (alt_text != null) picture.alternative_text = alt_text; image_cache.request_paintable (url, (is_loaded, data) => { - picture.paintable = data; if (is_loaded) { + picture.paintable = data; item.done (); } }); diff --git a/src/Views/Profile.vala b/src/Views/Profile.vala index 4b49c4306..e5ed1189d 100644 --- a/src/Views/Profile.vala +++ b/src/Views/Profile.vala @@ -233,7 +233,8 @@ public class Tuba.Views.Profile : Views.Timeline { } void on_cache_response (bool is_loaded, owned Gdk.Paintable? data) { - background.paintable = data; + if (is_loaded) + background.paintable = data; } } diff --git a/src/Widgets/Attachment/Image.vala b/src/Widgets/Attachment/Image.vala index 1789d26e6..a51f07782 100644 --- a/src/Widgets/Attachment/Image.vala +++ b/src/Widgets/Attachment/Image.vala @@ -72,7 +72,8 @@ public class Tuba.Widgets.Attachment.Image : Widgets.Attachment.Item { } protected virtual void on_cache_response (bool is_loaded, owned Gdk.Paintable? data) { - pic.paintable = data; + if (is_loaded) + pic.paintable = data; } public signal void spoiler_revealed (); diff --git a/src/Widgets/Avatar.vala b/src/Widgets/Avatar.vala index d1224c16b..0e2c38896 100644 --- a/src/Widgets/Avatar.vala +++ b/src/Widgets/Avatar.vala @@ -53,6 +53,7 @@ public class Tuba.Widgets.Avatar : Gtk.Button { } void on_cache_response (bool is_loaded, owned Gdk.Paintable? data) { - avatar.custom_image = data; + if (is_loaded) + avatar.custom_image = data; } } diff --git a/src/Widgets/BookWyrmPage.vala b/src/Widgets/BookWyrmPage.vala index ecd6e7e27..7cf2d6e52 100644 --- a/src/Widgets/BookWyrmPage.vala +++ b/src/Widgets/BookWyrmPage.vala @@ -106,7 +106,8 @@ public class Tuba.Widgets.BookWyrmPage : Gtk.Box { } void on_cache_response (bool is_loaded, owned Gdk.Paintable? data) { - cover.paintable = data; + if (is_loaded) + cover.paintable = data; } [GtkCallback] diff --git a/src/Widgets/Emoji.vala b/src/Widgets/Emoji.vala index 35c625fc5..a200af54c 100644 --- a/src/Widgets/Emoji.vala +++ b/src/Widgets/Emoji.vala @@ -31,7 +31,7 @@ public class Tuba.Widgets.Emoji : Adw.Bin { } void on_cache_response (bool is_loaded, owned Gdk.Paintable? data) { - if (image != null) + if (image != null && is_loaded) image.paintable = data; } } From f2dcf443326e4534dfac74c9374c7a463f74d4e2 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 05:54:09 +0300 Subject: [PATCH 41/76] fix(ImageCache): avoid extra var for pixbuf --- src/Services/Cache/ImageCache.vala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Services/Cache/ImageCache.vala b/src/Services/Cache/ImageCache.vala index 9042990bb..5535524bc 100644 --- a/src/Services/Cache/ImageCache.vala +++ b/src/Services/Cache/ImageCache.vala @@ -9,9 +9,7 @@ public class Tuba.ImageCache : AbstractCache { throw new Oopsie.INSTANCE (@"Server returned $error"); } - var pixbuf = yield new Gdk.Pixbuf.from_stream_async (in_stream); - - return Gdk.Texture.for_pixbuf (pixbuf); + return Gdk.Texture.for_pixbuf (yield new Gdk.Pixbuf.from_stream_async (in_stream)); } public void request_paintable (string? url, owned OnItemChangedFn cb) { From 850fc8f6e0eb487f9abd32437f39ad88160033a8 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 05:55:41 +0300 Subject: [PATCH 42/76] feat(Settings): use array for key inits --- src/Services/Settings.vala | 44 ++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/Services/Settings.vala b/src/Services/Settings.vala index 9e703387d..0273bd793 100644 --- a/src/Services/Settings.vala +++ b/src/Services/Settings.vala @@ -21,29 +21,35 @@ public class Tuba.Settings : GLib.Settings { public bool enlarge_custom_emojis { get; set; } public string[] muted_notification_types { get; set; default = {}; } + private string[] keys_to_init = { + "active-account", + "color-scheme", + "default-post-visibility", + "autostart", + "timeline-page-size", + "live-updates", + "public-live-updates", + "show-spoilers", + "hide-preview-cards", + "larger-font-size", + "larger-line-height", + "aggressive-resolving", + "strip-tracking", + "scale-emoji-hover", + "letterbox-media", + "media-viewer-expand-pictures", + "enlarge-custom-emojis", + "muted-notification-types" + }; public Settings () { Object (schema_id: Build.DOMAIN); - init ("active-account"); - init ("color-scheme"); - init ("default-post-visibility"); - init ("autostart"); - init ("work-in-background", true); - init ("timeline-page-size"); - init ("live-updates"); - init ("public-live-updates"); - init ("show-spoilers"); - init ("hide-preview-cards"); - init ("larger-font-size"); - init ("larger-line-height"); - init ("aggressive-resolving"); - init ("strip-tracking"); - init ("scale-emoji-hover"); - init ("letterbox-media"); - init ("media-viewer-expand-pictures"); - init ("enlarge-custom-emojis"); - init ("muted-notification-types"); + foreach (var key in keys_to_init) { + init (key); + } + + init ("work-in-background", true); changed.connect (on_changed); } From c7c6fa4a9625980e1b9d5257433cd2112c6df1ca Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 05:56:41 +0300 Subject: [PATCH 43/76] fix(Status): avoid lambda --- src/Widgets/Status.vala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index 27f70447b..dad66da60 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -155,10 +155,13 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { action_group.add_action (edit_history_simple_action); this.insert_action_group ("status", action_group); + stats_simple_action.set_enabled (false); - name_button.clicked.connect (() => name_label.on_activate_link (status.formal.account.handle)); + name_button.clicked.connect (on_name_button_clicked); + } - stats_simple_action.set_enabled (false); + private void on_name_button_clicked () { + name_label.on_activate_link (status.formal.account.handle); } private bool has_stats { get { return status.formal.reblogs_count != 0 || status.formal.favourites_count != 0; } } From 59f7c0cc2a7ba9377779c7d13e46b692af33c602 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 05:58:40 +0300 Subject: [PATCH 44/76] feat(Status): remove check_actions this is something notifications should handle --- src/Widgets/Notification.vala | 7 ++++++- src/Widgets/Status.vala | 10 ---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/Widgets/Notification.vala b/src/Widgets/Notification.vala index 03770428e..ee30b7056 100644 --- a/src/Widgets/Notification.vala +++ b/src/Widgets/Notification.vala @@ -1,5 +1,4 @@ public class Tuba.Widgets.Notification : Widgets.Status { - public API.Notification notification { get; construct set; } public Notification (API.Notification obj) { @@ -15,6 +14,12 @@ public class Tuba.Widgets.Notification : Widgets.Status { kind: obj.kind, status: status ); + + if (obj.kind == InstanceAccount.KIND_FOLLOW || obj.kind == InstanceAccount.KIND_FOLLOW_REQUEST) { + actions.visible = false; + visibility_indicator.visible = false; + date_label.visible = false; + } } } diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index dad66da60..f377678b2 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -190,7 +190,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { } protected void init_menu_button () { - check_actions (); if (context_menu == null) { create_actions (); } @@ -325,14 +324,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { remove.present (); } - private void check_actions () { - if (kind == InstanceAccount.KIND_FOLLOW || kind == InstanceAccount.KIND_FOLLOW_REQUEST) { - actions.visible = false; - visibility_indicator.visible = false; - date_label.visible = false; - } - } - protected string spoiler_text { owned get { var text = status.formal.spoiler_text; @@ -392,7 +383,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { string icon = null; string descr = null; string label_url = null; - check_actions (); accounts.active.describe_kind (this.kind, out icon, out descr, this.kind_instigator, out label_url); if (icon == null) { From c6d5c956290defae547ac331d59a55bf4684d592 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 06:00:34 +0300 Subject: [PATCH 45/76] fix(Status): date not getting formatted correctly on patch when expanded --- src/Widgets/Status.vala | 45 ++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index f377678b2..a9ea425df 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -338,9 +338,27 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { public string spoiler_text_revealed { get; set; default = _("Sensitive"); } public bool reveal_spoiler { get; set; default = true; } + // separator between the bottom bar items + string expanded_separator = "·"; protected string date { owned get { - return DateTime.humanize (status.formal.created_at); + if (expanded) { + // translators: this is a "long" date format shown in places like expanded posts or + // the profile "Joined" field. You can find all the available specifiers + // on https://valadoc.org/glib-2.0/GLib.DateTime.format.html + // Please do not stray far from the original and only include day, month + // and year. + // If unsure, either leave it as-is or set it to %x. + var date_local = _("%B %e, %Y"); + + // Re-parse the date into a MONTH DAY, YEAR (separator) HOUR:MINUTES + var date_parsed = new GLib.DateTime.from_iso8601 (status.formal.edited_at ?? status.formal.created_at, null); + date_parsed = date_parsed.to_timezone (new TimeZone.local ()); + + return date_parsed.format (@"$date_local $expanded_separator %H:%M").replace (" ", ""); // %e prefixes with whitespace on single digits + } else { + return DateTime.humanize (status.formal.edited_at ?? status.formal.created_at); + } } } @@ -560,14 +578,15 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { status.formal.account.open (); } + bool expanded = false; public void expand_root () { + if (expanded) return; + + expanded = true; activatable = false; content.selectable = true; content.add_css_class ("ttl-large-body"); - // separator between the bottom bar items - var separator = "·"; - // Move the avatar & thread line into the name box status_box.remove (avatar_side); title_box.prepend (avatar_side); @@ -584,19 +603,7 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { indicators.remove (edited_indicator); indicators.remove (visibility_indicator); - // translators: this is a "long" date format shown in places like expanded posts or - // the profile "Joined" field. You can find all the available specifiers - // on https://valadoc.org/glib-2.0/GLib.DateTime.format.html - // Please do not stray far from the original and only include day, month - // and year. - // If unsure, either leave it as-is or set it to %x. - var date_local = _("%B %e, %Y"); - - // Re-parse the date into a MONTH DAY, YEAR (separator) HOUR:MINUTES - var date_parsed = new GLib.DateTime.from_iso8601 (status.formal.created_at, null); - date_parsed = date_parsed.to_timezone (new TimeZone.local ()); - - date_label.label = date_parsed.format (@"$date_local $separator %H:%M").replace (" ", ""); // %e prefixes with whitespace on single digits + date_label.label = this.date; date_label.wrap = true; // The bottom bar @@ -635,11 +642,11 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { bottom_info.append (application_label); } - add_separators_to_expanded_bottom (bottom_info, separator); + add_separators_to_expanded_bottom (bottom_info); } // Adds *separator* between all *flowbox* children - private void add_separators_to_expanded_bottom (Gtk.FlowBox flowbox, string separator = "·") { + private void add_separators_to_expanded_bottom (Gtk.FlowBox flowbox, string separator = expanded_separator) { var i = 0; var child = flowbox.get_child_at_index (i); while (child != null) { From 7cb818de020dc1b481232bcb1c7e7ff470841784 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 06:03:04 +0300 Subject: [PATCH 46/76] feat(Status): do not disconnect the kind avi clicked signal if the avi doesn't exist so does the signal --- src/Widgets/Status.vala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index a9ea425df..dde920259 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -389,7 +389,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { } Widgets.Avatar? actor_avatar = null; - ulong actor_avatar_singal; ulong header_button_activate; private Binding actor_avatar_binding; const string[] SHOULD_SHOW_ACTOR_AVATAR = { @@ -422,16 +421,15 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { if (this.kind_instigator != null) { actor_avatar_binding = this.bind_property ("kind_instigator", actor_avatar, "account", BindingFlags.SYNC_CREATE); - actor_avatar_singal = actor_avatar.clicked.connect (open_kind_instigator_account); + actor_avatar.clicked.connect (open_kind_instigator_account); } else { actor_avatar_binding = status.bind_property ("account", actor_avatar, "account", BindingFlags.SYNC_CREATE); - actor_avatar_singal = actor_avatar.clicked.connect (open_status_account); + actor_avatar.clicked.connect (open_status_account); } } avatar.add_css_class ("ttl-status-avatar-border"); avatar_overlay.child = actor_avatar; } else if (actor_avatar != null) { - actor_avatar.disconnect (actor_avatar_singal); actor_avatar_binding.unbind (); avatar_overlay.child = null; From 015df9609a3c4c333125c99f2242cb97a143e90d Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 06:06:33 +0300 Subject: [PATCH 47/76] feat(Status): make is_owned () and has_media () getters --- src/API/Status.vala | 14 +++++++++----- src/Dialogs/Composer/Dialog.vala | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/API/Status.vala b/src/API/Status.vala index a0911ac87..0f443d978 100644 --- a/src/API/Status.vala +++ b/src/API/Status.vala @@ -158,13 +158,17 @@ public class Tuba.API.Status : Entity, Widgetizable { app.main_window.open_view (view); } - public bool is_owned () { - return formal.account.id == accounts.active.id; + public bool is_mine { + get { + return formal.account.id == accounts.active.id; + } } - public bool has_media () { - return media_attachments != null && !media_attachments.is_empty; - } + public bool has_media { + get { + return media_attachments != null && !media_attachments.is_empty; + } + } public virtual string get_reply_mentions () { var result = ""; diff --git a/src/Dialogs/Composer/Dialog.vala b/src/Dialogs/Composer/Dialog.vala index c53a8b652..b000c5ec9 100644 --- a/src/Dialogs/Composer/Dialog.vala +++ b/src/Dialogs/Composer/Dialog.vala @@ -60,7 +60,7 @@ public class Tuba.Dialogs.Compose : Adw.Window { id = t_status.id; status = t_status.content; - if (t_status.has_media ()) { + if (t_status.has_media) { media_attachments = t_status.media_attachments; foreach (var t_attachment in t_status.media_attachments) { From 4a91775e9ce5c782157fbfeaaae1abaeee14544b Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 06:08:06 +0300 Subject: [PATCH 48/76] feat(Status): is_quote use array for the widgets this allows us to skip some repetitive null checks and visibility toggles --- src/Widgets/Status.vala | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index dde920259..f86c7fc8f 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -27,11 +27,20 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { get { return _is_quote; } set { _is_quote = value; - menu_button.visible = !value; - emoji_reactions.visible = !value; - actions.visible = !value; - if (quoted_status_btn != null) - quoted_status_btn.visible = !value; + + Gtk.Widget?[] widgets_to_toggle = { + menu_button, + emoji_reactions, + actions, + quoted_status_btn, + prev_card + }; + + foreach (var widget in widgets_to_toggle) { + if (widget != null) { + widget.visible = !value; + } + } } } From e9c05896aa750d2d3850fb697991a40c0ec51502 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 06:09:44 +0300 Subject: [PATCH 49/76] feat(Status): remove direct visibility indicator on bind if not applicable for clearing before re-binding --- src/Widgets/Status.vala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index f86c7fc8f..41ebeff94 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -528,7 +528,11 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { visibility_indicator.icon_name = t_visibility.small_icon_name; visibility_indicator.tooltip_text = t_visibility.name; - if (change_background_on_direct && status.formal.visibility == "direct") this.add_css_class ("direct"); + if (change_background_on_direct && status.formal.visibility == "direct") { + this.add_css_class ("direct"); + } else { + this.remove_css_class ("direct"); + } avatar.account = status.formal.account; reactions = status.formal.compat_status_reactions; From 104b2cdfe565f49dbac4e94c1382d080bce87568 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 06:10:41 +0300 Subject: [PATCH 50/76] feat(Status): disconnect signals on bind for clearing before re-binding --- src/Widgets/Status.vala | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index 41ebeff94..97ca12f06 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -482,10 +482,18 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { private Gtk.Button prev_card; const string[] ALLOWED_CARD_TYPES = { "link", "video" }; + ulong[] formal_handler_ids = {}; + ulong[] this_handler_ids = {}; protected virtual void bind () { - var action_row = new ActionsRow (this.status.formal); - action_row.reply.connect (on_reply_button_clicked); - content_column.append (action_row); + foreach (var handler_id in formal_handler_ids) { + status.formal.disconnect (handler_id); + } + formal_handler_ids = {}; + + foreach (var handler_id in this_handler_ids) { + this.disconnect (handler_id); + } + this_handler_ids = {}; this.content.instance_emojis = status.formal.emojis_map; this.content.content = status.formal.content; @@ -514,7 +522,7 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { reveal_spoiler = !status.formal.has_spoiler || settings.show_spoilers; update_spoiler_status (); - notify["reveal-spoiler"].connect (update_spoiler_status); + this_handler_ids += notify["reveal-spoiler"].connect (update_spoiler_status); handle_label.label = this.subtitle_text; date_label.label = this.date; @@ -564,8 +572,8 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { } show_view_stats_action (); - status.formal.notify["reblogs-count"].connect (show_view_stats_action); - status.formal.notify["favourites-count"].connect (show_view_stats_action); + formal_handler_ids += status.formal.notify["reblogs-count"].connect (show_view_stats_action); + formal_handler_ids += status.formal.notify["favourites-count"].connect (show_view_stats_action); } void open_card_url () { From 2830f2878085e7eae16dcb029f8eb6b0c25948b0 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 06:11:38 +0300 Subject: [PATCH 51/76] feat(Status): remove obsolete actions box --- data/ui/widgets/status.ui | 8 -------- src/Widgets/Status.vala | 5 ++++- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/data/ui/widgets/status.ui b/data/ui/widgets/status.ui index 955c1fdec..3a977586b 100644 --- a/data/ui/widgets/status.ui +++ b/data/ui/widgets/status.ui @@ -312,14 +312,6 @@ - - - 6 - - - diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index 97ca12f06..a59d64fde 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -103,7 +103,7 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { [GtkChild] protected unowned Gtk.Label spoiler_label_rev; [GtkChild] protected unowned Gtk.Box spoiler_status_con; - [GtkChild] public unowned Gtk.Box actions; + public ActionsRow actions { get; private set; } [GtkChild] public unowned Widgets.VoteBox poll; protected Gtk.PopoverMenu context_menu { get; set; } @@ -494,6 +494,9 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { this.disconnect (handler_id); } this_handler_ids = {}; + actions = new ActionsRow (this.status.formal); + actions.reply.connect (on_reply_button_clicked); + content_column.append (actions); this.content.instance_emojis = status.formal.emojis_map; this.content.content = status.formal.content; From 04478b356a006a14238471f5f3c5bb9f3f9767ea Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 06:14:43 +0300 Subject: [PATCH 52/76] feat(Status): remove actions on rebind --- src/Widgets/Status.vala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index a59d64fde..d558c7659 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -494,6 +494,11 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { this.disconnect (handler_id); } this_handler_ids = {}; + + if (actions != null) { + actions.unbind (); + content_column.remove (actions); + } actions = new ActionsRow (this.status.formal); actions.reply.connect (on_reply_button_clicked); content_column.append (actions); From e343cea4168c522e16f54df83b15b3839ee2282e Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 06:19:17 +0300 Subject: [PATCH 53/76] feat(VoteBox): simplify poll binding BIDIRECTIONAL is enough --- data/ui/widgets/status.ui | 3 --- src/Widgets/Status.vala | 18 ++++++------------ src/Widgets/VoteBox.vala | 3 +-- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/data/ui/widgets/status.ui b/data/ui/widgets/status.ui index 3a977586b..81780fe8f 100644 --- a/data/ui/widgets/status.ui +++ b/data/ui/widgets/status.ui @@ -300,9 +300,6 @@ False - - - diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index d558c7659..d398d44cb 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -104,8 +104,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { [GtkChild] protected unowned Gtk.Box spoiler_status_con; public ActionsRow actions { get; private set; } - [GtkChild] public unowned Widgets.VoteBox poll; - protected Gtk.PopoverMenu context_menu { get; set; } private const GLib.ActionEntry[] ACTION_ENTRIES = { {"copy-url", copy_url}, @@ -481,6 +479,7 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { } private Gtk.Button prev_card; + private Widgets.VoteBox poll; const string[] ALLOWED_CARD_TYPES = { "link", "video" }; ulong[] formal_handler_ids = {}; ulong[] this_handler_ids = {}; @@ -558,16 +557,11 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { name_label.instance_emojis = status.formal.account.emojis_map; name_label.label = title_text; - if (status.id == "") { - actions.destroy (); - date_label.destroy (); - } - - if (status.formal.poll == null) { - poll.hide (); - } else { - poll.status_parent = status.formal; - status.formal.bind_property ("poll", poll, "poll", BindingFlags.SYNC_CREATE); + if (poll != null) content_box.remove (poll); + if (status.formal.poll != null) { + poll = new Widgets.VoteBox (); + status.formal.bind_property ("poll", poll, "poll", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); + content_box.append (poll); } if (prev_card != null) content_box.remove (prev_card); diff --git a/src/Widgets/VoteBox.vala b/src/Widgets/VoteBox.vala index 41485a577..b996f9f0f 100644 --- a/src/Widgets/VoteBox.vala +++ b/src/Widgets/VoteBox.vala @@ -6,7 +6,6 @@ public class Tuba.Widgets.VoteBox : Gtk.Box { [GtkChild] protected unowned Gtk.Label expires_label; public API.Poll? poll { get; set;} - public API.Status? status_parent { get; set; } protected Gee.ArrayList selected_index = new Gee.ArrayList (); construct { @@ -21,7 +20,7 @@ public class Tuba.Widgets.VoteBox : Gtk.Box { API.Poll.vote (accounts.active, poll.options, selected_index, poll.id) .then ((sess, mess, in_stream) => { var parser = Network.get_parser_from_inputstream (in_stream); - status_parent.poll = API.Poll.from_json (typeof (API.Poll), network.parse_node (parser)); + poll = API.Poll.from_json (typeof (API.Poll), network.parse_node (parser)); button.sensitive = true; }) From 87df0066d7a4123bcafa37bcd7d5a6c619b83a9e Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 06:21:36 +0300 Subject: [PATCH 54/76] feat(Status): remove attachments on rebind --- data/ui/widgets/status.ui | 3 --- src/Widgets/Status.vala | 12 +++++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/data/ui/widgets/status.ui b/data/ui/widgets/status.ui index 81780fe8f..f787311eb 100644 --- a/data/ui/widgets/status.ui +++ b/data/ui/widgets/status.ui @@ -300,9 +300,6 @@ False - - - diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index d398d44cb..374e603cd 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -97,7 +97,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { [GtkChild] protected unowned Gtk.Stack spoiler_stack; [GtkChild] protected unowned Gtk.Box content_box; [GtkChild] public unowned Widgets.MarkupView content; - [GtkChild] protected unowned Widgets.Attachment.Box attachments; [GtkChild] protected unowned Gtk.Button spoiler_button; [GtkChild] protected unowned Gtk.Label spoiler_label; [GtkChild] protected unowned Gtk.Label spoiler_label_rev; @@ -479,6 +478,7 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { } private Gtk.Button prev_card; + private Widgets.Attachment.Box attachments; private Widgets.VoteBox poll; const string[] ALLOWED_CARD_TYPES = { "link", "video" }; ulong[] formal_handler_ids = {}; @@ -552,8 +552,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { avatar.account = status.formal.account; reactions = status.formal.compat_status_reactions; - attachments.has_spoiler = status.formal.sensitive; - attachments.list = status.formal.media_attachments; name_label.instance_emojis = status.formal.account.emojis_map; name_label.label = title_text; @@ -564,6 +562,14 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { content_box.append (poll); } + if (attachments != null) content_box.remove (attachments); + if (status.formal.has_media) { + attachments = new Widgets.Attachment.Box (); + attachments.has_spoiler = status.formal.sensitive; + attachments.list = status.formal.media_attachments; + content_box.append (attachments); + } + if (prev_card != null) content_box.remove (prev_card); if (!settings.hide_preview_cards && status.formal.card != null && status.formal.card.kind in ALLOWED_CARD_TYPES) { try { From 656cf50e9e3d12e4e90b6f859ef90e9aeb761b27 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sat, 26 Aug 2023 06:23:09 +0300 Subject: [PATCH 55/76] chore: lint --- src/API/Status.vala | 2 +- src/Services/Cache/AbstractCache.vala | 2 +- src/Services/Cache/EntityCache.vala | 2 -- src/Views/Profile.vala | 1 - src/Views/Timeline.vala | 4 ---- src/Widgets/Conversation.vala | 1 - src/Widgets/RichLabel.vala | 1 - src/Widgets/Status.vala | 6 ++---- 8 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/API/Status.vala b/src/API/Status.vala index 0f443d978..de9675c20 100644 --- a/src/API/Status.vala +++ b/src/API/Status.vala @@ -166,7 +166,7 @@ public class Tuba.API.Status : Entity, Widgetizable { public bool has_media { get { - return media_attachments != null && !media_attachments.is_empty; + return media_attachments != null && !media_attachments.is_empty; } } diff --git a/src/Services/Cache/AbstractCache.vala b/src/Services/Cache/AbstractCache.vala index c79ddf688..806460455 100644 --- a/src/Services/Cache/AbstractCache.vala +++ b/src/Services/Cache/AbstractCache.vala @@ -18,6 +18,7 @@ public class Tuba.AbstractCache : Object { setup_maintenance (); } } + public uint size { get { return items.size; } } @@ -100,5 +101,4 @@ public class Tuba.AbstractCache : Object { items.clear (); items_in_progress.clear (); } - } diff --git a/src/Services/Cache/EntityCache.vala b/src/Services/Cache/EntityCache.vala index 2e978afc3..997058a50 100644 --- a/src/Services/Cache/EntityCache.vala +++ b/src/Services/Cache/EntityCache.vala @@ -1,5 +1,4 @@ public class Tuba.EntityCache : AbstractCache { - // Must return unique id for each JSON entity node protected string? get_node_cache_id (owned Json.Node node) { var obj = node.get_object (); @@ -40,5 +39,4 @@ public class Tuba.EntityCache : AbstractCache { return entity; } - } diff --git a/src/Views/Profile.vala b/src/Views/Profile.vala index e5ed1189d..e94ed627e 100644 --- a/src/Views/Profile.vala +++ b/src/Views/Profile.vala @@ -1,5 +1,4 @@ public class Tuba.Views.Profile : Views.Timeline { - public API.Account profile { get; construct set; } public API.Relationship rs { get; construct set; } public bool include_replies { get; set; default = false; } diff --git a/src/Views/Timeline.vala b/src/Views/Timeline.vala index ea31f422a..ddbcf75f7 100644 --- a/src/Views/Timeline.vala +++ b/src/Views/Timeline.vala @@ -113,10 +113,6 @@ public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase base.dispose (); } - public virtual bool is_status_owned (API.Status status) { - return status.is_owned (); - } - public override void clear () { this.page_prev = null; this.page_next = null; diff --git a/src/Widgets/Conversation.vala b/src/Widgets/Conversation.vala index 8066f7adb..0745121b3 100644 --- a/src/Widgets/Conversation.vala +++ b/src/Widgets/Conversation.vala @@ -23,7 +23,6 @@ public class Tuba.Widgets.Conversation : Widgets.Status { } ); - // this.indicators.child_set_property (this.visibility_indicator, "position", 2); this.actions.destroy (); } diff --git a/src/Widgets/RichLabel.vala b/src/Widgets/RichLabel.vala index 792ad90b1..7ba95ea0b 100644 --- a/src/Widgets/RichLabel.vala +++ b/src/Widgets/RichLabel.vala @@ -120,5 +120,4 @@ public class Tuba.Widgets.RichLabel : Adw.Bin { public static bool should_resolve_url (string url) { return settings.aggressive_resolving || "@" in url || "user" in url; } - } diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index 374e603cd..7c7b4c014 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -27,7 +27,7 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { get { return _is_quote; } set { _is_quote = value; - + Gtk.Widget?[] widgets_to_toggle = { menu_button, emoji_reactions, @@ -199,6 +199,7 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { if (context_menu == null) { create_actions (); } + menu_button.popover = context_menu; menu_button.visible = true; } @@ -283,7 +284,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { entity_cache.remove (this.status.formal.uri); pin_changed (); }) - .on_error (() => {}) .exec (); } @@ -687,7 +687,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { } // Threads - public enum ThreadRole { NONE, START, @@ -737,5 +736,4 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { break; } } - } From 23e88141b1cef7d393adee527ccae7048052fcac Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sun, 27 Aug 2023 05:30:22 +0300 Subject: [PATCH 56/76] feat(HtmlUtils): use libxml2 warning: libxml2 does not support HTML5 yet --- src/Utils/Html.vala | 128 ++++++++++++++++++++---------------- src/Widgets/MarkupView.vala | 2 +- tests/Html.test.vala | 8 +-- 3 files changed, 78 insertions(+), 60 deletions(-) diff --git a/src/Utils/Html.vala b/src/Utils/Html.vala index 09dae8a98..ad1e452b0 100644 --- a/src/Utils/Html.vala +++ b/src/Utils/Html.vala @@ -1,73 +1,91 @@ public class Tuba.HtmlUtils { + public static string remove_tags (string content) { + var fixed_paragraphs = content; - public const string FALLBACK_TEXT = "[ There was an error parsing this text ]"; - - private static Regex? _all_tags_regex; - public static Regex? all_tags_regex { - get { - if (_all_tags_regex == null) { - try { - _all_tags_regex = new Regex ("<(.|\n)*?>", GLib.RegexCompileFlags.OPTIMIZE | RegexCompileFlags.CASELESS); - } catch (GLib.RegexError e) { - warning (e.message); - } + var doc = Html.Doc.read_doc (HtmlUtils.replace_with_pango_markup (content), "", "utf8"); + if (doc != null) { + var root = doc->get_root_element (); + if (root != null) { + var t_content = fixed_paragraphs; + remove_tags_handler (root, out t_content); + fixed_paragraphs = t_content; } - return _all_tags_regex; } - } + delete doc; - private static Regex? _html_params_regex; - public static Regex? html_params_regex { - get { - if (_html_params_regex == null) { - try { - _html_params_regex = new Regex ( - "(class|target|rel|data-user|data-tag|translate)=\"(.|\n)*?\"", - GLib.RegexCompileFlags.OPTIMIZE | RegexCompileFlags.CASELESS - ); - } catch (GLib.RegexError e) { - warning (e.message); - } - } - return _html_params_regex; - } + return restore_entities (fixed_paragraphs); } - public static string remove_tags (string content) { - try { - //TODO: remove this when simplify() uses the HTML parsing class - var fixed_paragraphs = simplify (content); + private static void remove_tags_handler (Xml.Node* root, out string content) { + content = ""; + switch (root->name) { + case "br": + content += "\n"; + break; + case "text": + if (root->content != null) + content += GLib.Markup.escape_text (root->content); + break; + default: + for (var iter = root->children; iter != null; iter = iter->next) { + var t_content = ""; + remove_tags_handler (iter, out t_content); + content += t_content; + } + if (root->name == "p") content += "\n"; - return restore_entities (all_tags_regex?.replace (fixed_paragraphs, -1, 0, "") ?? fixed_paragraphs); - } catch (Error e) { - warning (e.message); - return FALLBACK_TEXT; + break; } } - //TODO: Perhaps this should use the HTML parser class - // since we depend on it anyway public static string simplify (string str) { - try { - var divided = str - .replace ("
", "\n") - .replace ("
", "") - .replace ("
", "\n") - .replace ("
", "\n") - .replace ("

", "") - .replace ("

", "\n\n") - .replace ("
", "")
-				.replace ("
", ""); + var simplified = str; - var simplified = html_params_regex?.replace (divided, -1, 0, "") ?? divided; + var doc = Html.Doc.read_doc (str, "", "utf8"); + if (doc != null) { + var root = doc->get_root_element (); + if (root != null) { + var t_content = simplified; + simplify_handler (root, out t_content); + simplified = t_content; + } + } + delete doc; - while (simplified.has_suffix ("\n")) - simplified = simplified.slice (0, simplified.last_index_of ("\n")); + return simplified.strip (); + } + + private static void simplify_handler (Xml.Node* root, out string content) { + content = ""; + switch (root->name) { + case "a": + var href = root->get_prop ("href"); + if (href != null) { + content += @""; + for (var iter = root->children; iter != null; iter = iter->next) { + var t_content = ""; + simplify_handler (iter, out t_content); + content += t_content; + } + content += ""; + } + break; + case "br": + content += "\n"; + break; + case "text": + if (root->content != null) + content += GLib.Markup.escape_text (root->content); + break; + default: + for (var iter = root->children; iter != null; iter = iter->next) { + var t_content = ""; + simplify_handler (iter, out t_content); + content += t_content; + } + if (root->name == "p") content += "\n\n"; - return simplified; - } catch (Error e) { - warning (@"Can't simplify string \"$str\":\n$(e.message)"); - return str; + break; } } diff --git a/src/Widgets/MarkupView.vala b/src/Widgets/MarkupView.vala index 25816af80..f60b2f747 100644 --- a/src/Widgets/MarkupView.vala +++ b/src/Widgets/MarkupView.vala @@ -203,7 +203,7 @@ public class Tuba.Widgets.MarkupView : Gtk.Box { case "a": var href = root->get_prop ("href"); if (href != null) { - v.write_chunk (""); + v.write_chunk (@""); traverse_and_handle (v, root, default_handler); v.write_chunk (""); } diff --git a/tests/Html.test.vala b/tests/Html.test.vala index c6ca4a326..0c60b7adc 100644 --- a/tests/Html.test.vala +++ b/tests/Html.test.vala @@ -15,12 +15,12 @@ const TestContent[] RESTORE_TESTS = { const TestContent[] SIMPLIFY_TESTS = { { - "Tuba\n", - "Tuba" + "Tuba\n", + "Tuba" }, { - "

Everything is going to be
okay

🐱
", - "Everything is going to be\nokay\n\n
🐱
" + "

Everything is going to be
okay

🐱
", + "Everything is going to be\nokay\n\n🐱" } }; From 92478c4208f5f26ceff0ccc72160434284d32428 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sun, 27 Aug 2023 05:32:28 +0300 Subject: [PATCH 57/76] feat(tests)[HtmlUtils]: remove_tags --- tests/Html.test.vala | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/Html.test.vala b/tests/Html.test.vala index 0c60b7adc..f6be5bc02 100644 --- a/tests/Html.test.vala +++ b/tests/Html.test.vala @@ -24,6 +24,21 @@ const TestContent[] SIMPLIFY_TESTS = { } }; +const TestContent[] REMOVE_TAGS_TESTS = { + { + "Tuba\n", + "Tuba\n" + }, + { + "

Everything is going to be
okay

🐱
", + "Everything is going to be\nokay\n🐱" + }, + { + "Document
I am an
example
another

multi nested

end", + "DocumentI am an\nexampleanother multi nested one\nend" + } +}; + public void test_pango () { foreach (var test_pango in PANGO_TESTS) { var res = Tuba.HtmlUtils.replace_with_pango_markup (test_pango.original); @@ -48,11 +63,20 @@ public void test_simplify () { } } +public void test_remove_tags () { + foreach (var test_remove_tag in REMOVE_TAGS_TESTS) { + var res = Tuba.HtmlUtils.remove_tags (test_remove_tag.original); + + assert_cmpstr (res, CompareOperator.EQ, test_remove_tag.sanitized); + } +} + public int main (string[] args) { Test.init (ref args); Test.add_func ("/test_pango", test_pango); Test.add_func ("/test_restore", test_restore); Test.add_func ("/test_simplify", test_simplify); + Test.add_func ("/test_remove_tags", test_remove_tags); return Test.run (); } From d238c77d43c8a37cc96c210ce49e80ef20594eb5 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sun, 27 Aug 2023 05:32:48 +0300 Subject: [PATCH 58/76] chore: remove comment --- src/Widgets/Status.vala | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index 7c7b4c014..bd3000df7 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -21,7 +21,6 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { public API.Account? kind_instigator { get; set; default = null; } private Gtk.Button? quoted_status_btn { get; set; default = null; } - // TODO: remove everything but polls, attachments and content private bool _is_quote = false; public bool is_quote { get { return _is_quote; } From 4a5bf9300be0d7b59be9bc9ce9dcdeed00d95228 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sun, 27 Aug 2023 06:21:59 +0300 Subject: [PATCH 59/76] feat(Attachment.Item): avoid lambda --- src/Widgets/Attachment/Item.vala | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Widgets/Attachment/Item.vala b/src/Widgets/Attachment/Item.vala index 16b8e1904..e0181cd46 100644 --- a/src/Widgets/Attachment/Item.vala +++ b/src/Widgets/Attachment/Item.vala @@ -111,10 +111,7 @@ public class Tuba.Widgets.Attachment.Item : Adw.Bin { css_classes = { "heading", "flat" } }; - alt_btn_clicked_id = alt_btn.clicked.connect (() => { - if (entity != null && entity.description != null) - create_alt_text_window (entity.description, true); - }); + alt_btn_clicked_id = alt_btn.clicked.connect (on_alt_text_btn_clicked); badge_box.append (alt_btn); @@ -131,6 +128,11 @@ public class Tuba.Widgets.Attachment.Item : Adw.Bin { context_menu.unparent (); } + private void on_alt_text_btn_clicked () { + if (entity != null && entity.description != null) + create_alt_text_window (entity.description, true); + } + protected Adw.Window create_alt_text_window (string alt_text, bool show = false) { var alt_label = new Gtk.Label (alt_text) { wrap = true From a3c9618967cabb37e649f76873f8c0b32b41abe0 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sun, 27 Aug 2023 06:23:33 +0300 Subject: [PATCH 60/76] fix(Status): open formal account directly using on_activate_link does an extra request, this should be faster as it avoids resolving the account --- src/Widgets/Status.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Widgets/Status.vala b/src/Widgets/Status.vala index bd3000df7..bd3ed8b8e 100644 --- a/src/Widgets/Status.vala +++ b/src/Widgets/Status.vala @@ -166,7 +166,7 @@ public class Tuba.Widgets.Status : Gtk.ListBoxRow { } private void on_name_button_clicked () { - name_label.on_activate_link (status.formal.account.handle); + status.formal.account.open(); } private bool has_stats { get { return status.formal.reblogs_count != 0 || status.formal.favourites_count != 0; } } From ec4b758e62a61f6a345f312528e0da38fefa0f9d Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sun, 27 Aug 2023 06:24:50 +0300 Subject: [PATCH 61/76] feat(NewAccount): hide next button on auto_auth it's useless then, it's only used on manual auth --- data/ui/dialogs/new_account.ui | 1 + src/Dialogs/NewAccount.vala | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/data/ui/dialogs/new_account.ui b/data/ui/dialogs/new_account.ui index fbc00d8e6..37b3462cf 100644 --- a/data/ui/dialogs/new_account.ui +++ b/data/ui/dialogs/new_account.ui @@ -117,6 +117,7 @@ 1 Next + strict diff --git a/data/ui/widgets/preview_card.ui b/data/ui/widgets/preview_card.ui index 20646eb94..da060e983 100644 --- a/data/ui/widgets/preview_card.ui +++ b/data/ui/widgets/preview_card.ui @@ -39,6 +39,9 @@ 2 1 word-char + diff --git a/data/ui/widgets/status.ui b/data/ui/widgets/status.ui index f787311eb..e006e8ec1 100644 --- a/data/ui/widgets/status.ui +++ b/data/ui/widgets/status.ui @@ -1,7 +1,7 @@ -