UWP Selection dialog with icons

pull/130/head
Peter Repukat 3 years ago
parent 9eaceb3bfb
commit 76b6ee774c

@ -126,6 +126,7 @@
<None Include="qml\RPane.qml" />
<None Include="qml\ShortcutCards.qml" />
<None Include="qml\ShortcutProps.qml" />
<None Include="qml\UWPSelectDialog.qml" />
<QtRcc Include="qml.qrc" />
<None Include="qml\main.qml" />
</ItemGroup>
@ -133,7 +134,7 @@
<QtMoc Include="UIModel.h" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="qml\WinEventFilter.h" />
<ClInclude Include="WinEventFilter.h" />
</ItemGroup>
<ItemGroup>
<Xml Include="manifest.xml" />

@ -54,6 +54,9 @@
<None Include="qml\FluentTextInput.qml">
<Filter>Source Files</Filter>
</None>
<None Include="qml\UWPSelectDialog.qml">
<Filter>qml</Filter>
</None>
</ItemGroup>
<ItemGroup>
<QtMoc Include="UIModel.h">
@ -61,7 +64,7 @@
</QtMoc>
</ItemGroup>
<ItemGroup>
<ClInclude Include="qml\WinEventFilter.h">
<ClInclude Include="WinEventFilter.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>

@ -31,6 +31,9 @@ limitations under the License.
#pragma comment(lib, "Shlwapi.lib")
using namespace Windows::Management::Deployment;
using namespace Windows::Foundation::Collections;
#include <QGuiApplication>
#endif
UIModel::UIModel() : QObject(nullptr)
@ -135,13 +138,24 @@ void UIModel::deleteTarget(int index)
#ifdef _WIN32
QVariantList UIModel::uwpApps()
{
// TODO really should do this async, and notify gui when complete...
if (!IsWindows10OrGreater())
{
return QVariantList();
}
QVariantList pairs;
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
@ -149,7 +163,7 @@ QVariantList UIModel::uwpApps()
// 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();
@ -166,6 +180,7 @@ QVariantList UIModel::uwpApps()
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;
@ -173,6 +188,7 @@ QVariantList UIModel::uwpApps()
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);
@ -241,6 +257,14 @@ QVariantList UIModel::uwpApps()
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();
}
}
@ -258,6 +282,8 @@ QVariantList UIModel::uwpApps()
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
@ -283,7 +309,7 @@ QVariantList UIModel::uwpApps()
}
if (!SUCCEEDED(hr))
AppName = QString::fromWCharArray(package->Id->Name->Data());
AppName = QString::fromWCharArray(package->DisplayName->Data());
else
AppName = QString::fromWCharArray(appNameBuf);
free(appNameBuf);
@ -296,7 +322,7 @@ QVariantList UIModel::uwpApps()
}
else {
AppName = QString::fromWCharArray(package->Id->Name->Data());
AppName = QString::fromWCharArray(package->DisplayName->Data());
}
QString PRAID = QString::fromWCharArray(appId);
@ -308,6 +334,27 @@ QVariantList UIModel::uwpApps()
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);

@ -26,7 +26,7 @@ limitations under the License.
#endif
#include "UIModel.h"
#include "qml/WinEventFilter.h"
#include "WinEventFilter.h"
#ifdef _WIN32
// Some undocument stuff to enable aero on win10 or higher...

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

@ -95,7 +95,6 @@ Dialog {
onClicked: function(){
close()
confirmed("uwp")
const wtf = uiModel.uwpList;
}
}
}

