pull/130/head
Peter Repukat 3 years ago
parent b0e2aa4dc1
commit 3f250bdc22

@ -0,0 +1,7 @@
UseTab: false
IndentWidth: 4
BreakBeforeBraces: "Stroustrup"
AllowShortIfStatementsOnASingleLine: false
IndentCaseLabels: false
ColumnLimit: 0
PointerAlignment: "Left"

@ -123,8 +123,10 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="main.cpp" /> <ClCompile Include="main.cpp" />
<ClCompile Include="UIModel.cpp" /> <ClCompile Include="UIModel.cpp" />
<None Include=".clang-format" />
<None Include="qml\AddSelectTypeDialog.qml" /> <None Include="qml\AddSelectTypeDialog.qml" />
<None Include="qml\FluentTextInput.qml" /> <None Include="qml\FluentTextInput.qml" />
<None Include="qml\InfoDialog.qml" />
<None Include="qml\RPane.qml" /> <None Include="qml\RPane.qml" />
<None Include="qml\ShortcutCards.qml" /> <None Include="qml\ShortcutCards.qml" />
<None Include="qml\ShortcutProps.qml" /> <None Include="qml\ShortcutProps.qml" />
@ -136,6 +138,7 @@
<QtMoc Include="UIModel.h" /> <QtMoc Include="UIModel.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="UWPFetch.h" />
<ClInclude Include="VDFParser.h" /> <ClInclude Include="VDFParser.h" />
<ClInclude Include="WinEventFilter.h" /> <ClInclude Include="WinEventFilter.h" />
</ItemGroup> </ItemGroup>

@ -51,12 +51,16 @@
<None Include="qml\ShortcutProps.qml"> <None Include="qml\ShortcutProps.qml">
<Filter>qml</Filter> <Filter>qml</Filter>
</None> </None>
<None Include="qml\FluentTextInput.qml">
<Filter>Source Files</Filter>
</None>
<None Include="qml\UWPSelectDialog.qml"> <None Include="qml\UWPSelectDialog.qml">
<Filter>qml</Filter> <Filter>qml</Filter>
</None> </None>
<None Include="qml\InfoDialog.qml">
<Filter>qml</Filter>
</None>
<None Include="qml\FluentTextInput.qml">
<Filter>qml</Filter>
</None>
<None Include=".clang-format" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtMoc Include="UIModel.h"> <QtMoc Include="UIModel.h">
@ -70,6 +74,9 @@
<ClInclude Include="VDFParser.h"> <ClInclude Include="VDFParser.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="UWPFetch.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Xml Include="manifest.xml"> <Xml Include="manifest.xml">

