Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dinamically load icons if cache is being rebuilt (issue #2209) #2238

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
dce9a58
Only add banner and recently updated apps when the cache is fully loa…
Dec 31, 2024
efcfb0a
Ensure packge get_icon method gets a proper icon and does ot end up w…
Dec 31, 2024
3b2e072
Load with FileIcon instead of BytesIcon (should be ok since we now ve…
Dec 31, 2024
e912881
Added css to latest_apps_message to have a slightly bigger font
Dec 31, 2024
aaeab44
Instead of message dialogue in banner. Have a spinner in each unloade…
Jan 1, 2025
93e5cf9
Make some flatpak backends unowned
Jan 1, 2025
4564a8e
Eliminate a bunch of critical assertions and unused variables.
Jan 1, 2025
3372135
Better warnign message for remote icon url fetch
Jan 1, 2025
a69dccb
Remove unused CSS
Jan 1, 2025
ac670cb
Restoring some unused code back to original state
Jan 1, 2025
dd81748
Use a better name for generic icon in package
Jan 1, 2025
4c0048a
Fix for a crash when using FileIcon to load certain remote icons. Byt…
Jan 1, 2025
aafd3ad
Remove remote icons load as it makes get_icon a
Jan 1, 2025
687cea9
Fixing bad linter errros
Jan 2, 2025
17f4940
Implemented a dimmed icon update via Gtk.Stack instead of Gtk.Spinner
Jan 3, 2025
7b27f52
Changed named Gtk.Stack named icons to ref widget icons for performan…
Jan 4, 2025
7ab887f
Removed unnecesary get_app_scale_factor method
Feb 21, 2025
978a71e
Removed unnecesary changes related to PR 2209
Feb 21, 2025
bb0916b
Merge branch 'main' into italocapasso/issue-2209
Feb 21, 2025
e5510d0
Merge branch 'main' into italocapasso/issue-2209
edwood-grant Feb 21, 2025
c3aedeb
Fixing linting errors
Feb 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions data/styles/HomePage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,7 @@ homepage {
}
}
}

.icon-dim {
filter: blur(0.75px) saturate(10%);
}
2 changes: 1 addition & 1 deletion src/Application.vala
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public class AppCenter.App : Gtk.Application {
activate ();
});

var flatpak_backend = AppCenterCore.FlatpakBackend.get_default ();
unowned var flatpak_backend = AppCenterCore.FlatpakBackend.get_default ();
flatpak_backend.operation_finished.connect (on_operation_finished);

var update_manager = AppCenterCore.UpdateManager.get_default ();
Expand Down
3 changes: 3 additions & 0 deletions src/Core/FlatpakBackend.vala
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public class AppCenterCore.FlatpakPackage : Package {
public class AppCenterCore.FlatpakBackend : Object {
public signal void operation_finished (Package package, Package.State operation, Error? error);
public signal void cache_flush_needed ();
public signal void on_metadata_remote_preprocessed (string remote_title);

// Based on https://github.com/flatpak/flatpak/blob/417e3949c0ecc314e69311e3ee8248320d3e3d52/common/flatpak-run-private.h
private const string FLATPAK_METADATA_GROUP_APPLICATION = "Application";
Expand Down Expand Up @@ -1135,6 +1136,8 @@ public class AppCenterCore.FlatpakBackend : Object {
} else {
continue;
}

on_metadata_remote_preprocessed (remote.get_title ());
}
}

