Skip to content

Commit f4e340c

Browse files
authored
Add project window icon setting and apply it to runtime + Windows built executable icon (#750)
1 parent b949720 commit f4e340c

14 files changed

Lines changed: 313 additions & 41 deletions

File tree

4.92 KB
Loading

Sources/OvEditor/src/OvEditor/Core/Context.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ OvEditor::Core::Context::Context(const std::filesystem::path& p_projectFolder) :
106106
windowSettings.width = bestFitWindowSizeAndPosition[2];
107107
windowSettings.height = bestFitWindowSizeAndPosition[3];
108108
window = std::make_unique<OvWindowing::Window>(*device, windowSettings);
109-
std::vector<uint64_t> iconRaw = { 0,0,144115188614240000ULL,7500771567664627712ULL,7860776967494637312ULL,0,0,0,0,7212820467466371072ULL,11247766461832697600ULL,14274185407633888512ULL,12905091124788992000ULL,5626708973701824512ULL,514575842263176960,0,0,6564302121125019648ULL,18381468271671515136ULL,18381468271654737920ULL,18237353083595659264ULL,18165295488836311040ULL,6708138037527189504ULL,0,4186681893338480640ULL,7932834557741046016ULL,17876782538917681152ULL,11319824055216379904ULL,15210934132358518784ULL,18381468271520454400ULL,1085667680982603520ULL,0,18093237891929479168ULL,18309410677600032768ULL,11391881649237530624ULL,7932834561381570304ULL,17300321784231761408ULL,15210934132375296000ULL,8293405106311272448ULL,2961143145139082752ULL,16507969723533236736ULL,17516777143216379904ULL,10671305705855129600ULL,7356091234422036224ULL,16580027318695106560ULL,2240567205413984000ULL,18381468271470188544ULL,10959253511276599296ULL,4330520004484136960ULL,10815138323200743424ULL,11607771853338181632ULL,8364614976649238272ULL,17444719546862998784ULL,2669156352,18381468269893064448ULL,6419342512197474304ULL,11103650170688640000ULL,6492244531366860800ULL,14346241902646925312ULL,13841557270159628032ULL,7428148827772098304ULL,3464698581331941120ULL,18381468268953606144ULL,1645680384,18381468271554008832ULL,7140201027266418688ULL,5987558797656659712ULL,17588834734687262208ULL,7284033640602212096ULL,14273902834169157632ULL,18381468269087692288ULL,6852253225049397248ULL,17732667349600245504ULL,16291515470083266560ULL,10022503688432981760ULL,11968059825861367552ULL,9733991836700645376ULL,14850363587428816640ULL,18381468271168132864ULL,16147400282007410688ULL,656430432014827520,18381468270950094848ULL,15715054717226194944ULL,72057596690306560,11823944635485519872ULL,15859169905251653376ULL,17084149004500473856ULL,8581352906816952064ULL,2527949855582584832ULL,18381468271419856896ULL,8581352907253225472ULL,252776704,1376441223417430016ULL,14994761349590357760ULL,10527190521537370112ULL,0,9806614576878321664ULL,18381468271671515136ULL,17156206598538401792ULL,6059619689256392448ULL,10166619973990488064ULL,18381468271403079424ULL,17444719549178451968ULL,420746240,870625192710242304,4906133035823863552ULL,18381468269289150464ULL,18381468271671515136ULL,18381468271671515136ULL,9950729769032620032ULL,14778305994951169792ULL,269422336,0,0,18381468268785833984ULL,8941923452686178304ULL,18381468270950094848ULL,3440842496,1233456333565402880ULL,0,0,0,11823944636091210240ULL,2383877888,16724143605745719296ULL,2316834816,0,0 };
110-
window->SetIconFromMemory(reinterpret_cast<uint8_t*>(iconRaw.data()), 16, 16);
109+
const auto fallbackWindowIconPath = engineAssetsPath / "Textures" / "WindowIcon.png";
110+
window->SetIcon(fallbackWindowIconPath.string());
111111
inputManager = std::make_unique<OvWindowing::Inputs::InputManager>(*window);
112112
window->MakeCurrentContext();
113113
device->SetVsync(true);
@@ -201,6 +201,7 @@ void OvEditor::Core::Context::ResetProjectSettings()
201201
projectSettings.Add<bool>("multisampling", false);
202202
projectSettings.Add<int>("samples", 4);
203203
projectSettings.Add<int>("build_type", 0);
204+
projectSettings.Add<std::string>("window_icon", "");
204205
}
205206

