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 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -1112,6 +1113,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
Loading
Loading