[New Module] Workspaces (#34324)

* spell checker

* Adding OOBE Projects page

* changed the default hotkey

* module interface

* rename projects editor

* bug report tool

* installer

* gpo

* exit event constant

* extend search for projects by search over the containing apps' names

* [Projects] fix grammatical issue #43 (1 app - many apps)

* [Projects] Editor: Main page: fix layout if there are many apps, launch button not disappearing on the right side

* dsc

* github

* pipeline

* guid prefix

* [Projects] fixing general settings gpo handling in runner + minor changes

* arm build fix

* Do not allow saving project if name or applist is empty. Also minor UI changes

* version

* editor version

* spellcheck

* editor dll signing

* update projects names to filter them out

* shortcut saving fix

* [Projects] Editor: brining the highlighted app's icon into the foreground. + minor UI fixes

* spell checker

* spellcheck

* [Projects] re-implementing icon size calculation to have similar sized icons for every app.

* [projects] Adding info message for cases: there are no projects or no results for the search

* [Projects] Adding Edit button to the popup. + minor changes

* [Projects] Making popup having rounded corners

* changed "no projects" text color and position

* remove opening the first proj

* fix placing windows of the same app in the project

* [Projects] bringing back the breadcrumb on the editor page. Make it clickable.

* [Projects] optimizing click handlers

* [Projects] Removing not selected apps on save

* moved on thread executor to common

* moved display utils

* added convert rect

* unsigned monitor number

* set awareness

* app placement

* [Projects] Re-implementing preview drawing - one common image

* [Projects] fix boundary calculation, use DPI aware values

* fix launching with command line args

* Fix ARM64 CI build

* launch packaged apps using names when possible

* spell-check

* update packaged apps path

* projects editor single instance

* [Projects] Add Select all checkbox, Delete selected button

* Add Checkbox for per monitor selection

* modifying highlight in preview

* spell checker

* logs

* exclude help windows

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/49

* Add intermediate step to project creation

* minor bugfix

* mutex fix

* modifying highlight for minimized apps

* Fixing bug: re-draw the preview on app deletion in the editor

* Adding helper class for getting the right bounds for screens

* spell checker

* spell checker

* Minor fixes in the capture dialog

* get dpi unaware screen bounds

* refactoring: added utils

* changed window filter

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/2

* clean up

* refactoring

* projects common lib

* localizable default project prefix

* launcher resources

* clean up

* change snapshot project saving

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/14

* changed project data

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/14

* changed project creation save-cancel handles

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/14

* spell-check

* Remove checkboxes, delete feature

* remove unused from the project

* get command line args in the snapshot

* minimized settings snap fix

* set window property after launching

* FZ: ignore projects launched windows

* Implementing major new features: remove button, position manipulation, arguments, admin, minimized, maximized

* modifying colors

* launcher project filters

* clean up

* Hide Admin checkbox

* hide WIP

* spell-check

* Revert "Hide Admin checkbox"

This reverts commit 3036df9d7f.

* get app elevated property

* Implementing Launch and Edit feature

* fixing: update of listed projects on the main page after hitting save in editor

* Fix for packaged app's icons

* fixing scroll speed issue

* change scroll speed to 15

* launch elevated apps

* minor fixes

* minor fix

* enhancing shortcut handling

* can-launch-elevated check

* projects module interface telemetry

* Implementing store of setting "order by".

* minor string correction

* moved projects data parsing

* telemetry

* add move apps checkbox

* notification about elevated apps

* restart unelevated

* move existing windows

* keep opened windows at the same positions

* handle powertoys settings

* use common theme

* fix corrupted data: project id and monitor id

* project launch on "launch and edit"

* clean up

* show screen numbers instead of monitor names

* launcher error messages

* fix default shortcut

* Adding launch button to projects settings, dashboard and flyout

* Adding new app which is launched when launching a project. It shows the status of the launch process

* spell checker

* Renaming Projects to App Layouts. Replacing only string values, not the variable names

* Re-ordering modules after Renaming Projects + spell checker

* setting window size according to the screen (making it bigger)

* commenting out feature "move apps if exist"

* spell checker

* Add ProjectsLauncherUI to signing

* opening apps in minimized state which are placed on a monitor, which is not found at the moment of launching

* consistent file name

* removed unused sln

* telemetry: create event

* WindowPosition comparison

* telemetry: edit event

* fix muted Launch as admin checkbox

* telemetry: delete event

* updated Edit telemetry event

* added invoke point to launcher args

* added utils

* parse invoke point

* replaced tuple with struct

* telemetry: launch event

* MonitorRect comparison

* resources

* rename: folders

* remove outdated

* rename: window property

* rename: files and folders

* rename: common data structures

* rename: telemetry namespace

* rename: workspaces data

* rename ProjectsLib -> WorkspacesLib

* rename: gpo

* rename: settings

* rename: launcher UI

* rename: other

* rename: pt run

* rename: fz

* rename: module interface

* rename: icon

* rename: snapshot tool

* rename: editor

* rename: common files

* rename: launcher

* rename: editor resources

* fix empty file crash

* rename: json

* rename: module interface

* fix custom actions build

* added launch editor event constant

* xaml formatting

* Add missing method defition to interop::Constants idl
Remove Any CPU config

* more .sln cleanup

* [Run][PowerToys] Fix Workspaces utility (#34336)

polished workspaces utility

* build fix - align CppWinRT version

* address PR comment: fix isdigit

* indentation

* address PR comment: rename function

* address PR comment: changed version for workspaces and revision

* added supported version definition

* addressPR comment: use BringToForeground

* address PR comments: updated projects

* address PR comment: uncomment gpo in settings

* address PR comment: rename oobe view

* update OOBE image with current module name

* moved AppUtils

* launching with AppUserModel.ID

* fixed module order in settings

* fix xaml formatting

* [Workspaces] Close launcher if there are failed launches. Plus adding new spinner gif

* fix topmost LauncherUI

* clean up

* UI closing

* BugReportTool - omit cmd arg data

* Delete icon on workspace removal

* Adding cancellation to launcher UI.

* reordered launching

* fix terminating UI

* Removing old shortcut on workspace renaming

* Sentence case labels

* get process path without waiting

* comment out unused

* remove unused argument

* logs

* New icon

* fix launch and edit for the new project

* fix launch and edit: save new project

* Update exe icons

---------

Co-authored-by: donlaci <laszlo@janeasystems.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: Stefan Markovic <stefan@janeasystems.com>
Co-authored-by: Davide Giacometti <davide.giacometti@outlook.it>
Co-authored-by: Niels Laute <niels.laute@live.nl>
This commit is contained in:
Seraphima Zykova
2024-08-23 09:28:13 +02:00
committed by GitHub
parent 2a8e211cfd
commit 579619952d
221 changed files with 12805 additions and 12 deletions

View File

@@ -0,0 +1,252 @@
#include "pch.h"
#include "AppUtils.h"
#include <atlbase.h>
#include <ShlObj.h>
#include <propvarutil.h>
#include <filesystem>
#include <common/logger/logger.h>
#include <common/utils/process_path.h>
#include <common/utils/winapi_error.h>
namespace Utils
{
namespace Apps
{
namespace NonLocalizable
{
constexpr const wchar_t* PackageFullNameProp = L"System.AppUserModel.PackageFullName";
constexpr const wchar_t* PackageInstallPathProp = L"System.AppUserModel.PackageInstallPath";
constexpr const wchar_t* InstallPathProp = L"System.Link.TargetParsingPath";
constexpr const wchar_t* HostEnvironmentProp = L"System.AppUserModel.HostEnvironment";
constexpr const wchar_t* AppUserModelIdProp = L"System.AppUserModel.ID";
constexpr const wchar_t* FileExplorerName = L"File Explorer";
constexpr const wchar_t* FileExplorerPath = L"C:\\WINDOWS\\EXPLORER.EXE";
constexpr const wchar_t* PowerToys = L"PowerToys.exe";
constexpr const wchar_t* PowerToysSettingsUpper = L"POWERTOYS.SETTINGS.EXE";
constexpr const wchar_t* PowerToysSettings = L"PowerToys.Settings.exe";
}
AppList IterateAppsFolder()
{
AppList result{};
// get apps folder
CComPtr<IShellItem> folder;
HRESULT hr = SHGetKnownFolderItem(FOLDERID_AppsFolder, KF_FLAG_DEFAULT, nullptr, IID_PPV_ARGS(&folder));
if (FAILED(hr))
{
Logger::error(L"Failed to get known apps folder: {}", get_last_error_or_default(hr));
return result;
}
CComPtr<IEnumShellItems> enumItems;
hr = folder->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&enumItems));
if (FAILED(hr))
{
Logger::error(L"Failed to bind to enum items handler: {}", get_last_error_or_default(hr));
return result;
}
IShellItem* items;
while (enumItems->Next(1, &items, nullptr) == S_OK)
{
CComPtr<IShellItem> item = items;
CComHeapPtr<wchar_t> name;
hr = item->GetDisplayName(SIGDN_NORMALDISPLAY, &name);
if (FAILED(hr))
{
Logger::error(L"Failed to get display name for app: {}", get_last_error_or_default(hr));
continue;
}
AppData data{
.name = std::wstring(name.m_pData),
};
// properties
CComPtr<IPropertyStore> store;
hr = item->BindToHandler(NULL, BHID_PropertyStore, IID_PPV_ARGS(&store));
if (FAILED(hr))
{
Logger::error(L"Failed to bind to property store handler: {}", get_last_error_or_default(hr));
continue;
}
DWORD count = 0;
store->GetCount(&count);
for (DWORD i = 0; i < count; i++)
{
PROPERTYKEY pk;
hr = store->GetAt(i, &pk);
if (FAILED(hr))
{
Logger::error(L"Failed to get property key: {}", get_last_error_or_default(hr));
continue;
}
CComHeapPtr<wchar_t> pkName;
PSGetNameFromPropertyKey(pk, &pkName);
std::wstring prop(pkName.m_pData);
if (prop == NonLocalizable::PackageFullNameProp ||
prop == NonLocalizable::PackageInstallPathProp ||
prop == NonLocalizable::InstallPathProp ||
prop == NonLocalizable::HostEnvironmentProp ||
prop == NonLocalizable::AppUserModelIdProp)
{
PROPVARIANT pv;
PropVariantInit(&pv);
hr = store->GetValue(pk, &pv);
if (SUCCEEDED(hr))
{
if (prop == NonLocalizable::HostEnvironmentProp)
{
CComHeapPtr<int> propVariantInt;
propVariantInt.Allocate(1);
PropVariantToInt32(pv, propVariantInt);
if (prop == NonLocalizable::HostEnvironmentProp)
{
data.canLaunchElevated = *propVariantInt.m_pData != 1;
}
}
else
{
CComHeapPtr<wchar_t> propVariantString;
propVariantString.Allocate(512);
PropVariantToString(pv, propVariantString, 512);
if (prop == NonLocalizable::PackageFullNameProp)
{
data.packageFullName = propVariantString.m_pData;
}
else if (prop == NonLocalizable::AppUserModelIdProp)
{
data.appUserModelId = propVariantString.m_pData;
}
else if (prop == NonLocalizable::PackageInstallPathProp || prop == NonLocalizable::InstallPathProp)
{
data.installPath = propVariantString.m_pData;
}
}
PropVariantClear(&pv);
}
else
{
Logger::error(L"Failed to get property value: {}", get_last_error_or_default(hr));
}
}
}
if (!data.name.empty())
{
result.push_back(data);
}
}
return result;
}
const std::wstring& GetCurrentFolder()
{
static std::wstring currentFolder;
if (currentFolder.empty())
{
TCHAR buffer[MAX_PATH] = { 0 };
GetModuleFileName(NULL, buffer, MAX_PATH);
std::wstring::size_type pos = std::wstring(buffer).find_last_of(L"\\/");
currentFolder = std::wstring(buffer).substr(0, pos);
}
return currentFolder;
}
const std::wstring& GetCurrentFolderUpper()
{
static std::wstring currentFolderUpper;
if (currentFolderUpper.empty())
{
currentFolderUpper = GetCurrentFolder();
std::transform(currentFolderUpper.begin(), currentFolderUpper.end(), currentFolderUpper.begin(), towupper);
}
return currentFolderUpper;
}
AppList GetAppsList()
{
return IterateAppsFolder();
}
std::optional<AppData> GetApp(const std::wstring& appPath, const AppList& apps)
{
std::wstring appPathUpper(appPath);
std::transform(appPathUpper.begin(), appPathUpper.end(), appPathUpper.begin(), towupper);
// edge case, "Windows Software Development Kit" has the same app path as "File Explorer"
if (appPathUpper == NonLocalizable::FileExplorerPath)
{
return AppData{
.name = NonLocalizable::FileExplorerName,
.installPath = appPath,
};
}
// PowerToys
if (appPathUpper.contains(GetCurrentFolderUpper()))
{
if (appPathUpper.contains(NonLocalizable::PowerToysSettingsUpper))
{
return AppData{
.name = NonLocalizable::PowerToysSettings,
.installPath = GetCurrentFolder() + L"\\" + NonLocalizable::PowerToys
};
}
else
{
return AppData{
.name = std::filesystem::path(appPath).stem(),
.installPath = appPath,
};
}
}
for (const auto& appData : apps)
{
if (!appData.installPath.empty())
{
std::wstring installPathUpper(appData.installPath);
std::transform(installPathUpper.begin(), installPathUpper.end(), installPathUpper.begin(), towupper);
if (appPathUpper.contains(installPathUpper))
{
return appData;
}
// edge case, some apps (e.g., Gitkraken) have different .exe files in the subfolders.
// apps list contains only one path, so in this case app is not found
if (std::filesystem::path(appPath).filename() == std::filesystem::path(appData.installPath).filename())
{
return appData;
}
}
}
return AppData{
.installPath = appPath
};
}
std::optional<AppData> GetApp(HWND window, const AppList& apps)
{
std::wstring processPath = get_process_path(window);
return Utils::Apps::GetApp(processPath, apps);
}
}
}