From 8d310c2aecf712761bf84c9916a7e576fe0189dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Wed, 5 Mar 2025 16:40:14 -0800 Subject: [PATCH 1/5] Show screenshots in search list --- data/styles/SearchPage.scss | 24 +++++++++---- src/Core/Package.vala | 61 +++++++++++++++++++++++++++++++ src/Views/AppInfoView.vala | 64 +-------------------------------- src/Widgets/SearchListItem.vala | 58 ++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+), 69 deletions(-) diff --git a/data/styles/SearchPage.scss b/data/styles/SearchPage.scss index 1157dd5d5..d4e59e62a 100644 --- a/data/styles/SearchPage.scss +++ b/data/styles/SearchPage.scss @@ -1,8 +1,20 @@ -navigation-view-page.search stack.large row { - border-radius: 0.25em; -} +navigation-view-page.search { + stack.large row { + border-radius: 0.25em; + } + + listview { + border-spacing: 2em; + } + + search-list-item { + border-spacing: 1em 0; + margin: 0.5em 1em; + } -search-list-item { - border-spacing: 1em 0; - margin: 0.5em 1em; + picture { + background: #{'@selected_bg_color'}; + border-radius: 0.5em; + margin-top: 1em; + } } diff --git a/src/Core/Package.vala b/src/Core/Package.vala index 4676c6fe1..2a35693a0 100644 --- a/src/Core/Package.vala +++ b/src/Core/Package.vala @@ -915,6 +915,67 @@ public class AppCenterCore.Package : Object { return null; } + public GenericArray get_screenshots () { + bool has_matching_environment = false; + bool has_matching_style = false; + var desktop_environment = Environment.get_variable ("XDG_SESSION_DESKTOP"); + var prefer_dark_style = Gtk.Settings.get_default ().gtk_application_prefer_dark_theme; + var desktop_style = prefer_dark_style? AppStream.ColorSchemeKind.DARK.to_string () : AppStream.ColorSchemeKind.LIGHT.to_string (); + + component.sort_screenshots (desktop_environment, desktop_style.to_string (), false); + var all_screenshots = component.get_screenshots_all (); + + // This first pass is to gather if we have matching style and matching + // desktop environments, this is useful if we need to fall back to any + // screnshot if none of the conditions are fullfiled + foreach (unowned var screenshot in all_screenshots) { + var environment_id = screenshot.get_environment (); + if (environment_id != null) { + var environment_split = environment_id.split (":", 2); + var screenshot_environment = environment_split[0]; + var screenshot_style = environment_split[1] ?? AppStream.ColorSchemeKind.LIGHT.to_string (); + + if (screenshot_environment == desktop_environment) { + has_matching_environment = true; + } + + if (screenshot_style == desktop_style) { + has_matching_style = true; + } + } + } + + var screenshots = new GenericArray (); + + foreach (unowned var screenshot in all_screenshots) { + var environment_id = screenshot.get_environment (); + if (environment_id == null) { + screenshots.add (screenshot); + break; + } + + var environment_split = environment_id.split (":", 2); + var screenshot_environment = environment_split[0]; + var screenshot_style = environment_split[1] ?? AppStream.ColorSchemeKind.LIGHT.to_string (); + + var same_environment = screenshot_environment == desktop_environment; + var same_style = screenshot_style == desktop_style; + + if (same_environment && same_style) { + screenshots.add (screenshot); + } else if (same_environment && !same_style && !has_matching_style) { + screenshots.add (screenshot); + } else if (!same_environment && same_style && !has_matching_environment) { + screenshots.add (screenshot); + } else if (!has_matching_environment && !has_matching_style) { + screenshots.add (screenshot); + break; + } + } + + return screenshots; + } + public async uint64 get_download_size_including_deps () { uint64 size = 0; try { diff --git a/src/Views/AppInfoView.vala b/src/Views/AppInfoView.vala index f144a1b4e..a46a19568 100644 --- a/src/Views/AppInfoView.vala +++ b/src/Views/AppInfoView.vala @@ -500,70 +500,8 @@ public class AppCenter.Views.AppInfoView : Adw.NavigationPage { } } - bool has_matching_environment = false; - bool has_matching_style = false; - var desktop_environment = Environment.get_variable ("XDG_SESSION_DESKTOP"); - var prefer_dark_style = Gtk.Settings.get_default ().gtk_application_prefer_dark_theme; - var desktop_style = prefer_dark_style - ? AppStream.ColorSchemeKind.DARK.to_string () - : AppStream.ColorSchemeKind.LIGHT.to_string (); - - package_component.sort_screenshots (desktop_environment, - desktop_style.to_string (), - false); - - var all_screenshots = package_component.get_screenshots_all (); - - // This first pass is to gather if we have matching style and matching - // desktop environments, this is useful if we need to fall back to any - // screnshot if none of the conditions are fullfiled - all_screenshots.foreach ((screenshot) => { - var environment_id = screenshot.get_environment (); - if (environment_id != null) { - var environment_split = environment_id.split (":", 2); - var screenshot_environment = environment_split[0]; - var screenshot_style = environment_split[1] - ?? AppStream.ColorSchemeKind.LIGHT.to_string (); - - if (screenshot_environment == desktop_environment) { - has_matching_environment = true; - } - - if (screenshot_style == desktop_style) { - has_matching_style = true; - } - } - }); - - screenshots = new GenericArray (); - - all_screenshots.foreach ((screenshot) => { - var environment_id = screenshot.get_environment (); - if (environment_id == null) { - screenshots.add (screenshot); - return; - } - - var environment_split = environment_id.split (":", 2); - var screenshot_environment = environment_split[0]; - var screenshot_style = environment_split[1] - ?? AppStream.ColorSchemeKind.LIGHT.to_string (); - - var same_environment = screenshot_environment == desktop_environment; - var same_style = screenshot_style == desktop_style; - - if (same_environment && same_style) { - screenshots.add (screenshot); - } else if (same_environment && !same_style && !has_matching_style) { - screenshots.add (screenshot); - } else if (!same_environment && same_style && !has_matching_environment) { - screenshots.add (screenshot); - } else if (!has_matching_environment && !has_matching_style) { - screenshots.add (screenshot); - return; - } - }); + screenshots = package.get_screenshots (); if (screenshots.length > 0) { screenshot_carousel = new Adw.Carousel () { allow_mouse_drag = true, diff --git a/src/Widgets/SearchListItem.vala b/src/Widgets/SearchListItem.vala index e77d41f3a..4c757a844 100644 --- a/src/Widgets/SearchListItem.vala +++ b/src/Widgets/SearchListItem.vala @@ -4,6 +4,8 @@ */ public class AppCenter.SearchListItem : Gtk.Grid { + private const int SCREENSHOT_HEIGHT = 180; + public AppCenterCore.Package package { set { icon_image.gicon = value.get_icon (icon_image.pixel_size, scale_factor); @@ -16,13 +18,22 @@ public class AppCenter.SearchListItem : Gtk.Grid { action_stack = new ActionStack (value); attach (action_stack, 2, 0, 1, 2); + + load_screenshot (value); } } + private static AppCenterCore.ScreenshotCache? screenshot_cache; + private AppCenter.ActionStack action_stack; private Gtk.Image icon_image; private Gtk.Label name_label; private Gtk.Label summary_label; + private Gtk.Picture screenshot_picture; + + static construct { + screenshot_cache = new AppCenterCore.ScreenshotCache (); + } class construct { set_css_name ("search-list-item"); @@ -48,8 +59,55 @@ public class AppCenter.SearchListItem : Gtk.Grid { summary_label.add_css_class (Granite.STYLE_CLASS_DIM_LABEL); summary_label.add_css_class (Granite.STYLE_CLASS_SMALL_LABEL); + screenshot_picture = new Gtk.Picture () { + content_fit = SCALE_DOWN, + halign = START, + height_request = SCREENSHOT_HEIGHT, + vexpand = true, + visible = false + }; + screenshot_picture.add_css_class (Granite.STYLE_CLASS_CARD); + attach (icon_image, 0, 0, 1, 2); attach (name_label, 1, 0); attach (summary_label, 1, 1); + attach (screenshot_picture, 0, 2, 3); + } + + private void load_screenshot (AppCenterCore.Package package) { + screenshot_picture.visible = false; + + string? screenshot_url = null; + + var screenshots = package.get_screenshots (); + foreach (unowned var screenshot in screenshots) { + screenshot_url = screenshot.get_image (-1, SCREENSHOT_HEIGHT, scale_factor).get_url (); + screenshot_picture.alternative_text = screenshot.get_caption (); + + if (screenshot.get_kind () == DEFAULT && screenshot_url != null) { + break; + } + } + + if (screenshot_url == null) { + return; + } + + screenshot_cache.fetch.begin (screenshot_url, (obj, res) => { + string? screenshot_path = null; + var fetched = screenshot_cache.fetch.end (res, out screenshot_path); + + if (!fetched || screenshot_path == null) { + return; + } + + screenshot_picture.file = File.new_for_path (screenshot_path); + + if (!screenshot_picture.file.query_exists ()) { + return; + } + + screenshot_picture.visible = true; + }); } } From 494994d5e0742e5d169d4650df0cce781d79a9ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Wed, 5 Mar 2025 17:15:01 -0800 Subject: [PATCH 2/5] Grid view --- data/styles/SearchPage.scss | 13 +++++++++---- src/Views/SearchView.vala | 7 ++++--- src/Widgets/SearchListItem.vala | 8 ++++++-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/data/styles/SearchPage.scss b/data/styles/SearchPage.scss index d4e59e62a..f452455d9 100644 --- a/data/styles/SearchPage.scss +++ b/data/styles/SearchPage.scss @@ -1,20 +1,25 @@ navigation-view-page.search { - stack.large row { - border-radius: 0.25em; + stack.large child { + border-radius: 1em; + padding-bottom: 0.5em; } - listview { + gridview { border-spacing: 2em; } search-list-item { border-spacing: 1em 0; margin: 0.5em 1em; + + image { + margin: 0 -4px; + } } picture { background: #{'@selected_bg_color'}; border-radius: 0.5em; - margin-top: 1em; + margin-top: calc(1em - 3px); } } diff --git a/src/Views/SearchView.vala b/src/Views/SearchView.vala index 74bcbef90..3806c87c6 100644 --- a/src/Views/SearchView.vala +++ b/src/Views/SearchView.vala @@ -28,7 +28,7 @@ public class AppCenter.SearchView : Adw.NavigationPage { public bool mimetype { get; set; default = false; } private AppCenterCore.SearchEngine search_engine; - private Gtk.ListView list_view; + private Gtk.GridView list_view; private Gtk.NoSelection selection_model; private Gtk.SearchEntry search_entry; private Gtk.Stack stack; @@ -72,10 +72,11 @@ public class AppCenter.SearchView : Adw.NavigationPage { var factory = new Gtk.SignalListItemFactory (); - list_view = new Gtk.ListView (selection_model, factory) { + list_view = new Gtk.GridView (selection_model, factory) { single_click_activate = true, hexpand = true, - vexpand = true + vexpand = true, + max_columns = 2 }; stack = new Gtk.Stack () { diff --git a/src/Widgets/SearchListItem.vala b/src/Widgets/SearchListItem.vala index 4c757a844..9ebe9f7dd 100644 --- a/src/Widgets/SearchListItem.vala +++ b/src/Widgets/SearchListItem.vala @@ -16,7 +16,9 @@ public class AppCenter.SearchListItem : Gtk.Grid { remove (action_stack); } - action_stack = new ActionStack (value); + action_stack = new ActionStack (value) { + halign = END + }; attach (action_stack, 2, 0, 1, 2); load_screenshot (value); @@ -52,6 +54,9 @@ public class AppCenter.SearchListItem : Gtk.Grid { name_label.add_css_class (Granite.STYLE_CLASS_H3_LABEL); summary_label = new Gtk.Label (null) { + ellipsize = END, + hexpand = true, + lines = 2, valign = START, wrap = true, xalign = 0 @@ -61,7 +66,6 @@ public class AppCenter.SearchListItem : Gtk.Grid { screenshot_picture = new Gtk.Picture () { content_fit = SCALE_DOWN, - halign = START, height_request = SCREENSHOT_HEIGHT, vexpand = true, visible = false From 82008139b2cd251fe0e875a173edace65063d747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Wed, 16 Apr 2025 10:57:24 -0700 Subject: [PATCH 3/5] Use screenshot widget --- data/styles/SearchPage.scss | 4 +--- src/Widgets/Screenshot.vala | 6 +++++- src/Widgets/SearchListItem.vala | 21 +++++++-------------- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/data/styles/SearchPage.scss b/data/styles/SearchPage.scss index f452455d9..9c315e323 100644 --- a/data/styles/SearchPage.scss +++ b/data/styles/SearchPage.scss @@ -17,9 +17,7 @@ navigation-view-page.search { } } - picture { - background: #{'@selected_bg_color'}; - border-radius: 0.5em; + screenshot { margin-top: calc(1em - 3px); } } diff --git a/src/Widgets/Screenshot.vala b/src/Widgets/Screenshot.vala index dce55f1fa..ed8f6d16f 100644 --- a/src/Widgets/Screenshot.vala +++ b/src/Widgets/Screenshot.vala @@ -33,13 +33,17 @@ public class AppCenter.Screenshot : Granite.Bin { var box = new Gtk.Box (VERTICAL, 0) { halign = CENTER }; - box.append (label); box.append (picture); child = box; add_css_class (Granite.STYLE_CLASS_CARD); bind_property ("caption", label, "label"); + notify["caption"].connect (() => { + if (caption != "" && label.parent == null) { + box.prepend (label); + } + }); } public void set_branding (AppCenterCore.Package package) { diff --git a/src/Widgets/SearchListItem.vala b/src/Widgets/SearchListItem.vala index 6b7018c51..7b6e16374 100644 --- a/src/Widgets/SearchListItem.vala +++ b/src/Widgets/SearchListItem.vala @@ -31,7 +31,7 @@ public class AppCenter.SearchListItem : Gtk.Grid { private AppCenter.AppIcon app_icon; private Gtk.Label name_label; private Gtk.Label summary_label; - private Gtk.Picture screenshot_picture; + private AppCenter.Screenshot screenshot_picture; static construct { screenshot_cache = new AppCenterCore.ScreenshotCache (); @@ -62,13 +62,9 @@ public class AppCenter.SearchListItem : Gtk.Grid { summary_label.add_css_class (Granite.STYLE_CLASS_DIM_LABEL); summary_label.add_css_class (Granite.STYLE_CLASS_SMALL_LABEL); - screenshot_picture = new Gtk.Picture () { - content_fit = SCALE_DOWN, - height_request = SCREENSHOT_HEIGHT, - vexpand = true, - visible = false + screenshot_picture = new AppCenter.Screenshot () { + height_request = 180, }; - screenshot_picture.add_css_class (Granite.STYLE_CLASS_CARD); attach (app_icon, 0, 0, 1, 2); attach (name_label, 1, 0); @@ -79,12 +75,14 @@ public class AppCenter.SearchListItem : Gtk.Grid { private void load_screenshot (AppCenterCore.Package package) { screenshot_picture.visible = false; + screenshot_picture.set_branding (package); + string? screenshot_url = null; var screenshots = package.get_screenshots (); foreach (unowned var screenshot in screenshots) { screenshot_url = screenshot.get_image (-1, SCREENSHOT_HEIGHT, scale_factor).get_url (); - screenshot_picture.alternative_text = screenshot.get_caption (); + screenshot_picture.tooltip_text = screenshot.get_caption (); if (screenshot.get_kind () == DEFAULT && screenshot_url != null) { break; @@ -103,12 +101,7 @@ public class AppCenter.SearchListItem : Gtk.Grid { return; } - screenshot_picture.file = File.new_for_path (screenshot_path); - - if (!screenshot_picture.file.query_exists ()) { - return; - } - + screenshot_picture.path = screenshot_path; screenshot_picture.visible = true; }); } From 391d474edfeda880fbb0967d89f1ce55178723ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Sat, 30 Aug 2025 10:05:58 -0700 Subject: [PATCH 4/5] Revert using Grid --- data/styles/SearchPage.scss | 6 +----- src/Views/SearchView.vala | 7 +++---- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/data/styles/SearchPage.scss b/data/styles/SearchPage.scss index 9c315e323..d5fc6adf3 100644 --- a/data/styles/SearchPage.scss +++ b/data/styles/SearchPage.scss @@ -1,13 +1,9 @@ navigation-view-page.search { - stack.large child { + stack.large row { border-radius: 1em; padding-bottom: 0.5em; } - gridview { - border-spacing: 2em; - } - search-list-item { border-spacing: 1em 0; margin: 0.5em 1em; diff --git a/src/Views/SearchView.vala b/src/Views/SearchView.vala index 3806c87c6..74bcbef90 100644 --- a/src/Views/SearchView.vala +++ b/src/Views/SearchView.vala @@ -28,7 +28,7 @@ public class AppCenter.SearchView : Adw.NavigationPage { public bool mimetype { get; set; default = false; } private AppCenterCore.SearchEngine search_engine; - private Gtk.GridView list_view; + private Gtk.ListView list_view; private Gtk.NoSelection selection_model; private Gtk.SearchEntry search_entry; private Gtk.Stack stack; @@ -72,11 +72,10 @@ public class AppCenter.SearchView : Adw.NavigationPage { var factory = new Gtk.SignalListItemFactory (); - list_view = new Gtk.GridView (selection_model, factory) { + list_view = new Gtk.ListView (selection_model, factory) { single_click_activate = true, hexpand = true, - vexpand = true, - max_columns = 2 + vexpand = true }; stack = new Gtk.Stack () { From 31d57cf796afadab4d25c8907017149e5963e29d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Sat, 30 Aug 2025 10:07:19 -0700 Subject: [PATCH 5/5] use screenshot height --- src/Widgets/SearchListItem.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Widgets/SearchListItem.vala b/src/Widgets/SearchListItem.vala index 51fd8d6e0..e5854d74f 100644 --- a/src/Widgets/SearchListItem.vala +++ b/src/Widgets/SearchListItem.vala @@ -63,7 +63,7 @@ public class AppCenter.SearchListItem : Gtk.Grid { summary_label.add_css_class (Granite.STYLE_CLASS_SMALL_LABEL); screenshot_picture = new AppCenter.Screenshot () { - height_request = 180, + height_request = SCREENSHOT_HEIGHT }; attach (app_icon, 0, 0, 1, 2);