Expand Down
141 changes: 83 additions & 58 deletions src/Core/Package.vala
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ public class AppCenterCore.Package : Object {
public ChangeInformation change_information { public get; private set; }
public GLib.Cancellable action_cancellable { public get; private set; }
public State state { public get; private set; default = State.NOT_INSTALLED; }
public bool uses_generic_icon { public get; private set; }
public bool icon_available { public get; private set; }

public double progress {
get {
Expand Down Expand Up @@ -649,79 +651,102 @@ public class AppCenterCore.Package : Object {
}

public GLib.Icon get_icon (uint size, uint scale_factor) {
GLib.Icon? icon = null;
uint current_size = 0;
uint current_scale = 0;
uint pixel_size = size * scale_factor;

unowned var icons = component.get_icons ();
foreach (unowned var _icon in icons) {
switch (_icon.get_kind ()) {
uint cached_current_size = 0;
uint cached_current_scale = 0;

AppStream.Icon? stock_icon = null;
AppStream.Icon? cached_icon = null;
AppStream.Icon? local_icon = null;

uses_generic_icon = false;
icon_available = true;
unowned var all_icons = component.get_icons ();
foreach (var icon in all_icons) {
switch (icon.get_kind ()) {
case AppStream.IconKind.STOCK:
unowned string icon_name = _icon.get_name ();
if (Gtk.IconTheme.get_for_display (Gdk.Display.get_default ()).has_icon (icon_name)) {
return new ThemedIcon (icon_name);
if (Gtk.IconTheme.get_for_display (Gdk.Display.get_default ())
.has_icon (icon.get_name ())) {
stock_icon = icon;
}

break;
case AppStream.IconKind.CACHED:
case AppStream.IconKind.LOCAL:
var icon_scale = _icon.get_scale ();
var icon_width = _icon.get_width () * icon_scale;
bool is_bigger = (icon_width > current_size && current_size < pixel_size);
bool has_better_dpi = (icon_width == current_size && current_scale < icon_scale && scale_factor <= icon_scale);
if (is_bigger || has_better_dpi) {
var file = File.new_for_path (_icon.get_filename ());
icon = new FileIcon (file);
current_size = icon_width;
current_scale = icon_scale;
}

local_icon = icon;
break;
case AppStream.IconKind.CACHED:
var icon_scale = icon.get_scale ();
var icon_width = icon.get_width () * icon_scale;

case AppStream.IconKind.UNKNOWN:
warning ("'%s' is an unknown kind of AppStream icon", _icon.get_name ());
break;
bool is_bigger = icon_width > cached_current_size
&& cached_current_size < pixel_size;
bool has_better_dpi = icon_width == cached_current_size
&& cached_current_scale < icon_scale
&& scale_factor <= icon_scale;

case AppStream.IconKind.REMOTE:
warning ("'%s' is a remote AppStream icon", _icon.get_name ());
break;
}
}

if (icon == null) {
switch (component.get_kind ()) {
case AppStream.ComponentKind.ADDON:
icon = new ThemedIcon ("extension");
break;
case AppStream.ComponentKind.FONT:
icon = new ThemedIcon ("font-x-generic");
if (is_bigger || has_better_dpi) {
cached_icon = icon;
cached_current_size = icon_width;
cached_current_scale = icon_scale;
}
break;
case AppStream.ComponentKind.ICON_THEME:
icon = new ThemedIcon ("preferences-desktop-theme");
case AppStream.IconKind.REMOTE:
debug ("'%s' is an unknown kind of AppStream icon", icon.get_name ());
// We are not loading remote icons for now due to blocking events downloading the file
// TODO: Dynamically load remote icons without blocking get_icon method
break;
case AppStream.ComponentKind.CODEC:
case AppStream.ComponentKind.CONSOLE_APP:
case AppStream.ComponentKind.DESKTOP_APP:
case AppStream.ComponentKind.DRIVER:
case AppStream.ComponentKind.FIRMWARE:
case AppStream.ComponentKind.GENERIC:

case AppStream.ComponentKind.INPUT_METHOD: //ComponentKind.INPUTMETHOD is deprecated has same value so cannot be included
case AppStream.ComponentKind.LOCALIZATION:
case AppStream.ComponentKind.OPERATING_SYSTEM:
case AppStream.ComponentKind.REPOSITORY:
case AppStream.ComponentKind.RUNTIME:
case AppStream.ComponentKind.SERVICE:
case AppStream.ComponentKind.UNKNOWN:
case AppStream.ComponentKind.WEB_APP:
debug ("component kind not handled %s", component.get_kind ().to_string ());
icon = new ThemedIcon ("application-default-icon");
case AppStream.IconKind.UNKNOWN:
debug ("'%s' is an unknown kind of AppStream icon", icon.get_name ());
break;
}
}

return icon;
// Respecting the recommended order by the AppStream API
// https://www.freedesktop.org/software/appstream/docs/chap-CatalogData.html#tag-ct-icon
// STOCK -> CACHED -> LOCAL -> REMOTE
GLib.File? file = null;
if (stock_icon != null) {
return new ThemedIcon (stock_icon.get_name ());
} else if (cached_icon != null) {
file = File.new_for_path (cached_icon.get_filename ());
} else if (local_icon != null) {
file = File.new_for_path (local_icon.get_filename ());
} else {
// Either a remote or unkonw icon type
icon_available = false;
}

if (file != null && file.query_exists ()) {
return new FileIcon (file);
}

uses_generic_icon = true;
switch (component.get_kind ()) {
case AppStream.ComponentKind.ADDON:
return new ThemedIcon ("extension");
case AppStream.ComponentKind.FONT:
return new ThemedIcon ("font-x-generic");
case AppStream.ComponentKind.ICON_THEME:
return new ThemedIcon ("preferences-desktop-theme");
case AppStream.ComponentKind.CODEC:
case AppStream.ComponentKind.CONSOLE_APP:
case AppStream.ComponentKind.DESKTOP_APP:
case AppStream.ComponentKind.DRIVER:
case AppStream.ComponentKind.FIRMWARE:
case AppStream.ComponentKind.GENERIC:

case AppStream.ComponentKind.INPUT_METHOD: //ComponentKind.INPUTMETHOD is deprecated has same value so cannot be included
case AppStream.ComponentKind.LOCALIZATION:
case AppStream.ComponentKind.OPERATING_SYSTEM:
case AppStream.ComponentKind.REPOSITORY:
case AppStream.ComponentKind.RUNTIME:
case AppStream.ComponentKind.SERVICE:
case AppStream.ComponentKind.UNKNOWN:
case AppStream.ComponentKind.WEB_APP:
default:
debug ("Component kind not handled %s", component.get_kind ().to_string ());
return new ThemedIcon ("application-default-icon");
}
}

public Package? get_plugin_host_package () {
Expand Down
2 changes: 1 addition & 1 deletion src/Services/SearchProvider.vala
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class SearchProvider : Object {
public HashTable<string, Variant>[] get_result_metas (string[] results) throws GLib.Error {
var result = new GenericArray<HashTable<string, Variant>> ();

var flatpak_backend = AppCenterCore.FlatpakBackend.get_default ();
unowned var flatpak_backend = AppCenterCore.FlatpakBackend.get_default ();
foreach (var str in results) {
var package = flatpak_backend.get_package_for_component_id (str);
if (package != null) {
Expand Down
24 changes: 22 additions & 2 deletions src/Views/AppInfoView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ public class AppCenter.Views.AppInfoView : Adw.NavigationPage {
}

construct {
AppCenterCore.FlatpakBackend.get_default ().cache_flush_needed.connect (() => {
unowned var backend = AppCenterCore.FlatpakBackend.get_default ();
backend.cache_flush_needed.connect (() => {
to_recycle = true;
});

Expand Down Expand Up @@ -154,15 +155,24 @@ public class AppCenter.Views.AppInfoView : Adw.NavigationPage {
var app_icon = new Gtk.Image () {
pixel_size = 128
};
var app_icon_updated = new Gtk.Image () {
pixel_size = 128
};

var badge_image = new Gtk.Image () {
halign = Gtk.Align.END,
valign = Gtk.Align.END,
pixel_size = 64
};

var app_icon_stack = new Gtk.Stack () {
transition_type = Gtk.StackTransitionType.CROSSFADE
};
app_icon_stack.add_child (app_icon);
app_icon_stack.add_child (app_icon_updated);

var app_icon_overlay = new Gtk.Overlay () {
child = app_icon,
child = app_icon_stack,
valign = Gtk.Align.START
};

Expand All @@ -183,6 +193,16 @@ public class AppCenter.Views.AppInfoView : Adw.NavigationPage {
}
}

if (package.uses_generic_icon && package.icon_available) {
app_icon.add_css_class ("icon-dim");
backend.on_metadata_remote_preprocessed.connect ((remote_title) => {
if (package.origin_description == remote_title) {
app_icon_updated.set_from_gicon (package.get_icon (128, scale_factor));
app_icon_stack.visible_child = app_icon_updated;
}
});
}

var app_title = new Gtk.Label (package.get_name ()) {
selectable = true,
wrap = true,
Expand Down
52 changes: 32 additions & 20 deletions src/Views/Homepage.vala
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public class AppCenter.Homepage : Adw.NavigationPage {
private Gtk.Revealer recently_updated_revealer;
private Widgets.Banner appcenter_banner;

private Gtk.EventControllerMotion banner_motion_controller;

private Gtk.Label updates_badge;
private Gtk.Revealer updates_badge_revealer;

Expand All @@ -47,7 +49,7 @@ public class AppCenter.Homepage : Adw.NavigationPage {
hexpand = true;
vexpand = true;

var banner_motion_controller = new Gtk.EventControllerMotion ();
banner_motion_controller = new Gtk.EventControllerMotion ();

banner_carousel = new Adw.Carousel () {
allow_long_swipes = true
Expand Down Expand Up @@ -78,7 +80,8 @@ public class AppCenter.Homepage : Adw.NavigationPage {
recently_updated_grid.attach (recently_updated_carousel, 0, 1);

recently_updated_revealer = new Gtk.Revealer () {
child = recently_updated_grid
child = recently_updated_grid,
reveal_child = false
};

var categories_label = new Granite.HeaderLabel (_("Categories")) {
Expand Down Expand Up @@ -249,7 +252,6 @@ public class AppCenter.Homepage : Adw.NavigationPage {
var headerbar = new Gtk.HeaderBar () {
show_title_buttons = true
};

if (!Utils.is_running_in_guest_session ()) {
headerbar.pack_end (updates_overlay);
}
Expand Down Expand Up @@ -281,16 +283,9 @@ public class AppCenter.Homepage : Adw.NavigationPage {
"#7239b3"
);
banner_carousel.append (appcenter_banner);

banner_carousel.page_changed.connect (page_changed_handler);
}

load_banners_and_carousels.begin ((obj, res) => {
load_banners_and_carousels.end (res);
banner_timeout_start ();
banner_motion_controller.enter.connect (banner_timeout_stop);
banner_motion_controller.leave.connect (banner_timeout_start);
});
load_banners_and_carousels ();

category_flow.child_activated.connect ((child) => {
var card = (AbstractCategoryCard) child;
Expand All @@ -313,14 +308,10 @@ public class AppCenter.Homepage : Adw.NavigationPage {
});
}

private void page_changed_handler () {
banner_carousel.remove (appcenter_banner);
banner_carousel.page_changed.disconnect (page_changed_handler);
}
private void load_banners_and_carousels () {
unowned var backend = AppCenterCore.FlatpakBackend.get_default ();

private async void load_banners_and_carousels () {
unowned var fp_client = AppCenterCore.FlatpakBackend.get_default ();
var packages_by_release_date = fp_client.get_featured_packages_by_release_date ();
var packages_by_release_date = backend.get_featured_packages_by_release_date ();
var packages_in_banner = new Gee.LinkedList<AppCenterCore.Package> ();

foreach (var package in packages_by_release_date) {
Expand All @@ -331,7 +322,7 @@ public class AppCenter.Homepage : Adw.NavigationPage {
var installed = false;
foreach (var origin_package in package.origin_packages) {
try {
if (AppCenterCore.FlatpakBackend.get_default ().is_package_installed (origin_package)) {
if (backend.is_package_installed (origin_package)) {
installed = true;
break;
}
Expand All @@ -348,6 +339,14 @@ public class AppCenter.Homepage : Adw.NavigationPage {
show_package (package);
});

if (package.uses_generic_icon && package.icon_available) {
backend.on_metadata_remote_preprocessed.connect ((remote_title) => {
if (remote_title == package.origin_description) {
banner.update_icon (package.get_icon (128, this.scale_factor));
}
});
}

banner_carousel.append (banner);
}
}
Expand All @@ -366,7 +365,7 @@ public class AppCenter.Homepage : Adw.NavigationPage {
var installed = false;
foreach (var origin_package in package.origin_packages) {
try {
if (AppCenterCore.FlatpakBackend.get_default ().is_package_installed (origin_package)) {
if (backend.is_package_installed (origin_package)) {
installed = true;
break;
}
Expand All @@ -377,11 +376,24 @@ public class AppCenter.Homepage : Adw.NavigationPage {

if (!installed) {
var package_row = new AppCenter.Widgets.ListPackageRowGrid (package);
if (package.uses_generic_icon && package.icon_available) {
backend.on_metadata_remote_preprocessed.connect ((remote_title) => {
if (remote_title == package.origin_description) {
package_row.update_icon (package.get_icon (128, this.scale_factor));
}
});
}
recently_updated_carousel.append (package_row);
}
}

banner_carousel.remove (appcenter_banner);
banner_carousel.scroll_to (banner_carousel.get_nth_page (0), false);
recently_updated_revealer.reveal_child = recently_updated_carousel.get_first_child () != null;

banner_timeout_start ();
banner_motion_controller.enter.connect (banner_timeout_stop);
banner_motion_controller.leave.connect (banner_timeout_start);
}

private void banner_timeout_start () {
Expand Down
Loading