@ -16,27 +16,13 @@ limitations under the License.
#include "UIModel.h" #include "UIModel.h"
#include <QDir> #include <QDir>
#include <QJsonDocument>
#ifdef _WIN32
#include <Windows.h>
#include <VersionHelpers.h>
#include <collection.h>
#include <appmodel.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <windows.h>
#include <appxpackaging.h>
#pragma comment(lib, "Shlwapi.lib")
using namespace Windows::Management::Deployment;
using namespace Windows::Foundation::Collections;
#include <QGuiApplication> #include <QGuiApplication>
#include <QJsonDocument>
#ifdef _WIN32
#include <WinReg/WinReg.hpp> #include <WinReg/WinReg.hpp>
#endif
#ifdef _WIN32
#include "UWPFetch.h"
#endif #endif
UIModel::UIModel() : QObject(nullptr) UIModel::UIModel() : QObject(nullptr)
@ -59,7 +45,6 @@ UIModel::UIModel() : QObject(nullptr)
parseShortcutVDF(); parseShortcutVDF();
readConfigs(); readConfigs();
} }
void UIModel::readConfigs() void UIModel::readConfigs()
@ -70,14 +55,12 @@ void UIModel::readConfigs()
return !entry.endsWith(".json"); return !entry.endsWith(".json");
}); });
std::for_each(entries.begin(), entries.end(), [this](const auto& name) std::for_each(entries.begin(), entries.end(), [this](const auto& name) {
{
auto path = config_path_; auto path = config_path_;
path /= config_dir_name_.toStdString(); path /= config_dir_name_.toStdString();
path /= name.toStdString(); path /= name.toStdString();
QFile file(path); QFile file(path);
if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) {
{
// meh // meh
return; return;
} }
@ -103,7 +86,6 @@ void UIModel::readConfigs()
targets_.append(json.toVariantMap()); targets_.append(json.toVariantMap());
}); });
emit targetListChanged(); emit targetListChanged();
} }
@ -152,12 +134,9 @@ void UIModel::deleteTarget(int index)
bool UIModel::isInSteam(QVariant shortcut) bool UIModel::isInSteam(QVariant shortcut)
{ {
const auto map = shortcut.toMap(); const auto map = shortcut.toMap();
for (auto& steam_shortcut : shortcuts_vdf_.shortcuts) for (auto& steam_shortcut : shortcuts_vdf_.shortcuts) {
{ if (map["name"].toString() == QString::fromStdString(steam_shortcut.appName.value)) {
if (map["name"].toString() == QString::fromStdString(steam_shortcut.appName.value)) if (QString::fromStdString(steam_shortcut.exe.value).toLower().contains("glossitarget.exe")) {
{
if (QString::fromStdString(steam_shortcut.exe.value).toLower().contains("glossitarget.exe"))
{
return true; return true;
} }
} }
@ -178,11 +157,9 @@ bool UIModel::addToSteam(QVariant shortcut)
vdfshortcut.idx = shortcuts_vdf_.shortcuts.size(); vdfshortcut.idx = shortcuts_vdf_.shortcuts.size();
vdfshortcut.appName.value = name.toStdString(); vdfshortcut.appName.value = name.toStdString();
vdfshortcut.exe.value = ("\"" + appDir.absolutePath() + "/GlosSITarget.exe" + "\"").toStdString(); vdfshortcut.exe.value = ("\"" + appDir.absolutePath() + "/GlosSITarget.exe" + "\"").toStdString();
vdfshortcut.StartDir.value = ( vdfshortcut.StartDir.value = (launch && !maybeLaunchPath.isEmpty()
launch && !maybeLaunchPath.isEmpty()
? (std::string("\"") + std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string() + "\"") ? (std::string("\"") + std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string() + "\"")
: ("\"" + appDir.absolutePath() + "\"").toStdString() : ("\"" + appDir.absolutePath() + "\"").toStdString());
);
vdfshortcut.appId.value = VDFParser::Parser::calculateAppId(vdfshortcut); vdfshortcut.appId.value = VDFParser::Parser::calculateAppId(vdfshortcut);
// ShortcutPath; default // ShortcutPath; default
vdfshortcut.LaunchOptions.value = (name + ".json").toStdString(); vdfshortcut.LaunchOptions.value = (name + ".json").toStdString();
@ -195,11 +172,11 @@ bool UIModel::addToSteam(QVariant shortcut)
// DevkitOverrideAppID; default // DevkitOverrideAppID; default
// LastPlayTime; default // LastPlayTime; default
auto maybeIcon = map["icon"].toString(); auto maybeIcon = map["icon"].toString();
if (maybeIcon.isEmpty()) if (maybeIcon.isEmpty()) {
{
if (launch && !maybeLaunchPath.isEmpty()) if (launch && !maybeLaunchPath.isEmpty())
vdfshortcut.icon.value = maybeLaunchPath.toStdString(); vdfshortcut.icon.value = maybeLaunchPath.toStdString();
} else { }
else {
vdfshortcut.icon.value = maybeIcon.toStdString(); vdfshortcut.icon.value = maybeIcon.toStdString();
} }
// Add installed locally and GlosSI tag // Add installed locally and GlosSI tag
@ -217,19 +194,16 @@ bool UIModel::addToSteam(QVariant shortcut)
const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + shortcutsfile_.toStdWString(); const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + shortcutsfile_.toStdWString();
return VDFParser::Parser::writeShortcuts(config_path, shortcuts_vdf_); return VDFParser::Parser::writeShortcuts(config_path, shortcuts_vdf_);
} }
bool UIModel::removeFromSteam(const QString& name) bool UIModel::removeFromSteam(const QString& name)
{ {
auto& scuts = shortcuts_vdf_.shortcuts; auto& scuts = shortcuts_vdf_.shortcuts;
scuts.erase(std::remove_if(scuts.begin(), scuts.end(), [&name](const auto& shortcut) scuts.erase(std::remove_if(scuts.begin(), scuts.end(), [&name](const auto& shortcut) {
{
return shortcut.appName.value == name.toStdString(); return shortcut.appName.value == name.toStdString();
}), scuts.end()); }),
for (int i = 0; i < scuts.size(); i++) scuts.end());
{ for (int i = 0; i < scuts.size(); i++) {
if (scuts[i].idx != i) if (scuts[i].idx != i) {
{
scuts[i].idx = i; scuts[i].idx = i;
} }
} }
@ -240,232 +214,7 @@ bool UIModel::removeFromSteam(const QString& name)
#ifdef _WIN32 #ifdef _WIN32
QVariantList UIModel::uwpApps() QVariantList UIModel::uwpApps()
{ {
// TODO really should do this async, and notify gui when complete... return UWPFetch::UWPAppList();
if (!IsWindows10OrGreater())
{
return QVariantList();
}
std::vector<std::wstring> logoNames{
L"Square150x150Logo",
L"Square310x310Logo",
L"Square44x44Logo",
L"Square71x71Logo",
L"Square70x70Logo",
L"Logo",
L"SmallLogo",
L"Square30x30Logo",
};
QVariantList pairs;
// is it considered stealing when you take code that was pull-requested by someone else into your own repo?
// Anyway... Stolen from: https://github.com/Thracky/GloSC/commit/3cd92e058498e3ab9d73ced140bbd7e490f639a7
// https://github.com/Alia5/GloSC/commit/3cd92e058498e3ab9d73ced140bbd7e490f639a7
// TODO: only return apps for current user.
// TODO: I have no clue how this WinRT shit works; HELP MEH!
PackageManager^ packageManager = ref new PackageManager();
IIterable<Windows::ApplicationModel::Package^>^ packages = packageManager->FindPackages();
int packageCount = 0;
// Only way to get the count of packages is to iterate through the whole collection first
std::for_each(Windows::Foundation::Collections::begin(packages), Windows::Foundation::Collections::end(packages),
[&](Windows::ApplicationModel::Package^ package)
{
packageCount += 1;
});
int currPackage = 0;
// Iterate through all the packages
std::for_each(Windows::Foundation::Collections::begin(packages), Windows::Foundation::Collections::end(packages),
[&](Windows::ApplicationModel::Package^ package)
{
QGuiApplication::processEvents();
HRESULT hr = S_OK;
IStream* inputStream = NULL;
UINT32 pathLen = 0;
IAppxManifestReader* manifestReader = NULL;
IAppxFactory* appxFactory = NULL;
LPWSTR appId = NULL;
LPWSTR manifestAppName = NULL;
LPWSTR iconName = NULL;
// Get the package path on disk so we can load the manifest XML and get the PRAID
GetPackagePathByFullName(package->Id->FullName->Data(), &pathLen, NULL);
if (pathLen > 0) {
// Length of the path + "\\AppxManifest.xml" that we'll be appending
UINT32 manifestLen = pathLen + 20;
PWSTR pathBuf = (PWSTR)malloc(manifestLen * sizeof(wchar_t));
GetPackagePathByFullName(package->Id->FullName->Data(), &pathLen, pathBuf);
PWSTR manifest_xml = L"\\AppxManifest.xml";
hr = StringCchCatW(pathBuf, manifestLen, manifest_xml);
// Let's ignore a bunch of built in apps and such
if (wcsstr(pathBuf, L"SystemApps")) {
hr = E_FAIL;
}
else if (wcsstr(pathBuf, L".NET.Native."))
hr = E_FAIL;
else if (wcsstr(pathBuf, L".VCLibs."))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.UI"))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.Advertising"))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.Services.Store"))
hr = E_FAIL;
BOOL hasCurrent = FALSE;
// Open the manifest XML
if (SUCCEEDED(hr)) {
hr = SHCreateStreamOnFileEx(
pathBuf,
STGM_READ | STGM_SHARE_EXCLUSIVE,
0, // default file attributes
FALSE, // do not create new file
NULL, // no template
&inputStream);
}
if (SUCCEEDED(hr)) {
hr = CoCreateInstance(
__uuidof(AppxFactory),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IAppxFactory),
(LPVOID*)(&appxFactory));
}
if (SUCCEEDED(hr)) {
hr = appxFactory->CreateManifestReader(inputStream, &manifestReader);
}
// Grab application ID (PRAID) and DisplayName from the XML
if (SUCCEEDED(hr)) {
IAppxManifestApplicationsEnumerator* applications = NULL;
manifestReader->GetApplications(&applications);
if (SUCCEEDED(hr)) {
hr = applications->GetHasCurrent(&hasCurrent);
if (hasCurrent) {
IAppxManifestApplication* application = NULL;
hr = applications->GetCurrent(&application);
if (SUCCEEDED(hr)) {
application->GetStringValue(L"Id", &appId);
application->GetStringValue(L"DisplayName", &manifestAppName);
for (auto& logoNameStr : logoNames)
{
application->GetStringValue(logoNameStr.c_str(), &iconName);
if (!std::wstring(iconName).empty())
{
break;
}
}
application->Release();
}
}
else {
hr = S_FALSE;
}
applications->Release();
}
manifestReader->Release();
inputStream->Release();
}
if (SUCCEEDED(hr)) {
PWSTR appNameBuf;
QString AppUMId = QString::fromWCharArray(package->Id->FamilyName->Data());
QString AppName;
QString Path = QString::fromWCharArray(package->EffectivePath->Data());
// QString thumbToken = QString::fromWCharArray(package->GetThumbnailToken()->Data());
if (manifestAppName != NULL)
{
// If the display name is an indirect string, we'll try and load it using SHLoadIndirectString
if (wcsstr(manifestAppName, L"ms-resource:"))
{
PWSTR res_name = wcsdup(&manifestAppName[12]);
appNameBuf = (PWSTR)malloc(1026);
LPCWSTR resource_str = L"@{";
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/resources/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
// Try several resource paths
if (!SUCCEEDED(hr)) {
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/Resources/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
// If the third one doesn't work, we give up and use the package name from PackageManager
if (!SUCCEEDED(hr)) {
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
}
}
if (!SUCCEEDED(hr))
AppName = QString::fromWCharArray(package->DisplayName->Data());
else
AppName = QString::fromWCharArray(appNameBuf);
free(appNameBuf);
}
else
{
appNameBuf = manifestAppName;
AppName = QString::fromWCharArray(appNameBuf);
}
}
else {
AppName = QString::fromWCharArray(package->DisplayName->Data());
}
QString PRAID = QString::fromWCharArray(appId);
CoTaskMemFree(appId);
if (!PRAID.isEmpty()) {
AppUMId = AppUMId.append("!");
AppUMId = AppUMId.append(PRAID);
}
QVariantMap uwpPair;
uwpPair.insert("AppName", AppName);
uwpPair.insert("AppUMId", AppUMId);
uwpPair.insert("Path", Path);
QString icoFName = Path + "/" + QString::fromWCharArray(iconName);
std::filesystem::path icoPath(icoFName.toStdString());
std::vector<QString> possibleextensions = { ".scale-100", ".scale-125", ".scale-150", ".scale-200" };
if (!std::filesystem::exists(icoPath))
{
for (const auto& ext : possibleextensions)
{
QString maybeFname = QString(icoFName).replace(".png", ext + ".png");
std::filesystem::path maybePath(maybeFname.toStdString());
if (std::filesystem::exists(maybePath))
{
icoPath = maybePath;
break;
}
}
}
uwpPair.insert("IconPath", QString::fromStdString(icoPath.string()));
free(pathBuf);
pairs.push_back(uwpPair);
}
}
currPackage += 1;
});
return pairs;
} }
#endif #endif
@ -491,8 +240,7 @@ void UIModel::writeTarget(const QJsonObject& json, const QString& name)
path /= config_dir_name_.toStdString(); path /= config_dir_name_.toStdString();
path /= (name + ".json").toStdString(); path /= (name + ".json").toStdString();
QFile file(path); QFile file(path);
if (!file.open(QIODevice::Text | QIODevice::ReadWrite)) if (!file.open(QIODevice::Text | QIODevice::ReadWrite)) {
{
// meh // meh
return; return;
} }

@ -14,15 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
#pragma once #pragma once
#include <filesystem> #include "VDFParser.h"
#include <QJsonObject>
#include <QObject> #include <QObject>
#include <QVariant> #include <QVariant>
#include <QJsonObject> #include <filesystem>
#include "VDFParser.h"
class UIModel : public QObject class UIModel : public QObject {
{
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool isWindows READ getIsWindows CONSTANT) Q_PROPERTY(bool isWindows READ getIsWindows CONSTANT)
@ -59,7 +57,6 @@ private:
void writeTarget(const QJsonObject& json, const QString& name); void writeTarget(const QJsonObject& json, const QString& name);
std::filesystem::path getSteamPath() const; std::filesystem::path getSteamPath() const;
std::wstring getSteamUserId() const; std::wstring getSteamUserId() const;
void parseShortcutVDF(); void parseShortcutVDF();

@ -0,0 +1,237 @@
#pragma once
#include <QVariant>
#include <VersionHelpers.h>
#include <Windows.h>
#include <appmodel.h>
#include <appxpackaging.h>
#include <collection.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <windows.h>
#pragma comment(lib, "Shlwapi.lib")
using namespace Windows::Management::Deployment;
using namespace Windows::Foundation::Collections;
namespace UWPFetch {
QVariantList UWPAppList()
{
// TODO really should do this async, and notify gui when complete...
if (!IsWindows10OrGreater()) {
return QVariantList();
}
std::vector<std::wstring> logoNames{
L"Square150x150Logo",
L"Square310x310Logo",
L"Square44x44Logo",
L"Square71x71Logo",
L"Square70x70Logo",
L"Logo",
L"SmallLogo",
L"Square30x30Logo",
};
QVariantList pairs;
// is it considered stealing when you take code that was pull-requested by someone else into your own repo?
// Anyway... Stolen from: https://github.com/Thracky/GloSC/commit/3cd92e058498e3ab9d73ced140bbd7e490f639a7
// https://github.com/Alia5/GloSC/commit/3cd92e058498e3ab9d73ced140bbd7e490f639a7
// TODO: only return apps for current user.
// TODO: I have no clue how this WinRT shit works; HELP MEH!
// BTW: This config app needs to run as admin, in order to modify steams shortcuts.vdf
// This won't hopefully be a problem fetching UWP apps for a specific user?
PackageManager ^ packageManager = ref new PackageManager();
IIterable<Windows::ApplicationModel::Package ^> ^ packages = packageManager->FindPackages();
int packageCount = 0;
// Only way to get the count of packages is to iterate through the whole collection first
std::for_each(Windows::Foundation::Collections::begin(packages), Windows::Foundation::Collections::end(packages),
[&](Windows::ApplicationModel::Package ^ package) {
packageCount += 1;
});
int currPackage = 0;
// Iterate through all the packages
std::for_each(Windows::Foundation::Collections::begin(packages), Windows::Foundation::Collections::end(packages),
[&](Windows::ApplicationModel::Package ^ package) {
QGuiApplication::processEvents();
HRESULT hr = S_OK;
IStream* inputStream = NULL;
UINT32 pathLen = 0;
IAppxManifestReader* manifestReader = NULL;
IAppxFactory* appxFactory = NULL;
LPWSTR appId = NULL;
LPWSTR manifestAppName = NULL;
LPWSTR iconName = NULL;
// Get the package path on disk so we can load the manifest XML and get the PRAID
GetPackagePathByFullName(package->Id->FullName->Data(), &pathLen, NULL);
if (pathLen > 0) {
// Length of the path + "\\AppxManifest.xml" that we'll be appending
UINT32 manifestLen = pathLen + 20;
PWSTR pathBuf = (PWSTR)malloc(manifestLen * sizeof(wchar_t));
GetPackagePathByFullName(package->Id->FullName->Data(), &pathLen, pathBuf);
PWSTR manifest_xml = L"\\AppxManifest.xml";
hr = StringCchCatW(pathBuf, manifestLen, manifest_xml);
// Let's ignore a bunch of built in apps and such
if (wcsstr(pathBuf, L"SystemApps")) {
hr = E_FAIL;
}
else if (wcsstr(pathBuf, L".NET.Native."))
hr = E_FAIL;
else if (wcsstr(pathBuf, L".VCLibs."))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.UI"))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.Advertising"))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.Services.Store"))
hr = E_FAIL;
BOOL hasCurrent = FALSE;
// Open the manifest XML
if (SUCCEEDED(hr)) {
hr = SHCreateStreamOnFileEx(
pathBuf,
STGM_READ | STGM_SHARE_EXCLUSIVE,
0, // default file attributes
FALSE, // do not create new file
NULL, // no template
&inputStream);
}
if (SUCCEEDED(hr)) {
hr = CoCreateInstance(
__uuidof(AppxFactory),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IAppxFactory),
(LPVOID*)(&appxFactory));
}
if (SUCCEEDED(hr)) {
hr = appxFactory->CreateManifestReader(inputStream, &manifestReader);
}
// Grab application ID (PRAID) and DisplayName from the XML
if (SUCCEEDED(hr)) {
IAppxManifestApplicationsEnumerator* applications = NULL;
manifestReader->GetApplications(&applications);
if (SUCCEEDED(hr)) {
hr = applications->GetHasCurrent(&hasCurrent);
if (hasCurrent) {
IAppxManifestApplication* application = NULL;
hr = applications->GetCurrent(&application);
if (SUCCEEDED(hr)) {
application->GetStringValue(L"Id", &appId);
application->GetStringValue(L"DisplayName", &manifestAppName);
for (auto& logoNameStr : logoNames) {
application->GetStringValue(logoNameStr.c_str(), &iconName);
if (!std::wstring(iconName).empty()) {
break;
}
}
application->Release();
}
}
else {
hr = S_FALSE;
}
applications->Release();
}
manifestReader->Release();
inputStream->Release();
}
if (SUCCEEDED(hr)) {
PWSTR appNameBuf;
QString AppUMId = QString::fromWCharArray(package->Id->FamilyName->Data());
QString AppName;
QString Path = QString::fromWCharArray(package->EffectivePath->Data());
// QString thumbToken = QString::fromWCharArray(package->GetThumbnailToken()->Data());
if (manifestAppName != NULL) {
// If the display name is an indirect string, we'll try and load it using SHLoadIndirectString
if (wcsstr(manifestAppName, L"ms-resource:")) {
PWSTR res_name = wcsdup(&manifestAppName[12]);
appNameBuf = (PWSTR)malloc(1026);
LPCWSTR resource_str = L"@{";
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/resources/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
// Try several resource paths
if (!SUCCEEDED(hr)) {
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/Resources/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
// If the third one doesn't work, we give up and use the package name from PackageManager
if (!SUCCEEDED(hr)) {
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
}
}
if (!SUCCEEDED(hr))
AppName = QString::fromWCharArray(package->DisplayName->Data());
else
AppName = QString::fromWCharArray(appNameBuf);
free(appNameBuf);
}
else {
appNameBuf = manifestAppName;
AppName = QString::fromWCharArray(appNameBuf);
}
}
else {
AppName = QString::fromWCharArray(package->DisplayName->Data());
}
QString PRAID = QString::fromWCharArray(appId);
CoTaskMemFree(appId);
if (!PRAID.isEmpty()) {
AppUMId = AppUMId.append("!");
AppUMId = AppUMId.append(PRAID);
}
QVariantMap uwpPair;
uwpPair.insert("AppName", AppName);
uwpPair.insert("AppUMId", AppUMId);
uwpPair.insert("Path", Path);
QString icoFName = Path + "/" + QString::fromWCharArray(iconName);
std::filesystem::path icoPath(icoFName.toStdString());
std::vector<QString> possibleextensions = {".scale-100", ".scale-125", ".scale-150", ".scale-200"};
if (!std::filesystem::exists(icoPath)) {
for (const auto& ext : possibleextensions) {
QString maybeFname = QString(icoFName).replace(".png", ext + ".png");
std::filesystem::path maybePath(maybeFname.toStdString());
if (std::filesystem::exists(maybePath)) {
icoPath = maybePath;
break;
}
}
}
uwpPair.insert("IconPath", QString::fromStdString(icoPath.string()));
free(pathBuf);
pairs.push_back(uwpPair);
}
}
currPackage += 1;
});
return pairs;
}
} // namespace UWPFetch

