Merge branch 'dev/feature/projects' of https://github.com/microsoft/PowerToys into dev/feature/projects

This commit is contained in:
donlaci
2024-07-09 15:22:57 +02:00
33 changed files with 777 additions and 297 deletions

View File

@@ -57,6 +57,7 @@ appdata
APPEXECLINK
Appium
Applicationcan
APPLICATIONFRAMEHOST
appmanifest
APPNAME
appref

View File

@@ -605,6 +605,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "projects-common", "projects
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProjectsModuleInterface", "src\modules\Projects\ProjectsModuleInterface\ProjectsModuleInterface.vcxproj", "{45285DF2-9742-4ECA-9AC9-58951FC26489}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProjectsLib", "src\modules\Projects\ProjectsLib\ProjectsLib.vcxproj", "{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -3423,6 +3425,22 @@ Global
{45285DF2-9742-4ECA-9AC9-58951FC26489}.Release|x64.Build.0 = Release|x64
{45285DF2-9742-4ECA-9AC9-58951FC26489}.Release|x86.ActiveCfg = Release|x64
{45285DF2-9742-4ECA-9AC9-58951FC26489}.Release|x86.Build.0 = Release|x64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|Any CPU.ActiveCfg = Debug|x64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|Any CPU.Build.0 = Debug|x64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|ARM64.ActiveCfg = Debug|ARM64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|ARM64.Build.0 = Debug|ARM64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|x64.ActiveCfg = Debug|x64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|x64.Build.0 = Debug|x64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|x86.ActiveCfg = Debug|x64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|x86.Build.0 = Debug|x64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|Any CPU.ActiveCfg = Release|x64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|Any CPU.Build.0 = Release|x64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|ARM64.ActiveCfg = Release|ARM64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|ARM64.Build.0 = Release|ARM64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|x64.ActiveCfg = Release|x64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|x64.Build.0 = Release|x64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|x86.ActiveCfg = Release|x64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Release|x86.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -3645,6 +3663,7 @@ Global
{3D63307B-9D27-44FD-B033-B26F39245B85} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{BE126CBB-AE12-406A-9837-A05ACFCA57A7} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{45285DF2-9742-4ECA-9AC9-58951FC26489} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

View File

@@ -0,0 +1,88 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using Projects.Data;
using static ProjectsEditor.Data.ProjectData;
namespace ProjectsEditor.Data
{
public class ProjectData : ProjectsEditorData<ProjectWrapper>
{
public struct ApplicationWrapper
{
public struct WindowPositionWrapper
{
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
public string Application { get; set; }
public string ApplicationPath { get; set; }
public string Title { get; set; }
public string PackageFullName { get; set; }
public string CommandLineArguments { get; set; }
public bool Minimized { get; set; }
public bool Maximized { get; set; }
public WindowPositionWrapper Position { get; set; }
public int Monitor { get; set; }
}
public struct MonitorConfigurationWrapper
{
public struct MonitorRectWrapper
{
public int Top { get; set; }
public int Left { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
public string Id { get; set; }
public string InstanceId { get; set; }
public int MonitorNumber { get; set; }
public int Dpi { get; set; }
public MonitorRectWrapper MonitorRectDpiAware { get; set; }
public MonitorRectWrapper MonitorRectDpiUnaware { get; set; }
}
public struct ProjectWrapper
{
public string Id { get; set; }
public string Name { get; set; }
public long CreationTime { get; set; }
public long LastLaunchedTime { get; set; }
public bool IsShortcutNeeded { get; set; }
public List<MonitorConfigurationWrapper> MonitorConfiguration { get; set; }
public List<ApplicationWrapper> Applications { get; set; }
}
}
}

View File

@@ -5,6 +5,7 @@
using System.Collections.Generic;
using Projects.Data;
using ProjectsEditor.Utils;
using static ProjectsEditor.Data.ProjectData;
using static ProjectsEditor.Data.ProjectsData;
namespace ProjectsEditor.Data
@@ -19,81 +20,6 @@ namespace ProjectsEditor.Data
}
}
public struct ApplicationWrapper
{
public struct WindowPositionWrapper
{
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
public string Application { get; set; }
public string ApplicationPath { get; set; }
public string Title { get; set; }
public string PackageFullName { get; set; }
public string CommandLineArguments { get; set; }
public bool Minimized { get; set; }
public bool Maximized { get; set; }
public WindowPositionWrapper Position { get; set; }
public int Monitor { get; set; }
}
public struct MonitorConfigurationWrapper
{
public struct MonitorRectWrapper
{
public int Top { get; set; }
public int Left { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
public string Id { get; set; }
public string InstanceId { get; set; }
public int MonitorNumber { get; set; }
public int Dpi { get; set; }
public MonitorRectWrapper MonitorRectDpiAware { get; set; }
public MonitorRectWrapper MonitorRectDpiUnaware { get; set; }
}
public struct ProjectWrapper
{
public string Id { get; set; }
public string Name { get; set; }
public long CreationTime { get; set; }
public long LastLaunchedTime { get; set; }
public bool IsShortcutNeeded { get; set; }
public List<MonitorConfigurationWrapper> MonitorConfiguration { get; set; }
public List<ApplicationWrapper> Applications { get; set; }
}
public struct ProjectsListWrapper
{
public List<ProjectWrapper> Projects { get; set; }

View File

@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using ProjectsEditor.Utils;
namespace ProjectsEditor.Data
{
public class TempProjectData : ProjectData
{
public string File
{
get
{
return FolderUtils.DataFolder() + "\\temp-project.json";
}
}
public void DeleteTempFile()
{
if (System.IO.File.Exists(File))
{
System.IO.File.Delete(File);
}
}
}
}

View File

@@ -6,6 +6,7 @@ using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using ProjectsEditor.Data;
using ProjectsEditor.Models;
using ProjectsEditor.ViewModels;
@@ -27,13 +28,24 @@ namespace ProjectsEditor
private void SaveButtonClicked(object sender, RoutedEventArgs e)
{
Project projectToSave = this.DataContext as Project;
_mainViewModel.SaveProject(projectToSave);
if (projectToSave.EditorWindowTitle == Properties.Resources.CreateProject)
{
_mainViewModel.AddNewProject(projectToSave);
}
else
{
_mainViewModel.SaveProject(projectToSave);
}
_mainViewModel.SwitchToMainView();
}
private void CancelButtonClicked(object sender, RoutedEventArgs e)
{
_mainViewModel.CancelLastEdit();
// delete the temp file created by the snapshot tool
TempProjectData parser = new TempProjectData();
parser.DeleteTempFile();
_mainViewModel.SwitchToMainView();
}

View File

@@ -150,6 +150,15 @@ namespace ProjectsEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Project.
/// </summary>
public static string DefaultProjectNamePrefix {
get {
return ResourceManager.GetString("DefaultProjectNamePrefix", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Remove.
/// </summary>

View File

@@ -147,6 +147,9 @@
<data name="DaysAgo" xml:space="preserve">
<value>days ago</value>
</data>
<data name="DefaultProjectNamePrefix" xml:space="preserve">
<value>Project</value>
</data>
<data name="Delete" xml:space="preserve">
<value>Remove</value>
</data>

View File

@@ -2,19 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using ProjectsEditor.ViewModels;
namespace ProjectsEditor
@@ -41,7 +29,7 @@ namespace ProjectsEditor
private void SnapshotButtonClicked(object sender, RoutedEventArgs e)
{
Close();
_mainViewModel.AddNewProject();
_mainViewModel.SnapNewProject();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)

View File

@@ -46,24 +46,20 @@ namespace ProjectsEditor.Utils
}
}
public ParsingResult ParseProject(string fileName, out Project project)
public ParsingResult ParseTempProject(out Project project)
{
project = null;
try
{
ProjectsData parser = new ProjectsData();
if (!File.Exists(fileName))
TempProjectData parser = new TempProjectData();
if (!File.Exists(parser.File))
{
Logger.LogWarning($"ParseProject method. Projects storage file not found: {parser.File}");
return new ParsingResult(true);
return new ParsingResult(false);
}
ProjectsData.ProjectsListWrapper projects = parser.Read(fileName);
if (!ExtractProject(projects, out project))
{
Logger.LogWarning($"ParseProject method. Projects storage file content could not be set. Reason: {Properties.Resources.Error_Parsing_Message}");
return new ParsingResult(false, ProjectsEditor.Properties.Resources.Error_Parsing_Message);
}
project = GetProjectFromWrapper(parser.Read(parser.File));
parser.DeleteTempFile();
return new ParsingResult(true);
}
@@ -74,25 +70,7 @@ namespace ProjectsEditor.Utils
}
}
private bool ExtractProject(ProjectsData.ProjectsListWrapper projects, out Project project)
{
project = null;
if (projects.Projects == null)
{
return false;
}
if (projects.Projects.Count != 1)
{
return false;
}
ProjectsData.ProjectWrapper projectWrapper = projects.Projects[0];
project = GetProjectFromWrapper(projectWrapper);
return true;
}
private Project GetProjectFromWrapper(ProjectsData.ProjectWrapper project)
private Project GetProjectFromWrapper(ProjectData.ProjectWrapper project)
{
Project newProject = new Project()
{
@@ -143,24 +121,24 @@ namespace ProjectsEditor.Utils
{
ProjectsData serializer = new ProjectsData();
ProjectsData.ProjectsListWrapper projectsWrapper = new ProjectsData.ProjectsListWrapper { };
projectsWrapper.Projects = new List<ProjectsData.ProjectWrapper>();
projectsWrapper.Projects = new List<ProjectData.ProjectWrapper>();
foreach (Project project in projects)
{
ProjectsData.ProjectWrapper wrapper = new ProjectsData.ProjectWrapper
ProjectData.ProjectWrapper wrapper = new ProjectData.ProjectWrapper
{
Id = project.Id,
Name = project.Name,
CreationTime = project.CreationTime,
IsShortcutNeeded = project.IsShortcutNeeded,
LastLaunchedTime = project.LastLaunchedTime,
Applications = new List<ProjectsData.ApplicationWrapper> { },
MonitorConfiguration = new List<ProjectsData.MonitorConfigurationWrapper> { },
Applications = new List<ProjectData.ApplicationWrapper> { },
MonitorConfiguration = new List<ProjectData.MonitorConfigurationWrapper> { },
};
foreach (var app in project.Applications)
{
wrapper.Applications.Add(new ProjectsData.ApplicationWrapper
wrapper.Applications.Add(new ProjectData.ApplicationWrapper
{
Application = app.AppName,
ApplicationPath = app.AppPath,
@@ -169,7 +147,7 @@ namespace ProjectsEditor.Utils
CommandLineArguments = app.CommandLineArguments,
Maximized = app.Maximized,
Minimized = app.Minimized,
Position = new ProjectsData.ApplicationWrapper.WindowPositionWrapper
Position = new ProjectData.ApplicationWrapper.WindowPositionWrapper
{
X = app.Position.X,
Y = app.Position.Y,
@@ -182,20 +160,20 @@ namespace ProjectsEditor.Utils
foreach (var monitor in project.Monitors)
{
wrapper.MonitorConfiguration.Add(new ProjectsData.MonitorConfigurationWrapper
wrapper.MonitorConfiguration.Add(new ProjectData.MonitorConfigurationWrapper
{
Id = monitor.MonitorName,
InstanceId = monitor.MonitorInstanceId,
MonitorNumber = monitor.MonitorNumber,
Dpi = monitor.Dpi,
MonitorRectDpiAware = new ProjectsData.MonitorConfigurationWrapper.MonitorRectWrapper
MonitorRectDpiAware = new ProjectData.MonitorConfigurationWrapper.MonitorRectWrapper
{
Left = (int)monitor.MonitorDpiAwareBounds.Left,
Top = (int)monitor.MonitorDpiAwareBounds.Top,
Width = (int)monitor.MonitorDpiAwareBounds.Width,
Height = (int)monitor.MonitorDpiAwareBounds.Height,
},
MonitorRectDpiUnaware = new ProjectsData.MonitorConfigurationWrapper.MonitorRectWrapper
MonitorRectDpiUnaware = new ProjectData.MonitorConfigurationWrapper.MonitorRectWrapper
{
Left = (int)monitor.MonitorDpiUnawareBounds.Left,
Top = (int)monitor.MonitorDpiUnawareBounds.Top,

View File

@@ -8,9 +8,9 @@ using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Timers;
using ManagedCommon;
@@ -127,7 +127,6 @@ namespace ProjectsEditor.ViewModels
}
private Project editedProject;
private bool isEditedProjectNewlyCreated;
private string projectNameBeingEdited;
private MainWindow _mainWindow;
private System.Timers.Timer lastUpdatedTimer;
@@ -218,45 +217,62 @@ namespace ProjectsEditor.ViewModels
project.Name = projectNameBeingEdited;
}
public async void AddNewProject()
public async void SnapNewProject()
{
CancelSnapshot();
await Task.Run(() => RunSnapshotTool());
if (_projectsEditorIO.ParseProjects(this).Result == true && Projects.Count != 0)
{
int repeatCounter = 1;
string newName = Projects.Count != 0 ? Projects.Last().Name : "Project 1"; // TODO: localizable project name
while (Projects.Where(x => x.Name.Equals(Projects.Last().Name, StringComparison.Ordinal)).Count() > 1)
{
Projects.Last().Name = $"{newName} ({repeatCounter})";
repeatCounter++;
}
_projectsEditorIO.SerializeProjects(Projects.ToList());
EditProject(Projects.Last(), true);
await Task.Run(() => RunSnapshotTool());
Project project = new Project();
if (_projectsEditorIO.ParseTempProject(out project).Result)
{
EditProject(project, true);
}
}
public void EditProject(Project selectedProject, bool isNewlyCreated = false)
{
isEditedProjectNewlyCreated = isNewlyCreated;
var editPage = new ProjectEditor(this);
SetEditedProject(selectedProject);
Project projectEdited = new Project(selectedProject) { EditorWindowTitle = isNewlyCreated ? Properties.Resources.CreateProject : Properties.Resources.EditProject };
projectEdited.Initialize();
editPage.DataContext = projectEdited;
if (isNewlyCreated)
{
// generate a default name for the new project
string defaultNamePrefix = Properties.Resources.DefaultProjectNamePrefix;
int nextProjectIndex = 0;
foreach (var proj in Projects)
{
if (proj.Name.StartsWith(defaultNamePrefix, StringComparison.CurrentCulture))
{
try
{
int index = int.Parse(proj.Name[(defaultNamePrefix.Length + 1)..], CultureInfo.CurrentCulture);
if (nextProjectIndex < index)
{
nextProjectIndex = index;
}
}
catch (Exception)
{
}
}
}
selectedProject.Name = defaultNamePrefix + " " + (nextProjectIndex + 1).ToString(CultureInfo.CurrentCulture);
}
selectedProject.EditorWindowTitle = isNewlyCreated ? Properties.Resources.CreateProject : Properties.Resources.EditProject;
selectedProject.Initialize();
editPage.DataContext = selectedProject;
_mainWindow.ShowPage(editPage);
lastUpdatedTimer.Stop();
}
public void CancelLastEdit()
public void AddNewProject(Project project)
{
if (isEditedProjectNewlyCreated)
{
Projects.Remove(editedProject);
_projectsEditorIO.SerializeProjects(Projects.ToList());
}
Projects.Add(project);
_projectsEditorIO.SerializeProjects(Projects.ToList());
}
public void DeleteProject(Project selectedProject)

View File

@@ -5,6 +5,7 @@
#include <winrt/Windows.ApplicationModel.Core.h>
#include <shellapi.h>
#include <ShellScalingApi.h>
#include <projects-common/AppUtils.h>
#include <projects-common/MonitorEnumerator.h>

View File

@@ -3,6 +3,9 @@
<!-- Project configurations -->
<!-- Props that should be disabled while building on CI server -->
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h ProjectLauncherResource.base.rc ProjectLauncherResource.rc" />
</Target>
<!-- C++ source compile-specific things for all configurations -->
<ItemDefinitionGroup>
<ClCompile>
@@ -143,15 +146,19 @@
<ProjectReference Include="..\..\..\common\Display\Display.vcxproj">
<Project>{caba8dfb-823b-4bf2-93ac-3f31984150d9}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
<ProjectReference Include="..\ProjectsLib\ProjectsLib.vcxproj">
<Project>{b31fcc55-b5a4-4ea7-b414-2dceae6af332}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ProjectLauncherResource.rc" />
<ResourceCompile Include="Generated Files/ProjectLauncherResource.rc" />
<None Include="ProjectLauncherResource.base.rc" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resource.resx" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />

View File

@@ -46,8 +46,13 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ProjectLauncherResource.rc">
<ResourceCompile Include="ProjectLauncherResource.base.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resource.resx">
<Filter>Resource Files</Filter>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{b31fcc55-b5a4-4ea7-b414-2dceae6af332}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>ProjectsLib</RootNamespace>
<ProjectName>ProjectsLib</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\;..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,5 @@
// pch.cpp: source file corresponding to the pre-compiled header
#include "pch.h"
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.

View File

@@ -0,0 +1,10 @@
// pch.h: This is a precompiled header file.
// Files listed below are compiled only once, improving build performance for future builds.
// This also affects IntelliSense performance, including code completion and many code browsing features.
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
// Do not add files here that you will be updating frequently as this negates the performance advantage.
#ifndef PCH_H
#define PCH_H
#endif //PCH_H

View File

@@ -0,0 +1,57 @@
#pragma once
#include <vector>
#include <projects-common/Data.h>
#include <common/logger/logger.h>
namespace ProjectsJsonUtils
{
inline std::vector<Project> Read(const std::wstring& fileName)
{
std::vector<Project> projects{};
try
{
auto savedProjectsJson = json::from_file(fileName);
if (savedProjectsJson.has_value())
{
auto savedProjects = JsonUtils::ProjectsListJSON::FromJson(savedProjectsJson.value());
if (savedProjects.has_value())
{
projects = savedProjects.value();
}
}
}
catch (std::exception ex)
{
Logger::error("Error reading projects file. {}", ex.what());
}
return projects;
}
inline void Write(const std::wstring& fileName, const std::vector<Project>& projects)
{
try
{
json::to_file(fileName, JsonUtils::ProjectsListJSON::ToJson(projects));
}
catch (std::exception ex)
{
Logger::error("Error writing projects file. {}", ex.what());
}
}
inline void Write(const std::wstring& fileName, const Project& project)
{
try
{
json::to_file(fileName, JsonUtils::ProjectJSON::ToJson(project));
}
catch (std::exception ex)
{
Logger::error("Error writing projects file. {}", ex.what());
}
}
}

View File

@@ -3,6 +3,9 @@
<!-- Project configurations -->
<!-- Props that should be disabled while building on CI server -->
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h ProjectsSnapshotToolResources.base.rc ProjectsSnapshotToolResources.rc" />
</Target>
<!-- C++ source compile-specific things for all configurations -->
<ItemDefinitionGroup>
<ClCompile>
@@ -127,10 +130,14 @@
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="SnapshotUtils.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="JsonUtils.h" />
<ClInclude Include="NameUtils.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="resource.base.h" />
<ClInclude Include="SnapshotUtils.h" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
@@ -139,15 +146,19 @@
<ProjectReference Include="..\..\..\common\Display\Display.vcxproj">
<Project>{caba8dfb-823b-4bf2-93ac-3f31984150d9}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
<ProjectReference Include="..\ProjectsLib\ProjectsLib.vcxproj">
<Project>{b31fcc55-b5a4-4ea7-b414-2dceae6af332}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ProjectsSnapshotTool.rc" />
<ResourceCompile Include="Generated Files/ProjectsSnapshotToolResources.rc" />
<None Include="ProjectsSnapshotToolResources.base.rc" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resource.resx" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />

View File

@@ -18,7 +18,16 @@
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<ClInclude Include="NameUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="JsonUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SnapshotUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.base.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
@@ -29,12 +38,23 @@
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SnapshotUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="ProjectsSnapshotToolResources.base.rc">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ProjectsSnapshotTool.rc">
<EmbeddedResource Include="Resource.resx">
<Filter>Resource Files</Filter>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files/ProjectsSnapshotToolResources.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>

View File

@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="DefaultProjectNamePrefix" xml:space="preserve">
<value>Project</value>
</data>
</root>

View File

@@ -0,0 +1,73 @@
#include "pch.h"
#include "SnapshotUtils.h"
#include <Psapi.h>
#include <projects-common/AppUtils.h>
#include <projects-common/WindowEnumerator.h>
#include <projects-common/WindowFilter.h>
#include <common/utils/process_path.h>
#include <TlHelp32.h>
namespace SnapshotUtils
{
std::vector<Project::Application> GetApps(const std::function<unsigned int(HWND)> getMonitorNumberFromWindowHandle)
{
std::vector<Project::Application> apps{};
auto installedApps = Utils::Apps::GetAppsList();
auto windows = WindowEnumerator::Enumerate(WindowFilter::Filter);
for (const auto window : windows)
{
// filter by window rect size
RECT rect = WindowUtils::GetWindowRect(window);
if (rect.right - rect.left <= 0 || rect.bottom - rect.top <= 0)
{
continue;
}
// filter by window title
std::wstring title = WindowUtils::GetWindowTitle(window);
if (title.empty())
{
continue;
}
// filter by app path
std::wstring processPath = get_process_path_waiting_uwp(window);
if (processPath.empty() || WindowUtils::IsExcludedByDefault(window, processPath, title))
{
continue;
}
auto data = Utils::Apps::GetApp(processPath, installedApps);
if (!data.has_value() || data->name.empty())
{
continue;
}
Project::Application app{
.name = data.value().name,
.title = title,
.path = processPath,
.packageFullName = data.value().packageFullName,
.commandLineArgs = L"",
.isMinimized = WindowUtils::IsMinimized(window),
.isMaximized = WindowUtils::IsMaximized(window),
.position = Project::Application::Position{
.x = rect.left,
.y = rect.top,
.width = rect.right - rect.left,
.height = rect.bottom - rect.top,
},
.monitor = getMonitorNumberFromWindowHandle(window),
};
apps.push_back(app);
}
return apps;
}
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include <projects-common/Data.h>
namespace SnapshotUtils
{
std::vector<Project::Application> GetApps(const std::function<unsigned int(HWND)> getMonitorNumberFromWindowHandle);
};

View File

@@ -1,14 +1,13 @@
#include "pch.h"
#include <chrono>
#include <iostream>
#include <projects-common/AppUtils.h>
#include <projects-common/Data.h>
#include <projects-common/GuidUtils.h>
#include <projects-common/MonitorUtils.h>
#include <projects-common/WindowEnumerator.h>
#include <projects-common/WindowFilter.h>
#include <JsonUtils.h>
#include <SnapshotUtils.h>
#include <common/utils/gpo.h>
#include <common/utils/logger_helper.h>
@@ -45,88 +44,13 @@ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdLine, int cm
fileName = fileNameParam;
}
// read previously saved projects
std::vector<Project> projects;
try
{
auto savedProjectsJson = json::from_file(fileName);
if (savedProjectsJson.has_value())
{
auto savedProjects = JsonUtils::ProjectsListJSON::FromJson(savedProjectsJson.value());
if (savedProjects.has_value())
{
projects = savedProjects.value();
}
}
}
catch (std::exception ex)
{
Logger::error("Error reading projects file. {}", ex.what());
}
// new project name
std::wstring defaultNamePrefix = L"Project"; // TODO: localizable
int nextProjectIndex = 0;
for (const auto& proj : projects)
{
const std::wstring& name = proj.name;
if (name.starts_with(defaultNamePrefix))
{
try
{
int index = std::stoi(name.substr(defaultNamePrefix.length() + 1));
if (nextProjectIndex < index)
{
nextProjectIndex = index;
}
}
catch (std::exception) {}
}
}
std::wstring projectName = defaultNamePrefix + L" " + std::to_wstring(nextProjectIndex + 1);
// create new project
time_t creationTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
Project project{ .id = CreateGuidString(), .name = projectName, .creationTime = creationTime };
Project project{ .id = CreateGuidString(), .creationTime = creationTime };
Logger::trace(L"Creating project {}:{}", project.name, project.id);
// save monitor configuration
project.monitors = MonitorUtils::IdentifyMonitors();
// get list of windows
auto windows = WindowEnumerator::Enumerate(WindowFilter::Filter);
// get installed apps list
auto apps = Utils::Apps::GetAppsList();
for (const auto& window : windows)
{
// filter by window rect size
RECT rect = WindowUtils::GetWindowRect(window);
if (rect.right - rect.left <= 0 || rect.bottom - rect.top <= 0)
{
continue;
}
// filter by window title
std::wstring title = WindowUtils::GetWindowTitle(window);
if (title.empty())
{
continue;
}
// filter by app path
std::wstring processPath = get_process_path_waiting_uwp(window);
if (processPath.empty() || WindowUtils::IsExcludedByDefault(window, processPath, title))
{
continue;
}
auto data = Utils::Apps::GetApp(processPath, apps);
if (!data.has_value())
{
continue;
}
project.apps = SnapshotUtils::GetApps([&](HWND window) -> unsigned int {
auto windowMonitor = MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY);
unsigned int monitorNumber = 0;
for (const auto& monitor : project.monitors)
@@ -138,28 +62,11 @@ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdLine, int cm
}
}
Project::Application app {
.name = data.value().name,
.title = title,
.path = processPath,
.packageFullName = data.value().packageFullName,
.commandLineArgs = L"",
.isMinimized = WindowUtils::IsMinimized(window),
.isMaximized = WindowUtils::IsMaximized(window),
.position = Project::Application::Position {
.x = rect.left,
.y = rect.top,
.width = rect.right - rect.left,
.height = rect.bottom - rect.top,
},
.monitor = monitorNumber,
};
return monitorNumber;
});
project.apps.push_back(app);
}
projects.push_back(project);
json::to_file(fileName, JsonUtils::ProjectsListJSON::ToJson(projects));
ProjectsJsonUtils::Write(JsonUtils::TempProjectsFile(), project);
Logger::trace(L"Project {}:{} created", project.name, project.id);
return 0;
}

View File

@@ -16,12 +16,12 @@ namespace Utils
{
namespace NonLocalizable
{
const wchar_t* PackageFullNameProp = L"System.AppUserModel.PackageFullName";
const wchar_t* PackageInstallPathProp = L"System.AppUserModel.PackageInstallPath";
const wchar_t* InstallPathProp = L"System.Link.TargetParsingPath";
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";
const wchar_t* FileExplorerName = L"File Explorer";
const wchar_t* FileExplorerPath = L"C:\\WINDOWS\\EXPLORER.EXE";
constexpr const wchar_t* FileExplorerName = L"File Explorer";
constexpr const wchar_t* FileExplorerPath = L"C:\\WINDOWS\\EXPLORER.EXE";
}
struct AppData

View File

@@ -81,6 +81,12 @@ namespace JsonUtils
return std::wstring(settingsFolderPath) + L"\\projects.json";
}
inline std::wstring TempProjectsFile()
{
std::wstring settingsFolderPath = PTSettingsHelper::get_module_save_folder_location(NonLocalizable::ModuleKey);
return std::wstring(settingsFolderPath) + L"\\temp-project.json";
}
namespace ProjectJSON
{
namespace ApplicationJSON

View File

@@ -1,28 +1,23 @@
#pragma once
#include <Windows.h>
#include <ShellScalingApi.h>
#include <algorithm>
#include <common/Display/dpi_aware.h>
#include <common/utils/excluded_apps.h>
#include <common/utils/window.h>
// FancyZones WindowUtils
namespace WindowUtils
{
// Non-Localizable strings
namespace NonLocalizable
{
const wchar_t SystemAppsFolder[] = L"SYSTEMAPPS";
const wchar_t System[] = L"WINDOWS/SYSTEM";
const wchar_t System32[] = L"SYSTEM32";
const wchar_t SystemWOW64[] = L"SYSTEMWOW64";
const char SplashClassName[] = "MsoSplash";
const wchar_t SystemAppsFolder[] = L"SYSTEMAPPS";
const wchar_t CoreWindow[] = L"WINDOWS.UI.CORE.COREWINDOW";
const wchar_t SearchUI[] = L"SEARCHUI.EXE";
const wchar_t HelpWindow[] = L"C:\\WINDOWS\\HH.EXE";
const wchar_t HelpWindow[] = L"WINDOWS\\HH.EXE";
const wchar_t ApplicationFrameHost[] = L"WINDOWS\\SYSTEM32\\APPLICATIONFRAMEHOST.EXE";
const wchar_t ProjectsSnapshotTool[] = L"POWERTOYS.PROJECTSSNAPSHOTTOOL";
const wchar_t ProjectsEditor[] = L"POWERTOYS.PROJECTSEDITOR";
const wchar_t ProjectsLauncher[] = L"POWERTOYS.PROJECTSLAUNCHER";
@@ -33,6 +28,11 @@ namespace WindowUtils
return GetAncestor(window, GA_ROOT) == window;
}
inline bool IsMinimized(HWND window)
{
return IsIconic(window);
}
inline bool IsMaximized(HWND window) noexcept
{
WINDOWPLACEMENT placement{};
@@ -55,10 +55,7 @@ namespace WindowUtils
CharUpperBuffW(processPathUpper.data(), static_cast<DWORD>(processPathUpper.length()));
static std::vector<std::wstring> defaultExcludedFolders = {
NonLocalizable::SystemAppsFolder,
NonLocalizable::System,
NonLocalizable::System32,
NonLocalizable::SystemWOW64
NonLocalizable::SystemAppsFolder,
};
if (find_folder_in_path(processPathUpper, defaultExcludedFolders))
{
@@ -109,15 +106,6 @@ namespace WindowUtils
return rect;
}
}
// addition for Projects
namespace WindowUtils
{
inline bool IsMinimized(HWND window)
{
return IsIconic(window);
}
#define MAX_TITLE_LENGTH 255
inline std::wstring GetWindowTitle(HWND window)