Add dark icon property

This commit is contained in:
GenevensiS
2025-12-05 13:36:09 +01:00
parent 039a9c5b34
commit 04a3289755
10 changed files with 118 additions and 84 deletions
+1
View File
@@ -52,6 +52,7 @@ IMPLEMENT_REFLECTION(Field) {
REFLECT_LOCALIZED(caption); REFLECT_LOCALIZED(caption);
REFLECT_LOCALIZED(description); // FIXME: This field is both unused and uninitialized. REFLECT_LOCALIZED(description); // FIXME: This field is both unused and uninitialized.
REFLECT_N("icon", icon_filename); REFLECT_N("icon", icon_filename);
REFLECT_N("dark_icon", dark_icon_filename);
REFLECT(editable); REFLECT(editable);
REFLECT(save_value); REFLECT(save_value);
REFLECT(show_statistics); REFLECT(show_statistics);
+1
View File
@@ -48,6 +48,7 @@ public:
LocalizedString caption; ///< Caption for NativeLookEditor LocalizedString caption; ///< Caption for NativeLookEditor
LocalizedString description; ///< Description, used in status bar LocalizedString description; ///< Description, used in status bar
String icon_filename; ///< Filename for an icon (for list of fields) String icon_filename; ///< Filename for an icon (for list of fields)
String dark_icon_filename; ///< Filename for an icon (for list of fields) when a variant for dark mode is necessary
bool editable; ///< Can values of this field be edited? bool editable; ///< Can values of this field be edited?
bool save_value; ///< Should values of this field be written to files? Can be false for script generated fields. bool save_value; ///< Should values of this field be written to files? Can be false for script generated fields.
bool show_statistics; ///< Should this field appear as a group by choice in the statistics panel? bool show_statistics; ///< Should this field appear as a group by choice in the statistics panel?
+11 -4
View File
@@ -37,16 +37,21 @@ IMPLEMENT_REFLECTION(Installer) {
void Installer::validate(Version file_app_version) { void Installer::validate(Version file_app_version) {
Packaged::validate(file_app_version); Packaged::validate(file_app_version);
// load icons where possible // load icons where possible
FOR_EACH(p,packages) { FOR_EACH(p,packages) {
if (!p->icon_url.empty() && !starts_with(p->icon_url,_("http:"))) { String url = p->icon_url;
if (settings.darkMode() && !p->dark_icon_url.empty()) {
url = p->dark_icon_url;
}
if (!url.empty() && !starts_with(url,_("http:"))) {
// TODO: support absolute icon names // TODO: support absolute icon names
try{ try{
String filename = p->name + _("/") + p->icon_url; String filename = p->name + _("/") + url;
auto img_stream = openIn(p->name + _("/") + p->icon_url); auto img_stream = openIn(p->name + _("/") + url);
image_load_file(p->icon, *img_stream); image_load_file(p->icon, *img_stream);
} catch (...) { } catch (...) {
// ignore errors, it's just an image // ignore errors, it's just an image
p->icon_url.clear(); p->icon_url.clear();
p->dark_icon_url.clear();
} }
} }
} }
@@ -202,6 +207,7 @@ PackageDescription::PackageDescription(const Packaged& package)
, short_name(package.short_name) , short_name(package.short_name)
, full_name(package.full_name) , full_name(package.full_name)
, icon_url(package.icon_filename) , icon_url(package.icon_filename)
, dark_icon_url(package.dark_icon_filename)
, installer_group(package.installer_group) , installer_group(package.installer_group)
, position_hint(package.position_hint) , position_hint(package.position_hint)
//, description(package.description) //, description(package.description)
@@ -237,6 +243,7 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(PackageDescription) {
REFLECT(short_name); REFLECT(short_name);
REFLECT(full_name); REFLECT(full_name);
REFLECT(icon_url); REFLECT(icon_url);
REFLECT(dark_icon_url);
REFLECT(installer_group); REFLECT(installer_group);
REFLECT(position_hint); REFLECT(position_hint);
REFLECT(description); REFLECT(description);
+11 -10
View File
@@ -60,16 +60,17 @@ public:
PackageDescription(); PackageDescription();
PackageDescription(const Packaged& package); PackageDescription(const Packaged& package);
String name; ///< Filename of the package String name; ///< Filename of the package
Version version; ///< Version number of this package Version version; ///< Version number of this package
String short_name; ///< Short name of this package String short_name; ///< Short name of this package
String full_name; ///< Name of this package, for menus etc. String full_name; ///< Name of this package, for menus etc.
String icon_url; ///< Filename or URL of icon to use in package lists String icon_url; ///< Filename or URL of icon to use in package lists
Image icon; ///< Icon for the package String dark_icon_url; ///< Filename or URL of icon to use in package lists
String installer_group; ///< Where to put this package in the installer Image icon; ///< Icon for the package
int position_hint; ///< A hint for the package list String installer_group; ///< Where to put this package in the installer
String description; ///< Changelog/description int position_hint; ///< A hint for the package list
vector<PackageDependencyP> dependencies; ///< Dependencies of this package String description; ///< Changelog/description
vector<PackageDependencyP> dependencies; ///< Dependencies of this package
/// Merge two descriptions a package. This package takes precedence /// Merge two descriptions a package. This package takes precedence
/** Usually one of the descriptions will refer to the locally installed one, the other to the new one */ /** Usually one of the descriptions will refer to the locally installed one, the other to the new one */
+28 -24
View File
@@ -16,23 +16,24 @@ extern ScriptValueP script_primary_choice;
// ----------------------------------------------------------------------------- : Statistics dimension // ----------------------------------------------------------------------------- : Statistics dimension
StatsDimension::StatsDimension() StatsDimension::StatsDimension()
: automatic (false) : automatic (false)
, position_hint(0) , position_hint (0)
, numeric (false) , numeric (false)
, bin_size (0) , bin_size (0)
, show_empty (false) , show_empty (false)
, split_list (false) , split_list (false)
{} {}
StatsDimension::StatsDimension(const Field& field) StatsDimension::StatsDimension(const Field& field)
: automatic (true) : automatic (true)
, name (field.name) , name (field.name)
, description (field.description) , description (field.description)
, position_hint(field.position_hint) , position_hint (field.position_hint)
, icon_filename(field.icon_filename) , icon_filename (field.icon_filename)
, numeric (false) , dark_icon_filename (field.dark_icon_filename)
, show_empty (false) , numeric (false)
, split_list (false) , show_empty (false)
, split_list (false)
{ {
// choice field? // choice field?
const ChoiceField* choice_field = dynamic_cast<const ChoiceField*>(&field); const ChoiceField* choice_field = dynamic_cast<const ChoiceField*>(&field);
@@ -67,6 +68,7 @@ IMPLEMENT_REFLECTION_NO_GET_MEMBER(StatsDimension) {
REFLECT_LOCALIZED(description); REFLECT_LOCALIZED(description);
REFLECT(position_hint); REFLECT(position_hint);
REFLECT_N("icon", icon_filename); REFLECT_N("icon", icon_filename);
REFLECT_N("dark_icon", dark_icon_filename);
REFLECT(script); REFLECT(script);
REFLECT(global_script); REFLECT(global_script);
REFLECT(numeric); REFLECT(numeric);
@@ -81,19 +83,20 @@ IMPLEMENT_REFLECTION_NO_GET_MEMBER(StatsDimension) {
// ----------------------------------------------------------------------------- : Statistics category // ----------------------------------------------------------------------------- : Statistics category
StatsCategory::StatsCategory() StatsCategory::StatsCategory()
: automatic(false) : automatic (false)
, position_hint(0) , position_hint (0)
, type(GRAPH_TYPE_BAR) , type (GRAPH_TYPE_BAR)
{} {}
StatsCategory::StatsCategory(const StatsDimensionP& dim) StatsCategory::StatsCategory(const StatsDimensionP& dim)
: automatic(true) : automatic (true)
, name (dim->name) , name (dim->name)
, description (dim->description) , description (dim->description)
, position_hint(dim->position_hint) , position_hint (dim->position_hint)
, icon_filename(dim->icon_filename) , icon_filename (dim->icon_filename)
, dimensions(1, dim) , dark_icon_filename (dim->dark_icon_filename)
, type(GRAPH_TYPE_BAR) , dimensions (1, dim)
, type (GRAPH_TYPE_BAR)
{} {}
IMPLEMENT_REFLECTION_NO_GET_MEMBER(StatsCategory) { IMPLEMENT_REFLECTION_NO_GET_MEMBER(StatsCategory) {
@@ -102,6 +105,7 @@ IMPLEMENT_REFLECTION_NO_GET_MEMBER(StatsCategory) {
REFLECT_LOCALIZED(description); REFLECT_LOCALIZED(description);
REFLECT(position_hint); REFLECT(position_hint);
REFLECT_N("icon", icon_filename); REFLECT_N("icon", icon_filename);
REFLECT_N("dark_icon", dark_icon_filename);
REFLECT(type); REFLECT(type);
REFLECT_N("dimensions", dimension_names); REFLECT_N("dimensions", dimension_names);
} }
+25 -23
View File
@@ -27,20 +27,21 @@ public:
StatsDimension(); StatsDimension();
StatsDimension(const Field&); StatsDimension(const Field&);
const bool automatic; ///< Based on a card field? const bool automatic; ///< Based on a card field?
String name; ///< Name of this dimension String name; ///< Name of this dimension
LocalizedString description; ///< Description, used in status bar LocalizedString description; ///< Description, used in status bar
int position_hint; ///< Hint for the ordering int position_hint; ///< Hint for the ordering
String icon_filename; ///< Icon for lists String icon_filename; ///< Icon for lists
Bitmap icon; ///< The loaded icon (optional of course) String dark_icon_filename; ///< Icon for lists, if a variant for dark mode is needed
OptionalScript script; ///< Script that determines the value(s), ran on each card Bitmap icon; ///< The loaded icon (optional of course)
OptionalScript global_script; ///< Script that determines the value(s), ran only once at the start OptionalScript script; ///< Script that determines the value(s), ran on each card
bool numeric; ///< Are the values numeric? If so, they require special sorting OptionalScript global_script; ///< Script that determines the value(s), ran only once at the start
double bin_size; ///< Bin adjecent numbers? bool numeric; ///< Are the values numeric? If so, they require special sorting
bool show_empty; ///< Should "" be shown? double bin_size; ///< Bin adjecent numbers?
bool split_list; ///< Split values into multiple ones separated by commas bool show_empty; ///< Should "" be shown?
map<String,Color> colors; ///< Colors for the categories bool split_list; ///< Split values into multiple ones separated by commas
vector<String> groups; ///< Order of the items map<String,Color> colors; ///< Colors for the categories
vector<String> groups; ///< Order of the items
DECLARE_REFLECTION(); DECLARE_REFLECTION();
}; };
@@ -54,15 +55,16 @@ public:
StatsCategory(); StatsCategory();
StatsCategory(const StatsDimensionP&); StatsCategory(const StatsDimensionP&);
const bool automatic; ///< Automatically generated? const bool automatic; ///< Automatically generated?
String name; ///< Name/label String name; ///< Name/label
LocalizedString description; ///< Description, used in status bar LocalizedString description; ///< Description, used in status bar
int position_hint; ///< Hint for the ordering int position_hint; ///< Hint for the ordering
String icon_filename; ///< Icon for lists String icon_filename; ///< Icon for lists
Bitmap icon; ///< The loaded icon (optional of course) String dark_icon_filename; ///< Icon for lists when a variant for dark mode is needed
vector<String> dimension_names; ///< Names of the dimensions to use Bitmap icon; ///< The loaded icon (optional of course)
vector<StatsDimensionP> dimensions; ///< Actual dimensions vector<String> dimension_names; ///< Names of the dimensions to use
GraphType type; ///< Type of graph to use vector<StatsDimensionP> dimensions; ///< Actual dimensions
GraphType type; ///< Type of graph to use
/// Initialize dimensions from dimension_names /// Initialize dimensions from dimension_names
void find_dimensions(const vector<StatsDimensionP>& available); void find_dimensions(const vector<StatsDimensionP>& available);
+5 -4
View File
@@ -159,13 +159,13 @@ public:
PackageIconRequest(PackageUpdateList* list, PackageUpdateList::TreeItem* ti) PackageIconRequest(PackageUpdateList* list, PackageUpdateList::TreeItem* ti)
: ThumbnailRequest( : ThumbnailRequest(
list, list,
_("package_") + ti->package->description->icon_url + _("_") + ti->package->description->version.toString(), _("package_") + (settings.darkMode() && !ti->package->description->dark_icon_url.empty() ? ti->package->description->dark_icon_url : ti->package->description->icon_url) + _("_") + ti->package->description->version.toString(),
wxDateTime(1,wxDateTime::Jan,2000)) wxDateTime(1,wxDateTime::Jan,2000))
, list(list), ti(ti) , list(list), ti(ti)
{} {}
Image generate() override { Image generate() override {
wxURL url(ti->package->description->icon_url); wxURL url(settings.darkMode() && !ti->package->description->dark_icon_url.empty() ? ti->package->description->dark_icon_url : ti->package->description->icon_url);
unique_ptr<wxInputStream> isP(url.GetInputStream()); unique_ptr<wxInputStream> isP(url.GetInputStream());
if (!isP) return wxImage(); if (!isP) return wxImage();
SeekAtStartInputStream is2(*isP); SeekAtStartInputStream is2(*isP);
@@ -219,8 +219,9 @@ void PackageUpdateList::initItems() {
if (p && p->description->icon.Ok()) { // it has an icon if (p && p->description->icon.Ok()) { // it has an icon
ti.setIcon(p->description->icon); ti.setIcon(p->description->icon);
} else if (p) { // it doesn't have an icon (yet) } else if (p) { // it doesn't have an icon (yet)
ti.setIcon(load_resource_image(_("installer_package"))); ti.setIcon(load_resource_image(_("installer_package")));
if (!p->description->icon_url.empty()) { String icon_url = settings.darkMode() && !p->description->dark_icon_url.empty() ? p->description->dark_icon_url : p->description->icon_url;
if (!icon_url.empty()) {
// download icon // download icon
thumbnail_thread.request(make_intrusive<PackageIconRequest>(this,&ti)); thumbnail_thread.request(make_intrusive<PackageIconRequest>(this,&ti));
} }
+13 -7
View File
@@ -228,13 +228,19 @@ void StatDimensionList::drawItem(DC& dc, int x, int y, size_t item) {
} }
StatsDimension& dim = *dimensions.at(item - show_empty); StatsDimension& dim = *dimensions.at(item - show_empty);
// draw icon // draw icon
if (!dim.icon_filename.empty() && !dim.icon.Ok()) { if(!dim.icon.Ok()) {
auto file = game->openIn(dim.icon_filename); String filename = dim.icon_filename;
Image img(*file); if (settings.darkMode() && !dim.dark_icon_filename.empty()) {
if (img.HasMask()) img.InitAlpha(); // we can't handle masks filename = dim.dark_icon_filename;
Image resampled(21, 21); }
resample_preserve_aspect(img, resampled); if (!filename.empty()) {
if (img.Ok()) dim.icon = Bitmap(resampled); auto file = game->openIn(filename);
Image img(*file);
if (img.HasMask()) img.InitAlpha(); // we can't handle masks
Image resampled(21, 21);
resample_preserve_aspect(img, resampled);
if (img.Ok()) dim.icon = Bitmap(resampled);
}
} }
if (dim.icon.Ok()) { if (dim.icon.Ok()) {
dc.DrawBitmap(dim.icon, x+1, y+1); dc.DrawBitmap(dim.icon, x+1, y+1);
+13 -3
View File
@@ -572,6 +572,7 @@ IMPLEMENT_REFLECTION(Packaged) {
REFLECT(full_name); REFLECT(full_name);
REFLECT(folder_name); REFLECT(folder_name);
REFLECT_N("icon", icon_filename); REFLECT_N("icon", icon_filename);
REFLECT_N("dark_icon", dark_icon_filename);
REFLECT_NO_SCRIPT(position_hint); REFLECT_NO_SCRIPT(position_hint);
REFLECT(installer_group); REFLECT(installer_group);
REFLECT(version); REFLECT(version);
@@ -584,9 +585,18 @@ Packaged::Packaged()
, fully_loaded(true) , fully_loaded(true)
{} {}
unique_ptr<wxInputStream> Packaged::openIconFile() { unique_ptr<wxInputStream> Packaged::openIconFile() {
if (!icon_filename.empty()) { String filename = icon_filename;
return openIn(icon_filename); if (!dark_icon_filename.empty()) {
if (settings.darkMode()) {
wxFileName fn (dark_icon_filename);
String extension = fn.GetExt();
filename = dark_icon_filename.Replace(extension, _("")) + "_dark" + extension;
}
else filename = dark_icon_filename;
}
if (!filename.empty()) {
return openIn(filename);
} else { } else {
return unique_ptr<wxInputStream>(); return unique_ptr<wxInputStream>();
} }
+10 -9
View File
@@ -305,15 +305,16 @@ public:
Packaged(); Packaged();
virtual ~Packaged() {} virtual ~Packaged() {}
Version version; ///< Version number of this package Version version; ///< Version number of this package
Version compatible_version; ///< Earliest version number this package is compatible with Version compatible_version; ///< Earliest version number this package is compatible with
String installer_group; ///< Group to place this package in in the installer String installer_group; ///< Group to place this package in in the installer
String short_name; ///< Short name of this package String short_name; ///< Short name of this package
String full_name; ///< Name of this package, for menus etc. String full_name; ///< Name of this package, for menus etc.
String folder_name; ///< Name of the folder this package is loaded from. String folder_name; ///< Name of the folder this package is loaded from.
String icon_filename; ///< Filename of icon to use in package lists String icon_filename; ///< Filename of icon to use in package lists
vector<PackageDependencyP> dependencies; ///< Dependencies of this package String dark_icon_filename; ///< Filename of icon to use in package lists, when a variant for dark mode is needed
int position_hint; ///< A hint for the package list vector<PackageDependencyP> dependencies; ///< Dependencies of this package
int position_hint; ///< A hint for the package list
/// Get an input stream for the package icon, if there is any /// Get an input stream for the package icon, if there is any
unique_ptr<wxInputStream> openIconFile(); unique_ptr<wxInputStream> openIconFile();