206207
bool OvEditor::Core::Context::IsProjectSettingsIntegrityVerified()
@@ -215,7 +216,8 @@ bool OvEditor::Core::Context::IsProjectSettingsIntegrityVerified()
215216
projectSettings.IsKeyExisting("vsync") &&
216217
projectSettings.IsKeyExisting("multisampling") &&
217218
projectSettings.IsKeyExisting("samples") &&
218-
projectSettings.IsKeyExisting("build_type");
219+
projectSettings.IsKeyExisting("build_type") &&
220+
projectSettings.IsKeyExisting("window_icon");
219221
}
220222

221223
void OvEditor::Core::Context::ApplyProjectSettings()

Sources/OvEditor/src/OvEditor/Core/EditorActions.cpp

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include <OvTools/Utils/PathParser.h>
4444
#include <OvTools/Utils/String.h>
4545
#include <OvTools/Utils/SystemCalls.h>
46+
#include <OvRendering/Data/Image.h>
4647
#include <OvRendering/Resources/Parsers/EmbeddedAssetPath.h>
4748

4849
#include <OvWindowing/Dialogs/OpenFileDialog.h>
@@ -472,6 +473,60 @@ void OvEditor::Core::EditorActions::BuildAtLocation(
472473
{
473474
OVLOG_INFO("Game executable renamed to " + executableName);
474475

476+
#if defined(_WIN32)
477+
if (const std::string windowIconPath = m_context.projectSettings.GetOrDefault("window_icon", std::string{}); !windowIconPath.empty())
478+
{
479+
const std::filesystem::path windowIconRealPath{ GetRealPath(windowIconPath) };
480+
481+
if (!std::filesystem::exists(windowIconRealPath))
482+
{
483+
OVLOG_WARNING(
484+
std::format(
485+
"Window icon \"{}\" was not found. Keeping default executable icon.",
486+
windowIconRealPath.string()
487+
)
488+
);
489+
}
490+
else if (OvRendering::Data::Image iconImage{ windowIconRealPath }; !iconImage)
491+
{
492+
OVLOG_WARNING(
493+
std::format(
494+
"Failed to load window icon \"{}\". Keeping default executable icon.",
495+
windowIconRealPath.string()
496+
)
497+
);
498+
}
499+
else if (iconImage.isHDR)
500+
{
501+
OVLOG_WARNING(
502+
std::format(
503+
"Window icon \"{}\" is HDR and cannot be used for executable icons. Keeping default executable icon.",
504+
windowIconRealPath.string()
505+
)
506+
);
507+
}
508+
else
509+
{
510+
const size_t iconByteCount = static_cast<size_t>(iconImage.width) * static_cast<size_t>(iconImage.height) * 4u;
511+
const auto iconBytes = std::span<const uint8_t>{ static_cast<const uint8_t*>(iconImage.data), iconByteCount };
512+
if (!OvTools::Utils::SystemCalls::SetExecutableIcon(
513+
p_buildPath / executableName,
514+
iconBytes,
515+
static_cast<uint32_t>(iconImage.width),
516+
static_cast<uint32_t>(iconImage.height)
517+
))
518+
{
519+
OVLOG_WARNING(
520+
std::format(
521+
"Failed to apply executable icon from \"{}\". Keeping default executable icon.",
522+
windowIconRealPath.string()
523+
)
524+
);
525+
}
526+
}
527+
}
528+
#endif
529+
475530
if (p_autoRun)
476531
{
477532
const auto exePath = p_buildPath / executableName;
@@ -1085,23 +1140,13 @@ bool OvEditor::Core::EditorActions::ImportAssetAtLocation(const std::string& p_d
10851140
return false;
10861141
}
10871142

1088-
// Duplicate from AResourceManager.h
10891143
std::string OvEditor::Core::EditorActions::GetRealPath(const std::string& p_path)
10901144
{
1091-
std::filesystem::path result;
1092-
1093-
const std::string normalizedPath = OvTools::Utils::PathParser::MakeNonWindowsStyle(p_path);
1094-
1095-
if (normalizedPath.starts_with(':')) // The path is an engine path
1096-
{
1097-
result = m_context.engineAssetsPath / normalizedPath.substr(1);
1098-
}
1099-
else // The path is a project path
1100-
{
1101-
result = m_context.projectAssetsPath / normalizedPath;
1102-
}
1103-
1104-
return result.lexically_normal().string();
1145+
return OvTools::Utils::PathParser::GetRealPath(
1146+
std::filesystem::path{ p_path },
1147+
m_context.engineAssetsPath,
1148+
m_context.projectAssetsPath
1149+
).string();
11051150
}
11061151

11071152
std::string OvEditor::Core::EditorActions::GetResourcePath(const std::string& p_path, bool p_isFromEngine)

Sources/OvEditor/src/OvEditor/Panels/ProjectSettings.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include "OvEditor/Panels/ProjectSettings.h"
88
#include "OvEditor/Core/EditorActions.h"
9+
#include "OvTools/Utils/PathParser.h"
910

1011
#include <OvCore/Resources/Loaders/MaterialLoader.h>
1112
#include <OvCore/Helpers/GUIDrawer.h>
@@ -80,6 +81,7 @@ OvEditor::Panels::ProjectSettings::ProjectSettings(const std::string & p_title,
8081
GUIDrawer::DrawScalar<int>(columns, "Resolution Y", GenerateGatherer<int>("y_resolution"), GenerateProvider<int>("y_resolution"), 1, 0, 10000);
8182
GUIDrawer::DrawBoolean(columns, "Fullscreen", GenerateGatherer<bool>("fullscreen"), GenerateProvider<bool>("fullscreen"));
8283
GUIDrawer::DrawString(columns, "Executable name", GenerateGatherer<std::string>("executable_name"), GenerateProvider<std::string>("executable_name"));
84+
GUIDrawer::DrawAsset(columns, "Window Icon", GenerateGatherer<std::string>("window_icon"), GenerateProvider<std::string>("window_icon"), OvTools::Utils::PathParser::EFileType::TEXTURE);
8385
}
8486

8587
{

Sources/OvGame/src/OvGame/Core/Context.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <OvDebug/Logger.h>
1414

1515
#include <OvGame/Core/Context.h>
16+
#include <OvTools/Utils/PathParser.h>
1617

1718
using namespace OvCore::Global;
1819
using namespace OvCore::ResourceManagement;
@@ -93,8 +94,25 @@ OvGame::Core::Context::Context() :
9394
windowSettings.height = bestFitWindowSizeAndPosition[3];
9495
}
9596
window = std::make_unique<OvWindowing::Window>(*device, windowSettings);
96-
auto iconRaw = std::to_array<uint64_t>({ 0,0,144115188614240000ULL,7500771567664627712ULL,7860776967494637312ULL,0,0,0,0,7212820467466371072ULL,11247766461832697600ULL,14274185407633888512ULL,12905091124788992000ULL,5626708973701824512ULL,514575842263176960,0,0,6564302121125019648ULL,18381468271671515136ULL,18381468271654737920ULL,18237353083595659264ULL,18165295488836311040ULL,6708138037527189504ULL,0,4186681893338480640ULL,7932834557741046016ULL,17876782538917681152ULL,11319824055216379904ULL,15210934132358518784ULL,18381468271520454400ULL,1085667680982603520ULL,0,18093237891929479168ULL,18309410677600032768ULL,11391881649237530624ULL,7932834561381570304ULL,17300321784231761408ULL,15210934132375296000ULL,8293405106311272448ULL,2961143145139082752ULL,16507969723533236736ULL,17516777143216379904ULL,10671305705855129600ULL,7356091234422036224ULL,16580027318695106560ULL,2240567205413984000ULL,18381468271470188544ULL,10959253511276599296ULL,4330520004484136960ULL,10815138323200743424ULL,11607771853338181632ULL,8364614976649238272ULL,17444719546862998784ULL,2669156352,18381468269893064448ULL,6419342512197474304ULL,11103650170688640000ULL,6492244531366860800ULL,14346241902646925312ULL,13841557270159628032ULL,7428148827772098304ULL,3464698581331941120ULL,18381468268953606144ULL,1645680384,18381468271554008832ULL,7140201027266418688ULL,5987558797656659712ULL,17588834734687262208ULL,7284033640602212096ULL,14273902834169157632ULL,18381468269087692288ULL,6852253225049397248ULL,17732667349600245504ULL,16291515470083266560ULL,10022503688432981760ULL,11968059825861367552ULL,9733991836700645376ULL,14850363587428816640ULL,18381468271168132864ULL,16147400282007410688ULL,656430432014827520,18381468270950094848ULL,15715054717226194944ULL,72057596690306560,11823944635485519872ULL,15859169905251653376ULL,17084149004500473856ULL,8581352906816952064ULL,2527949855582584832ULL,18381468271419856896ULL,8581352907253225472ULL,252776704,1376441223417430016ULL,14994761349590357760ULL,10527190521537370112ULL,0,9806614576878321664ULL,18381468271671515136ULL,17156206598538401792ULL,6059619689256392448ULL,10166619973990488064ULL,18381468271403079424ULL,17444719549178451968ULL,420746240,870625192710242304,4906133035823863552ULL,18381468269289150464ULL,18381468271671515136ULL,18381468271671515136ULL,9950729769032620032ULL,14778305994951169792ULL,269422336,0,0,18381468268785833984ULL,8941923452686178304ULL,18381468270950094848ULL,3440842496,1233456333565402880ULL,0,0,0,11823944636091210240ULL,2383877888,16724143605745719296ULL,2316834816,0,0 });
97-
window->SetIconFromMemory(reinterpret_cast<uint8_t*>(iconRaw.data()), 16, 16);
97+
bool useFallbackWindowIcon = true;
98+
if (const std::string windowIconPath = projectSettings.GetOrDefault("window_icon", std::string{}); !windowIconPath.empty())
99+
{
100+
const std::filesystem::path realPath = OvTools::Utils::PathParser::GetRealPath(
101+
std::filesystem::path{ windowIconPath },
102+
engineAssetsPath,
103+
projectAssetsPath
104+
);
105+
106+
useFallbackWindowIcon = !std::filesystem::exists(realPath) || !window->SetIcon(realPath.string());
107+
}
108+
if (useFallbackWindowIcon)
109+
{
110+
const auto fallbackWindowIconPath = engineAssetsPath / "Textures" / "WindowIcon.png";
111+
if (!window->SetIcon(fallbackWindowIconPath.string()))
112+
{
113+
OVLOG_WARNING("Fallback window icon could not be loaded from: " + fallbackWindowIconPath.string());
114+
}
115+
}
98116
inputManager = std::make_unique<OvWindowing::Inputs::InputManager>(*window);
99117
window->MakeCurrentContext();
100118

Sources/OvRendering/include/OvRendering/Data/Image.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ namespace OvRendering::Data
2323
/**
2424
* Creates an image from a file on disk
2525
* @param p_filepath
26+
* @param p_flipVertically
2627
*/
27-
Image(const std::filesystem::path& p_filepath);
28+
Image(const std::filesystem::path& p_filepath, bool p_flipVertically = true);
2829

2930
/**
3031
* Creates an image from encoded image data in memory
32+
* @param p_flipVertically
3133
*/
32-
Image(const uint8_t* p_data, const size_t p_size);
34+
Image(const uint8_t* p_data, const size_t p_size, bool p_flipVertically = true);
3335

3436
/**
3537
* Destructor of the image

Sources/OvRendering/src/OvRendering/Data/Image.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010

1111
#include <stb_image/stb_image.h>
1212

13-
OvRendering::Data::Image::Image(const std::filesystem::path& p_filepath)
13+
OvRendering::Data::Image::Image(const std::filesystem::path& p_filepath, const bool p_flipVertically)
1414
{
15-
stbi_set_flip_vertically_on_load(true);
15+
stbi_set_flip_vertically_on_load(p_flipVertically ? 1 : 0);
1616

1717
isHDR = stbi_is_hdr(p_filepath.string().c_str());
1818

@@ -21,15 +21,15 @@ OvRendering::Data::Image::Image(const std::filesystem::path& p_filepath)
2121
static_cast<void*>(stbi_load(p_filepath.string().c_str(), &width, &height, &bpp, 4));
2222
}
2323

24-
OvRendering::Data::Image::Image(const uint8_t* p_data, const size_t p_size)
24+
OvRendering::Data::Image::Image(const uint8_t* p_data, const size_t p_size, const bool p_flipVertically)
2525
{
2626
if (!p_data || p_size == 0 || p_size > static_cast<size_t>(std::numeric_limits<int>::max()))
2727
{
2828
return;
2929
}
3030

3131
const int encodedSize = static_cast<int>(p_size);
32-
stbi_set_flip_vertically_on_load(true);
32+
stbi_set_flip_vertically_on_load(p_flipVertically ? 1 : 0);
3333
isHDR = stbi_is_hdr_from_memory(p_data, encodedSize) != 0;
3434

3535
data = isHDR ?

Sources/OvTools/include/OvTools/Utils/PathParser.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#pragma once
88

9+
#include <filesystem>
910
#include <string>
1011

1112

@@ -91,5 +92,18 @@ namespace OvTools::Utils
9192
* @param p_path
9293
*/
9394
static EFileType GetFileType(const std::string& p_path);
95+
96+
/**
97+
* Returns the real absolute path of an asset path.
98+
* Engine paths are prefixed with ':'.
99+
* @param p_path
100+
* @param p_enginePath
101+
* @param p_projectPath
102+
*/
103+
static std::filesystem::path GetRealPath(
104+
const std::filesystem::path& p_path,
105+
const std::filesystem::path& p_enginePath,
106+
const std::filesystem::path& p_projectPath
107+
);
94108
};
95109
}

Sources/OvTools/include/OvTools/Utils/SystemCalls.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
#pragma once
88

9+
#include <cstdint>
10+
#include <filesystem>
11+
#include <span>
912
#include <string>
1013

1114

@@ -39,6 +42,21 @@ namespace OvTools::Utils
3942
*/
4043
static void RunProgram(const std::string& p_file, const std::string& p_workingDir = "");
4144

45+
/**
46+
* Apply the given icon image to an executable icon resource.
47+
* @note No-op on Linux, as executable files don't embed icons.
48+
* @param p_executablePath
49+
* @param p_iconData
50+
* @param p_iconWidth
51+
* @param p_iconHeight
52+
*/
53+
static bool SetExecutableIcon(
54+
const std::filesystem::path& p_executablePath,
55+
std::span<const uint8_t> p_iconData,
56+
uint32_t p_iconWidth,
57+
uint32_t p_iconHeight
58+
);
59+
4260
/**
4361
* Open the given file for edition with the default application
4462
* @param p_file
@@ -62,4 +80,4 @@ namespace OvTools::Utils
6280
*/
6381
static bool ExecuteCommand(const std::string_view p_command);
6482
};
65-
}
83+
}

Sources/OvTools/src/OvTools/Utils/PathParser.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,19 @@ OvTools::Utils::PathParser::EFileType OvTools::Utils::PathParser::GetFileType(co
135135

136136
return EFileType::UNKNOWN;
137137
}
138+
139+
std::filesystem::path OvTools::Utils::PathParser::GetRealPath(
140+
const std::filesystem::path& p_path,
141+
const std::filesystem::path& p_enginePath,
142+
const std::filesystem::path& p_projectPath
143+
)
144+
{
145+
const std::string normalizedPath = MakeNonWindowsStyle(p_path.string());
146+
147+
if (!normalizedPath.empty() && normalizedPath[0] == ':')
148+
{
149+
return (p_enginePath / normalizedPath.substr(1)).lexically_normal();
150+
}
151+
152+
return (p_projectPath / normalizedPath).lexically_normal();
153+
}

0 commit comments

Comments
 (0)