@ -27,6 +27,8 @@ GridView {
anchors.topMargin: margins
visible: model.length > 0
signal editClicked(var index, var shortcutInfo)
ScrollBar.vertical: ScrollBar {
}
property real margins: 16
@ -75,10 +77,10 @@ GridView {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
wrapMode: Text.WordWrap
text: modelData.name
font.bold: true
font.pixelSize: 16
elide: Text.ElideRight
}
Column {
@ -91,6 +93,7 @@ GridView {
spacing: 8
visible: modelData.launchPath && modelData.launchPath.length > 0
Label {
id: typeLabel
text: uiModel.isWindows && modelData.launchPath
? modelData.launchPath.replace(/^.{1,3}:/, "").length < modelData.launchPath.length
? "Win32"
@ -103,6 +106,8 @@ GridView {
? modelData.launchPath.replace(/.*(\\|\/)/gm, "")
: ""
text: uiModel.isWindows ? te : te.replace(/\..{3}$/, "")
width: 292 - typeLabel.width - 72
elide: Text.ElideRight
}
}
}

@ -25,6 +25,7 @@ Item {
anchors.fill: parent
property alias fileDialog: fileDialog
property alias uwpSelectDialog: uwpSelectDialog
signal cancel()
signal done(var shortcut)
@ -163,6 +164,7 @@ Item {
Layout.alignment: Qt.AlignBottom
text: qsTr("UWP")
visible: uiModel.isWindows
onClicked: uwpSelectDialog.open();
}
Item {
height: 1
@ -278,6 +280,15 @@ Item {
}
}
// UWPSelectDialog {}
UWPSelectDialog {
id: uwpSelectDialog
onConfirmed: function(modelData) {
if (nameInput.text == "") {
nameInput.text = modelData.AppName
}
pathInput.text = modelData.AppUMId
launchApp.checked = true
}
}
}

@ -0,0 +1,182 @@
/*
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
import QtQuick.Layouts 6.2
import QtQuick.Controls.Material 6.2
Dialog {
id: dlg
anchors.centerIn: parent
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
onOpened: function() {
listview.model = uiModel.uwpList
}
onClosed: function() {
listview.model = null
}
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
implicitWidth: listview.width
implicitHeight: listview.height + titlelabel.height + 16 + 64
clip: true
Label {
id: titlelabel
text: qsTr("Select UWP App...")
font.pixelSize: 24
font.bold: true
}
BusyIndicator {
running: visible
anchors.centerIn: parent
opacity: (!listview.model || listview.model.length == 0) ? 1 : 0
Behavior on opacity {
NumberAnimation {
duration: 350
easing.type: Easing.InOutQuad
}
}
visible: opacity == 0 ? false : true
}
Button {
anchors.right: parent.right
anchors.top: listview.bottom
anchors.topMargin: 16
anchors.rightMargin: 2
text: qsTr("Cancel")
onClicked: dlg.close()
}
ListView {
anchors.top: titlelabel.bottom
anchors.topMargin: 16
id: listview
width: window.width * 0.45
height: window.height * 0.66
spacing: 0
clip: true
model: null
ScrollBar.vertical: ScrollBar {
}
opacity: (!listview.model || listview.model.length == 0) ? 0 : 1
Behavior on opacity {
ParallelAnimation {
NumberAnimation {
duration: 350
easing.type: Easing.InOutQuad
}
PropertyAnimation {
target: listview
property: "anchors.topMargin"
from: window.height * 0.75
to: 16
duration: 350
easing.type: Easing.InOutQuad
}
}
}
delegate: Item {
width: listview.width
height: 72
Image {
id: maybeIcon
width: 56
height: 56
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
source: "file:///" + modelData.IconPath
mipmap: true
smooth: true
}
Column {
anchors.left: maybeIcon.right
anchors.right: parent.right
anchors.leftMargin: 16
anchors.verticalCenter: parent.verticalCenter
spacing: 2
Label {
text: modelData.AppName
font.pixelSize: 18
font.bold: true
}
Label {
text: modelData.AppUMId
font.pixelSize: 12
}
}
Rectangle {
anchors.bottom: parent.bottom
height: 1
width: parent.width
color: Qt.rgba(1,1,1,0.25)
}
MouseArea {
anchors.fill: parent
onClicked: function(){
confirmed(modelData)
dlg.close();
}
}
}
}
}
}

@ -225,7 +225,7 @@ Window {
props.fileDialog.open();
}
if (param == "uwp") {
// props.fileDIalog.open();
props.uwpSelectDialog.open();
}
}
}

Loading…
Cancel
Save