@ -17,17 +17,15 @@ limitations under the License.
// "Shitty shortcuts.vdf Parser"<22> // "Shitty shortcuts.vdf Parser"<22>
#pragma once #pragma once
#include <charconv>
#include <filesystem> #include <filesystem>
#include <fstream>
#include <iostream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <iostream>
#include <fstream>
#include <charconv>
namespace VDFParser namespace VDFParser {
{ namespace crc {
namespace crc
{
template <typename CONT> template <typename CONT>
uint32_t calculate_crc(CONT container) uint32_t calculate_crc(CONT container)
{ {
@ -38,7 +36,8 @@ namespace VDFParser
for (size_t j = 0; j < 8; j++) { for (size_t j = 0; j < 8; j++) {
uint32_t b = (ch ^ crc) & 1; uint32_t b = (ch ^ crc) & 1;
crc >>= 1; crc >>= 1;
if (b) crc = crc ^ 0xEDB88320; if (b)
crc = crc ^ 0xEDB88320;
ch >>= 1; ch >>= 1;
} }
crc32_table[i] = crc; crc32_table[i] = crc;
@ -51,7 +50,7 @@ namespace VDFParser
} }
return ~crc; return ~crc;
} }
} } // namespace crc
static constexpr const char k_appid[] = {"appid"}; static constexpr const char k_appid[] = {"appid"};
static constexpr const char k_appname[] = {"appname"}; static constexpr const char k_appname[] = {"appname"};
@ -70,16 +69,14 @@ namespace VDFParser
static constexpr const char k_LastPlayTime[] = {"LastPlayTime"}; static constexpr const char k_LastPlayTime[] = {"LastPlayTime"};
static constexpr const char k_tags[] = {"tags"}; static constexpr const char k_tags[] = {"tags"};
enum VDFTypeId enum VDFTypeId {
{
StringList = 0, StringList = 0,
String, String,
Number, Number,
}; };
template <const char* const keyname, typename type, const uint8_t const _type_id> template <const char* const keyname, typename type, const uint8_t const _type_id>
struct VDFKeyPair struct VDFKeyPair {
{
VDFKeyPair() {} VDFKeyPair() {}
explicit VDFKeyPair(type _value) : value(_value) {} explicit VDFKeyPair(type _value) : value(_value) {}
static constexpr uint8_t _TID = _type_id; static constexpr uint8_t _TID = _type_id;
@ -108,8 +105,7 @@ namespace VDFParser
}; };
template <uint8_t initialByte> template <uint8_t initialByte>
struct VDFIdx struct VDFIdx {
{
VDFIdx(){}; VDFIdx(){};
VDFIdx(const VDFIdx& other) VDFIdx(const VDFIdx& other)
{ {
@ -123,26 +119,23 @@ namespace VDFParser
}; };
VDFIdx(int idx) VDFIdx(int idx)
{ {
if (idx > 99) if (idx > 99) {
{
data[0] = initialByte; data[0] = initialByte;
data[1] = 0; data[1] = 0;
} }
else if (idx < 10) else if (idx < 10) {
{
data[1] = std::to_string(idx).c_str()[0]; data[1] = std::to_string(idx).c_str()[0];
} }
else else {
{
auto meh = std::to_string(idx).c_str(); auto meh = std::to_string(idx).c_str();
data[0] = meh[0] + initialByte; data[0] = meh[0] + initialByte;
data[1] = meh[1]; data[1] = meh[1];
} }
} }
char data[2] = {initialByte, 0x0}; char data[2] = {initialByte, 0x0};
operator int() const { operator int() const
if (data[0] == initialByte)
{ {
if (data[0] == initialByte) {
int res = 0; int res = 0;
std::from_chars(&data[1], &data[1], res); std::from_chars(&data[1], &data[1], res);
return res; return res;
@ -167,8 +160,7 @@ namespace VDFParser
} }
}; };
struct ShortcutTag struct ShortcutTag {
{
ShortcutTag(){}; ShortcutTag(){};
ShortcutTag(const ShortcutTag& other) ShortcutTag(const ShortcutTag& other)
{ {
@ -194,8 +186,7 @@ namespace VDFParser
} }
}; };
struct Shortcut struct Shortcut {
{
VDFIdx<0x00> idx; VDFIdx<0x00> idx;
VDFKeyPair<k_appid, uint32_t, VDFTypeId::Number> appId{0x000000}; VDFKeyPair<k_appid, uint32_t, VDFTypeId::Number> appId{0x000000};
VDFKeyPair<k_appname, std::string, VDFTypeId::String> appName{""}; VDFKeyPair<k_appname, std::string, VDFTypeId::String> appName{""};
@ -237,8 +228,7 @@ namespace VDFParser
} }
}; };
struct VDFFile struct VDFFile {
{
VDFFile(){}; VDFFile(){};
VDFFile(const VDFFile& other) VDFFile(const VDFFile& other)
{ {
@ -264,8 +254,7 @@ namespace VDFParser
} }
}; };
class Parser class Parser {
{
private: private:
static inline std::ifstream ifile; static inline std::ifstream ifile;
static inline std::ofstream ofile; static inline std::ofstream ofile;
@ -273,8 +262,7 @@ namespace VDFParser
template <typename typ, typename size> template <typename typ, typename size>
static inline auto readVDFBuffer(typ& buff, size sz) static inline auto readVDFBuffer(typ& buff, size sz)
{ {
if (ifile.eof()) if (ifile.eof()) {
{
return; return;
} }
@ -293,10 +281,8 @@ namespace VDFParser
{ {
std::string str; std::string str;
char ch = '\x0'; char ch = '\x0';
do do {
{ if (ifile.eof()) {
if (ifile.eof())
{
return str; return str;
} }
ifile.read(&ch, sizeof(char)); ifile.read(&ch, sizeof(char));
@ -319,169 +305,140 @@ namespace VDFParser
VDFFile vdffile; VDFFile vdffile;
ifile.open(path.string(), std::ios::binary | std::ios::in); ifile.open(path.string(), std::ios::binary | std::ios::in);
if (!ifile.is_open()) if (!ifile.is_open()) {
{
return {}; return {};
} }
auto firsty = readVDFValue<uint8_t>(); auto firsty = readVDFValue<uint8_t>();
if (vdffile.first_byte != firsty) if (vdffile.first_byte != firsty) {
{
// TODO: invalid // TODO: invalid
ifile.close(); ifile.close();
return vdffile; return vdffile;
} }
auto headername = readVDFString(); auto headername = readVDFString();
if (vdffile.identifier != headername) if (vdffile.identifier != headername) {
{
// TODO: invalid // TODO: invalid
ifile.close(); ifile.close();
return vdffile; return vdffile;
} }
char buff[3]; char buff[3];
while(true) while (true) {
{ if (ifile.eof()) {
if (ifile.eof())
{
break; break;
} }
readVDFBuffer(buff, 3); // 2 bytes idx, 0x00 delmiter readVDFBuffer(buff, 3); // 2 bytes idx, 0x00 delmiter
if (buff[0] == 0x08 && buff[1] == 0x08) if (buff[0] == 0x08 && buff[1] == 0x08) {
{
break; break;
} }
Shortcut shortcut; Shortcut shortcut;
shortcut.idx.data[0] = buff[0]; shortcut.idx.data[1] = buff[1]; shortcut.idx.data[0] = buff[0];
shortcut.idx.data[1] = buff[1];
while (true) // TODO; while (true) // TODO;
{ {
if (ifile.eof()) if (ifile.eof()) {
{
break; break;
} }
VDFTypeId tid = static_cast<VDFTypeId>(readVDFValue<uint8_t>()); VDFTypeId tid = static_cast<VDFTypeId>(readVDFValue<uint8_t>());
if (tid == 0x08) if (tid == 0x08) {
{
auto nextbyte = readVDFValue<uint8_t>(); auto nextbyte = readVDFValue<uint8_t>();
if (nextbyte == 0x08) if (nextbyte == 0x08) {
{
break; break;
} else { }
else {
// WTF?! // WTF?!
// TODO: // TODO:
throw std::exception("WTF"); throw std::exception("WTF");
} }
} }
auto key = readVDFString(); auto key = readVDFString();
if ((tid == 0x08 && key[0] == 0x08) || key == "\x08\x08") if ((tid == 0x08 && key[0] == 0x08) || key == "\x08\x08") {
{
break; break;
} }
if (key == shortcut.appId.key) if (key == shortcut.appId.key) {
{
shortcut.appId.value = readVDFValue<uint32_t>(); shortcut.appId.value = readVDFValue<uint32_t>();
continue; continue;
} }
if (key == shortcut.appName.key) if (key == shortcut.appName.key) {
{
shortcut.appName.value = readVDFString(); shortcut.appName.value = readVDFString();
continue; continue;
} }
if (key == shortcut.exe.key) if (key == shortcut.exe.key) {
{
shortcut.exe.value = readVDFString(); shortcut.exe.value = readVDFString();
continue; continue;
} }
if (key == shortcut.StartDir.key) if (key == shortcut.StartDir.key) {
{
shortcut.StartDir.value = readVDFString(); shortcut.StartDir.value = readVDFString();
continue; continue;
} }
if (key == shortcut.icon.key) if (key == shortcut.icon.key) {
{
shortcut.icon.value = readVDFString(); shortcut.icon.value = readVDFString();
continue; continue;
} }
if (key == shortcut.ShortcutPath.key) if (key == shortcut.ShortcutPath.key) {
{
shortcut.ShortcutPath.value = readVDFString(); shortcut.ShortcutPath.value = readVDFString();
continue; continue;
} }
if (key == shortcut.LaunchOptions.key) if (key == shortcut.LaunchOptions.key) {
{
shortcut.LaunchOptions.value = readVDFString(); shortcut.LaunchOptions.value = readVDFString();
continue; continue;
} }
if (key == shortcut.IsHidden.key) if (key == shortcut.IsHidden.key) {
{
shortcut.IsHidden.value = readVDFValue<uint32_t>(); shortcut.IsHidden.value = readVDFValue<uint32_t>();
continue; continue;
} }
if (key == shortcut.AllowDesktopConfig.key) if (key == shortcut.AllowDesktopConfig.key) {
{
shortcut.AllowDesktopConfig.value = readVDFValue<uint32_t>(); shortcut.AllowDesktopConfig.value = readVDFValue<uint32_t>();
continue; continue;
} }
if (key == shortcut.AllowOverlay.key) if (key == shortcut.AllowOverlay.key) {
{
shortcut.AllowOverlay.value = readVDFValue<uint32_t>(); shortcut.AllowOverlay.value = readVDFValue<uint32_t>();
continue; continue;
} }
if (key == shortcut.openvr.key) if (key == shortcut.openvr.key) {
{
shortcut.openvr.value = readVDFValue<uint32_t>(); shortcut.openvr.value = readVDFValue<uint32_t>();
continue; continue;
} }
if (key == shortcut.Devkit.key) if (key == shortcut.Devkit.key) {
{
shortcut.Devkit.value = readVDFValue<uint32_t>(); shortcut.Devkit.value = readVDFValue<uint32_t>();
continue; continue;
} }
if (key == shortcut.DevkitGameID.key) if (key == shortcut.DevkitGameID.key) {
{
shortcut.DevkitGameID.value = readVDFString(); shortcut.DevkitGameID.value = readVDFString();
continue; continue;
} }
if (key == shortcut.DevkitOverrideAppID.key) if (key == shortcut.DevkitOverrideAppID.key) {
{
shortcut.DevkitOverrideAppID.value = readVDFValue<uint32_t>(); shortcut.DevkitOverrideAppID.value = readVDFValue<uint32_t>();
continue; continue;
} }
if (key == shortcut.LastPlayTime.key) if (key == shortcut.LastPlayTime.key) {
{
shortcut.LastPlayTime.value = readVDFValue<uint32_t>(); shortcut.LastPlayTime.value = readVDFValue<uint32_t>();
continue; continue;
} }
if (key == shortcut.tags.key) if (key == shortcut.tags.key) {
{
// TODO: read tags // TODO: read tags
ShortcutTag tag; ShortcutTag tag;
while (true) while (true) {
{ if (ifile.eof()) {
if (ifile.eof())
{
break; break;
} }
char tbuff[2]; char tbuff[2];
readVDFBuffer(tbuff, 2); // 2 bytes idx readVDFBuffer(tbuff, 2); // 2 bytes idx
if (tbuff[0] == 0x08 && tbuff[1] == 0x08) if (tbuff[0] == 0x08 && tbuff[1] == 0x08) {
{
ifile.seekg(-2, std::ios_base::cur); ifile.seekg(-2, std::ios_base::cur);
break; break;
} }
tag.idx.data[0] = tbuff[0]; tag.idx.data[1] = tbuff[1]; tag.idx.data[0] = tbuff[0];
tag.idx.data[1] = tbuff[1];
ifile.seekg(1, std::ios_base::cur); ifile.seekg(1, std::ios_base::cur);
tag.value = readVDFString(); tag.value = readVDFString();
shortcut.tags.value.push_back(tag); shortcut.tags.value.push_back(tag);
} }
continue; continue;
} }
} }
if (!(shortcut.idx.data[0] == 0x0 && shortcut.idx.data[1] == 0x0)) if (!(shortcut.idx.data[0] == 0x0 && shortcut.idx.data[1] == 0x0)) {
{
vdffile.shortcuts.push_back(shortcut); vdffile.shortcuts.push_back(shortcut);
} }
} }
@ -494,15 +451,13 @@ namespace VDFParser
static inline bool writeShortcuts(std::filesystem::path path, const VDFFile& vdffile) static inline bool writeShortcuts(std::filesystem::path path, const VDFFile& vdffile)
{ {
ofile.open(path.string(), std::ios::binary | std::ios::out); ofile.open(path.string(), std::ios::binary | std::ios::out);
if (!ofile.is_open()) if (!ofile.is_open()) {
{
return false; return false;
} }
ofile.write((char*)&vdffile.first_byte, 1); ofile.write((char*)&vdffile.first_byte, 1);
ofile.write(vdffile.identifier.data(), vdffile.identifier.length()); ofile.write(vdffile.identifier.data(), vdffile.identifier.length());
ofile.write("\x00", 1); ofile.write("\x00", 1);
for (auto& shortcut : vdffile.shortcuts) for (auto& shortcut : vdffile.shortcuts) {
{
ofile.write(shortcut.idx.data, 2); ofile.write(shortcut.idx.data, 2);
ofile.write("\x00", 1); ofile.write("\x00", 1);
// //
@ -587,12 +542,10 @@ namespace VDFParser
ofile.write(shortcut.LastPlayTime.key, 13); ofile.write(shortcut.LastPlayTime.key, 13);
ofile.write((char*)&shortcut.LastPlayTime.value, 4); ofile.write((char*)&shortcut.LastPlayTime.value, 4);
// //
ofile.write((char*)&shortcut.tags.type_id, 1); ofile.write((char*)&shortcut.tags.type_id, 1);
ofile.write(shortcut.tags.key, 5); ofile.write(shortcut.tags.key, 5);
for (auto& tag : shortcut.tags.value) for (auto& tag : shortcut.tags.value) {
{
ofile.write(tag.idx.data, 2); ofile.write(tag.idx.data, 2);
ofile.write("\x00", 1); ofile.write("\x00", 1);
ofile.write(tag.value.data(), tag.value.length()); ofile.write(tag.value.data(), tag.value.length());
@ -604,6 +557,5 @@ namespace VDFParser
ofile.close(); ofile.close();
return true; return true;
} }
}; };
} } // namespace VDFParser

@ -19,8 +19,7 @@ limitations under the License.
#define NOMINMAX #define NOMINMAX
#include <Windows.h> #include <Windows.h>
class WinEventFilter : public QAbstractNativeEventFilter class WinEventFilter : public QAbstractNativeEventFilter {
{
public: public:
WinEventFilter() = default; WinEventFilter() = default;
@ -37,8 +36,7 @@ public:
auto msg = static_cast<MSG*>(message)->message; auto msg = static_cast<MSG*>(message)->message;
auto lParam = static_cast<MSG*>(message)->lParam; auto lParam = static_cast<MSG*>(message)->lParam;
auto hwnd = static_cast<MSG*>(message)->hwnd; auto hwnd = static_cast<MSG*>(message)->hwnd;
if (msg == WM_NCCALCSIZE) if (msg == WM_NCCALCSIZE) {
{
auto sz = reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam); auto sz = reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam);
sz->rgrc[0].top -= 6; sz->rgrc[0].top -= 6;
} }

@ -19,9 +19,9 @@ limitations under the License.
#include <QWindow> #include <QWindow>
#ifdef _WIN32 #ifdef _WIN32
#include <VersionHelpers.h>
#include <Windows.h> #include <Windows.h>
#include <dwmapi.h> #include <dwmapi.h>
#include <VersionHelpers.h>
#pragma comment(lib, "Dwmapi.lib") #pragma comment(lib, "Dwmapi.lib")
#endif #endif
@ -30,8 +30,7 @@ limitations under the License.
#ifdef _WIN32 #ifdef _WIN32
// Some undocument stuff to enable aero on win10 or higher... // Some undocument stuff to enable aero on win10 or higher...
enum AccentState enum AccentState {
{
ACCENT_DISABLED = 0, ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1, ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2, ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
@ -40,8 +39,7 @@ enum AccentState
ACCENT_ENABLE_HOSTBACKDROP = 5, // RS5 1809 ACCENT_ENABLE_HOSTBACKDROP = 5, // RS5 1809
ACCENT_INVALID_STATE = 6 ACCENT_INVALID_STATE = 6
}; };
struct AccentPolicy struct AccentPolicy {
{
AccentState AccentState; AccentState AccentState;
int AccentFlags; int AccentFlags;
@ -49,14 +47,12 @@ struct AccentPolicy
int AnimationId; int AnimationId;
}; };
enum WindowCompositionAttribute enum WindowCompositionAttribute {
{
// ... // ...
WCA_ACCENT_POLICY = 19 WCA_ACCENT_POLICY = 19
// ... // ...
}; };
struct WindowCompositionAttributeData struct WindowCompositionAttributeData {
{
WindowCompositionAttribute Attribute; WindowCompositionAttribute Attribute;
void* Data; void* Data;
int SizeOfData; int SizeOfData;
@ -66,7 +62,6 @@ typedef HRESULT(__stdcall* PSetWindowCompositionAttribute)(HWND hwnd, WindowComp
#endif #endif
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
@ -98,16 +93,18 @@ int main(int argc, char* argv[])
// Enable blurbehind (not needed?) anyway gives nice background on win7 and 8 // Enable blurbehind (not needed?) anyway gives nice background on win7 and 8
DWM_BLURBEHIND bb{}; DWM_BLURBEHIND bb{};
bb.dwFlags = DWM_BB_ENABLE; bb.fEnable = true; bb.hRgnBlur = nullptr; bb.dwFlags = DWM_BB_ENABLE;
bb.fEnable = true;
bb.hRgnBlur = nullptr;
DwmEnableBlurBehindWindow(hwnd, &bb); DwmEnableBlurBehindWindow(hwnd, &bb);
if (IsWindows10OrGreater()) if (IsWindows10OrGreater()) {
{
// undoc stuff for aero >= Win10 // undoc stuff for aero >= Win10
int color = (0 << 24) + (0x21 << 16) + (0x11 << 8) + (0x11); int color = (0 << 24) + (0x21 << 16) + (0x11 << 8) + (0x11);
AccentPolicy accPol{}; AccentPolicy accPol{};
accPol.AccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND; accPol.AccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND;
accPol.AccentFlags = 2; accPol.GradientColor = color; accPol.AccentFlags = 2;
accPol.GradientColor = color;
accPol.AnimationId = 0; accPol.AnimationId = 0;
WindowCompositionAttributeData data{}; WindowCompositionAttributeData data{};
data.Attribute = WindowCompositionAttribute::WCA_ACCENT_POLICY; data.Attribute = WindowCompositionAttribute::WCA_ACCENT_POLICY;
@ -115,14 +112,10 @@ int main(int argc, char* argv[])
data.SizeOfData = sizeof(accPol); data.SizeOfData = sizeof(accPol);
auto user32dll = GetModuleHandle(L"user32.dll"); auto user32dll = GetModuleHandle(L"user32.dll");
if (user32dll) { if (user32dll) {
PSetWindowCompositionAttribute SetWindowCompositionAttribute = ( PSetWindowCompositionAttribute SetWindowCompositionAttribute = (reinterpret_cast<PSetWindowCompositionAttribute>(GetProcAddress(user32dll, "SetWindowCompositionAttribute")));
reinterpret_cast<PSetWindowCompositionAttribute>(GetProcAddress(user32dll, "SetWindowCompositionAttribute")) if (SetWindowCompositionAttribute) {
);
if (SetWindowCompositionAttribute)
{
auto res = SetWindowCompositionAttribute(hwnd, &data); auto res = SetWindowCompositionAttribute(hwnd, &data);
if (SUCCEEDED(res)) if (SUCCEEDED(res)) {
{
uimodel.setAcrylicEffect(true); uimodel.setAcrylicEffect(true);
} }
} }

@ -10,5 +10,6 @@
<file>svg/edit_white_24dp.svg</file> <file>svg/edit_white_24dp.svg</file>
<file>svg/steam.svg</file> <file>svg/steam.svg</file>
<file>qml/UWPSelectDialog.qml</file> <file>qml/UWPSelectDialog.qml</file>
<file>qml/InfoDialog.qml</file>
</qresource> </qresource>
</RCC> </RCC>

@ -0,0 +1,98 @@
/*
Copyright 2021 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import QtQuick 6.2
import QtQuick.Controls 6.2
Dialog {
id: dlg
anchors.centerIn: parent
property var confirmedParam: null
signal confirmed(var param)
visible: false
modal: true
dim: true
parent: Overlay.overlay
Overlay.modal: Rectangle {
color: Qt.rgba(0,0,0,0.4)
opacity: backdropOpacity
Behavior on opacity {
NumberAnimation {
duration: 300
}
}
}
property real backdropOpacity: 1.0
property alias titleText: title.text
property alias text: text.text
enter: Transition {
NumberAnimation{target: content; property: "y"; from: parent.height; to: 16; duration: 300; easing.type: Easing.OutQuad }
NumberAnimation{target: background; property: "y"; from: parent.height; to: 0; duration: 300; easing.type: Easing.OutQuad }
NumberAnimation{target: dlg; property: "backdropOpacity"; from: 0; to: 1; duration: 300; easing.type: Easing.OutQuad }
}
exit: Transition {
NumberAnimation{target: content; property: "y"; from: 16; to: parent.height; duration: 300; easing.type: Easing.InQuad }
NumberAnimation{target: background; property: "y"; from: 0; to: parent.height; duration: 300; easing.type: Easing.InQuad }
NumberAnimation{target: dlg; property: "backdropOpacity"; from: 1; to: 0; duration: 300; easing.type: Easing.InQuad }
}
background: RPane {
id: background
radius: 4
Material.elevation: 64
bgOpacity: 0.97
}
contentItem: Item {
id: content
readonly property real spacing: 16
implicitWidth: Math.max(row.width, col.width)
implicitHeight: title.height + row.height + spacing + col.height
Label {
id: title
anchors.top: parent.top
anchors.left: parent.left
text: dlg.title
font.bold: true
}
Column {
id: col
anchors.top: title.bottom
anchors.topMargin: parent.spacing
spacing: 16
Label {
id: text
}
}
Row {
id: row
anchors.top: col.bottom
anchors.topMargin: parent.spacing
spacing: 16
Button {
text: qsTr("OK")
onClicked: function(){
close()
confirmed(confirmedParam)
}
}
anchors.right: parent.right
}
}
}

@ -17,6 +17,7 @@ import QtQuick 6.2
import QtQuick.Layouts 6.2 import QtQuick.Layouts 6.2
import QtQuick.Controls 6.2 import QtQuick.Controls 6.2
import QtQuick.Controls.Material 6.2 import QtQuick.Controls.Material 6.2
import QtQuick.Dialogs 6.2
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
GridView { GridView {
@ -64,6 +65,12 @@ GridView {
NumberAnimation { properties: "x,y"; duration: 300; easing.type: Easing.InQuad } NumberAnimation { properties: "x,y"; duration: 300; easing.type: Easing.InQuad }
} }
InfoDialog {
id: writeErrorDialog
titleText: qsTr("Error")
text: qsTr("Error writing shortcuts.vdf...")
}
delegate: RPane { delegate: RPane {
id: delegateRoot id: delegateRoot
color: Qt.lighter(Material.background, 1.6) color: Qt.lighter(Material.background, 1.6)
@ -90,7 +97,7 @@ GridView {
anchors.left: parent.left anchors.left: parent.left
anchors.bottom: buttonrow.top anchors.bottom: buttonrow.top
anchors.margins: 12 anchors.margins: 12
spacing: 4 spacing: 8
Row { Row {
spacing: 8 spacing: 8
visible: modelData.launchPath && modelData.launchPath.length > 0 visible: modelData.launchPath && modelData.launchPath.length > 0
@ -112,6 +119,37 @@ GridView {
elide: Text.ElideRight elide: Text.ElideRight
} }
} }
Row {
visible: false // TODO: dunno about this...
spacing: 4
Label {
text: qsTr("Is in")
font.bold: true
}
Image {
anchors.verticalCenter: parent.verticalCenter
source: "qrc:/svg/steam.svg"
width: 16
height: 16
smooth: true
mipmap: true
ColorOverlay {
anchors.fill: parent
source: parent
color: "white"
}
}
Item {
width: 4
height: 1
}
Label {
anchors.verticalCenter: parent.verticalCenter
text: delegateRoot.isInSteam ? qsTr("Yes") : qsTr("No")
width: 292 - typeLabel.width - 72
elide: Text.ElideRight
}
}
} }
Image { Image {
@ -134,19 +172,20 @@ GridView {
onClicked: function(){ onClicked: function(){
if (delegateRoot.isInSteam) { if (delegateRoot.isInSteam) {
if (!uiModel.removeFromSteam(modelData.name)) { if (!uiModel.removeFromSteam(modelData.name)) {
// TODO: show error writeErrorDialog.open();
return; return;
} }
} else { } else {
if (!uiModel.addToSteam(modelData)) { if (!uiModel.addToSteam(modelData)) {
// TODO: show error writeErrorDialog.open();
return; return;
} }
} }
delegateRoot.isInSteam = uiModel.isInSteam(modelData) delegateRoot.isInSteam = uiModel.isInSteam(modelData)
steamShortcutsChanged = true
} }
highlighted: delegateRoot.isInSteam highlighted: delegateRoot.isInSteam
Material.accent: Material.color(Material.Red, Material.Shade400) Material.accent: Material.color(Material.Red, Material.Shade800)
Row { Row {
anchors.centerIn: parent anchors.centerIn: parent
spacing: 8 spacing: 8

@ -43,6 +43,17 @@ Window {
return Qt.rgba(color.r, color.g, color.b, alpha); return Qt.rgba(color.r, color.g, color.b, alpha);
} }
property bool steamShortcutsChanged: false
InfoDialog {
id: steamChangedDialog
titleText: qsTr("Attention!")
text: qsTr("Please restart Steam to reload your changes!")
onConfirmed: function (callback) {
callback();
}
}
Rectangle { Rectangle {
id: titleBar id: titleBar
visible: uiModel.isWindows visible: uiModel.isWindows
@ -83,7 +94,14 @@ Window {
ToolButton { ToolButton {
id: closbtn id: closbtn
text: "🗙" text: "🗙"
onClicked: window.close() onClicked: steamShortcutsChanged
? (function(){
steamChangedDialog.confirmedParam = () => {
window.close()
}
steamChangedDialog.open()
})()
: window.close()
background: Rectangle { background: Rectangle {
implicitWidth: 32 implicitWidth: 32
implicitHeight: 32 implicitHeight: 32
@ -99,7 +117,6 @@ Window {
} }
} }
} }
} }
Item { Item {
@ -229,6 +246,5 @@ Window {
} }
} }
} }
} }
} }

@ -20,12 +20,11 @@ limitations under the License.
#include "SteamTarget.h" #include "SteamTarget.h"
#include "OverlayLogSink.h"
#include "Settings.h"
#include <spdlog/sinks/basic_file_sink.h> #include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h> #include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include "OverlayLogSink.h"
#include "Settings.h"
#ifdef _WIN32 #ifdef _WIN32
#ifdef CONSOLE #ifdef CONSOLE

Loading…
Cancel
Save