mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-07 12:57:03 +01:00
Compare commits
11 Commits
dev/crloew
...
dev/addNew
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78ed5c7966 | ||
|
|
3dc491339a | ||
|
|
271d64b8dc | ||
|
|
a90b646e72 | ||
|
|
084402a2bd | ||
|
|
08fe8089b8 | ||
|
|
53212188b7 | ||
|
|
7c9876582c | ||
|
|
6cece12b85 | ||
|
|
863f7aa233 | ||
|
|
b81478eb97 |
1
.github/actions/spell-check/allow/names.txt
vendored
1
.github/actions/spell-check/allow/names.txt
vendored
@@ -71,6 +71,7 @@ Garside
|
||||
Gershaft
|
||||
Giordani
|
||||
Gokce
|
||||
grzhan
|
||||
Guo
|
||||
hanselman
|
||||
Harmath
|
||||
|
||||
28
.github/actions/spell-check/expect.txt
vendored
28
.github/actions/spell-check/expect.txt
vendored
@@ -69,11 +69,9 @@ AQS
|
||||
ARandom
|
||||
ARCHITEW
|
||||
ARemapped
|
||||
ari
|
||||
ARPINSTALLLOCATION
|
||||
ARPPRODUCTICON
|
||||
ARRAYSIZE
|
||||
arw
|
||||
asf
|
||||
AShortcut
|
||||
ASingle
|
||||
@@ -104,6 +102,7 @@ backtracer
|
||||
bbwe
|
||||
bck
|
||||
BESTEFFORT
|
||||
bezelled
|
||||
bhid
|
||||
BIF
|
||||
bigbar
|
||||
@@ -261,7 +260,6 @@ critsec
|
||||
Crossdevice
|
||||
CRSEL
|
||||
crx
|
||||
crw
|
||||
CSearch
|
||||
CSettings
|
||||
cso
|
||||
@@ -303,8 +301,8 @@ DCOM
|
||||
dcommon
|
||||
dcomp
|
||||
DComposition
|
||||
dcr
|
||||
dcs
|
||||
DCR
|
||||
DCs
|
||||
ddd
|
||||
DDEIf
|
||||
DDevice
|
||||
@@ -374,7 +372,6 @@ DRAWCLIPBOARD
|
||||
DRAWFRAME
|
||||
drawingcolor
|
||||
dreamsofameaningfullife
|
||||
drf
|
||||
drivedetectionwarning
|
||||
dshow
|
||||
DSTINVERT
|
||||
@@ -418,7 +415,6 @@ editkeyboardwindow
|
||||
EDITSHORTCUTS
|
||||
editshortcutswindow
|
||||
EFile
|
||||
eip
|
||||
ekus
|
||||
emmintrin
|
||||
Emoji
|
||||
@@ -592,7 +588,7 @@ Hiberboot
|
||||
HIBYTE
|
||||
hicon
|
||||
HIDEWINDOW
|
||||
hif
|
||||
Hif
|
||||
HIMAGELIST
|
||||
himl
|
||||
hinst
|
||||
@@ -668,7 +664,6 @@ IGNOREUNKNOWN
|
||||
IGraphics
|
||||
iid
|
||||
Iindex
|
||||
iiq
|
||||
IJson
|
||||
Ijwhost
|
||||
IKs
|
||||
@@ -731,6 +726,7 @@ ISSEPARATOR
|
||||
ITask
|
||||
ith
|
||||
ITHUMBNAIL
|
||||
ITwoWayPipeMessageIPCManaged
|
||||
IUI
|
||||
IUnknown
|
||||
IUse
|
||||
@@ -739,10 +735,8 @@ IWeb
|
||||
IWIC
|
||||
iwr
|
||||
IYUV
|
||||
jfi
|
||||
jfif
|
||||
jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi
|
||||
jif
|
||||
jjw
|
||||
jobject
|
||||
jpe
|
||||
@@ -751,7 +745,6 @@ Jsons
|
||||
jsonval
|
||||
junja
|
||||
jxr
|
||||
kdc
|
||||
keybd
|
||||
KEYBDDATA
|
||||
KEYBDINPUT
|
||||
@@ -876,7 +869,6 @@ MAXIMIZEBOX
|
||||
MAXSHORTCUTSIZE
|
||||
maxversiontested
|
||||
MBR
|
||||
mdc
|
||||
MDICHILD
|
||||
MDL
|
||||
mdtext
|
||||
@@ -884,7 +876,6 @@ mdtxt
|
||||
mdwn
|
||||
MEDIASUBTYPE
|
||||
mediatype
|
||||
mef
|
||||
MENUITEMINFO
|
||||
MENUITEMINFOW
|
||||
MERGECOPY
|
||||
@@ -942,7 +933,6 @@ mpmc
|
||||
MRM
|
||||
MRT
|
||||
mru
|
||||
mrw
|
||||
msc
|
||||
mscorlib
|
||||
msdata
|
||||
@@ -1007,6 +997,7 @@ newitem
|
||||
newpath
|
||||
newplus
|
||||
NEWPLUSCONTEXTMENU
|
||||
NEWPLUSSHELLEXTENSIONWIN
|
||||
newrow
|
||||
newsgroups
|
||||
NIF
|
||||
@@ -1065,7 +1056,6 @@ NOZORDER
|
||||
NPH
|
||||
npmjs
|
||||
NResize
|
||||
nrw
|
||||
nsunt
|
||||
NTAPI
|
||||
ntdll
|
||||
@@ -1095,7 +1085,6 @@ opensource
|
||||
openxmlformats
|
||||
OPTIMIZEFORINVOKE
|
||||
ORAW
|
||||
ori
|
||||
ORPHANEDDIALOGTITLE
|
||||
ORSCANS
|
||||
oss
|
||||
@@ -1180,6 +1169,7 @@ pnid
|
||||
Pnp
|
||||
Popups
|
||||
POPUPWINDOW
|
||||
POSITIONITEM
|
||||
POWERRENAMECONTEXTMENU
|
||||
powerrenameinput
|
||||
POWERRENAMETEST
|
||||
@@ -1274,7 +1264,6 @@ QUERYENDSESSION
|
||||
QUERYOPEN
|
||||
QUEUESYNC
|
||||
QUNS
|
||||
raf
|
||||
RAII
|
||||
RAlt
|
||||
Rasterize
|
||||
@@ -1378,8 +1367,6 @@ runtimes
|
||||
ruuid
|
||||
rvm
|
||||
rwin
|
||||
rwl
|
||||
rwz
|
||||
sacl
|
||||
safeprojectname
|
||||
SAMEKEYPREVIOUSLYMAPPED
|
||||
@@ -1513,7 +1500,6 @@ Srch
|
||||
SRCINVERT
|
||||
SRCPAINT
|
||||
SResize
|
||||
srf
|
||||
srme
|
||||
srre
|
||||
srw
|
||||
|
||||
@@ -181,6 +181,7 @@
|
||||
|
||||
"WinUI3Apps\\PowerToys.NewPlus.ShellExtension.dll",
|
||||
"WinUI3Apps\\NewPlusPackage.msix",
|
||||
"WinUI3Apps\\PowerToys.NewPlus.ShellExtension.win10.dll",
|
||||
|
||||
"PowerAccent.Core.dll",
|
||||
"PowerToys.PowerAccent.dll",
|
||||
|
||||
0
.pipelines/SDKUpgrade/sdkValidation.yml
Normal file
0
.pipelines/SDKUpgrade/sdkValidation.yml
Normal file
@@ -47,7 +47,7 @@
|
||||
-->
|
||||
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.1.5" />
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.6.240923002" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.6.241114003" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
|
||||
<PackageVersion Include="ModernWpfUI" Version="0.9.4" />
|
||||
@@ -57,7 +57,6 @@
|
||||
<PackageVersion Include="NLog" Version="5.0.4" />
|
||||
<PackageVersion Include="NLog.Extensions.Logging" Version="5.3.8" />
|
||||
<PackageVersion Include="NLog.Schema" Version="5.2.8" />
|
||||
<PackageVersion Include="OpenAI" Version="2.0.0" />
|
||||
<PackageVersion Include="ReverseMarkdown" Version="4.1.0" />
|
||||
<PackageVersion Include="ScipBe.Common.Office.OneNote" Version="3.0.1" />
|
||||
<PackageVersion Include="SharpCompress" Version="0.37.2" />
|
||||
|
||||
@@ -1333,7 +1333,7 @@ EXHIBIT A -Mozilla Public License.
|
||||
- Microsoft.Windows.CsWin32 0.2.46-beta
|
||||
- Microsoft.Windows.CsWinRT 2.1.5
|
||||
- Microsoft.Windows.SDK.BuildTools 10.0.22621.2428
|
||||
- Microsoft.WindowsAppSDK 1.6.240923002
|
||||
- Microsoft.WindowsAppSDK 1.6.241114003
|
||||
- Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9
|
||||
- Microsoft.Xaml.Behaviors.Wpf 1.1.39
|
||||
- ModernWpfUI 0.9.4
|
||||
|
||||
@@ -632,6 +632,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseWithoutBorders.UnitTes
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesCsharpLibrary", "src\modules\Workspaces\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj", "{89D0E199-B17A-418C-B2F8-7375B6708357}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NewPlus.ShellExtension.win10", "src\modules\NewPlus\NewShellExtensionContextMenu.win10\NewPlus.ShellExtension.win10.vcxproj", "{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
@@ -2792,6 +2794,18 @@ Global
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x64.Build.0 = Release|x64
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x86.ActiveCfg = Release|x64
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x86.Build.0 = Release|x64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x64.Build.0 = Debug|x64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x86.Build.0 = Debug|x64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x64.ActiveCfg = Release|x64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x64.Build.0 = Release|x64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x86.ActiveCfg = Release|x64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x86.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -3024,6 +3038,7 @@ Global
|
||||
{8F021B46-362B-485C-BFBA-CCF83E820CBD} = {8F62026A-294B-41C6-8839-87463613F216}
|
||||
{66614C26-314C-4B91-9071-76133422CFEF} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E} = {CA716AE6-FE5C-40AC-BB8F-2C87912687AC}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
||||
|
||||
@@ -41,6 +41,7 @@ Contact the developers of a plugin directly for assistance with a specific plugi
|
||||
| [ChatGPT](https://github.com/ferraridavide/ChatGPTPowerToys) | [ferraridavide](https://github.com/ferraridavide) | Ask a question to ChatGPT |
|
||||
| [CanIUse](https://github.com/skttl/ptrun-caniuse) | [skttl](https://github.com/skttl) | Look up browser feature support with caniuse.com |
|
||||
| [TailwindCSS](https://github.com/skttl/ptrun-tailwindcss) | [skttl](https://github.com/skttl) | Search the documentation of TailwindCSS |
|
||||
| [HttpStatusCodes](https://github.com/grzhan/HttpStatusCodePowerToys) | [grzhan](https://github.com/grzhan) | Search for http status codes |
|
||||
|
||||
## Extending software plugins
|
||||
|
||||
|
||||
@@ -18,6 +18,19 @@
|
||||
<DirectoryRef Id="NewPlusAssetsInstallFolder" FileSource="$(var.NewPlusAssetsFilesPath)">
|
||||
<!-- Generated by generateFileComponents.ps1 -->
|
||||
<!--NewPlusAssetsFiles_Component_Def-->
|
||||
|
||||
<!-- NewPlus Shell Extension for Win10 registration -->
|
||||
<Component Id="NewPlus_ShellExtension_win10" Guid="D5456D4A-6EEC-4B85-944D-6A6A4A74FFA6" Win64="yes">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\CLSID\{FF90D477-E32A-4BE8-8CC5-A502A97F5401}">
|
||||
<RegistryValue Type="string" Value="NewPlus Shell Extension Win10" />
|
||||
<RegistryValue Type="string" Name="ContextMenuOptIn" Value="" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Value="[WinUI3AppsInstallFolder]PowerToys.NewPlus.ShellExtension.win10.dll" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Apartment" />
|
||||
</RegistryKey>
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="SOFTWARE\Classes\Directory\background\ShellEx\ContextMenuHandlers\NewPlusShellExtensionWin10">
|
||||
<RegistryValue Type="string" Value="{FF90D477-E32A-4BE8-8CC5-A502A97F5401}"/>
|
||||
</RegistryKey>
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
|
||||
<ComponentGroup Id="NewPlusComponentGroup">
|
||||
@@ -27,6 +40,7 @@
|
||||
</RegistryKey>
|
||||
<RemoveFolder Id="RemoveFolderNewPlusAssetsFolder" Directory="NewPlusAssetsInstallFolder" On="uninstall"/>
|
||||
</Component>
|
||||
<ComponentRef Id="NewPlus_ShellExtension_win10" />
|
||||
</ComponentGroup>
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ export async function registerAdditionalLanguages(monaco){
|
||||
await languageDefinitions();
|
||||
registerAdditionalLanguage("cppExt", [".ino", ".pde"], "cpp", monaco)
|
||||
registerAdditionalLanguage("xmlExt", [".wsdl", ".csproj", ".vcxproj", ".vbproj", ".fsproj"], "xml", monaco)
|
||||
registerAdditionalLanguage("txtExt", [".sln", ".log", ".vsconfig", ".env", ".srt"], "txt", monaco)
|
||||
registerAdditionalLanguage("txtExt", [".sln", ".log", ".vsconfig", ".env", ".srt", ".ahk"], "txt", monaco)
|
||||
registerAdditionalLanguage("razorExt", [".razor"], "razor", monaco)
|
||||
registerAdditionalLanguage("vbExt", [".vbs"], "vb", monaco)
|
||||
registerAdditionalLanguage("iniExt", [".inf", ".gitconfig", ".gitattributes", ".editorconfig"], "ini", monaco)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -29,34 +29,28 @@ namespace Microsoft.PowerToys.FilePreviewCommon
|
||||
new XmlFormatter(),
|
||||
}.AsReadOnly();
|
||||
|
||||
private static string? _monacoDirectory;
|
||||
private static readonly Lazy<string> _monacoDirectory = new(GetRuntimeMonacoDirectory);
|
||||
|
||||
public static string GetRuntimeMonacoDirectory()
|
||||
/// <summary>
|
||||
/// Gets the path of the Monaco assets folder.
|
||||
/// </summary>
|
||||
public static string MonacoDirectory => _monacoDirectory.Value;
|
||||
|
||||
private static string GetRuntimeMonacoDirectory()
|
||||
{
|
||||
string codeBase = Assembly.GetExecutingAssembly().Location;
|
||||
string path = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(codeBase) ?? string.Empty, "Assets", "Monaco"));
|
||||
if (Path.Exists(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're likely in WinUI3Apps directory and need to go back to the base directory.
|
||||
return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(codeBase) ?? string.Empty, "..", "Assets", "Monaco"));
|
||||
}
|
||||
}
|
||||
string exePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty;
|
||||
|
||||
public static string MonacoDirectory
|
||||
{
|
||||
get
|
||||
// If the executable is within "WinUI3Apps", correct the path first.
|
||||
if (Path.GetFileName(exePath) == "WinUI3Apps")
|
||||
{
|
||||
if (string.IsNullOrEmpty(_monacoDirectory))
|
||||
{
|
||||
_monacoDirectory = GetRuntimeMonacoDirectory();
|
||||
}
|
||||
|
||||
return _monacoDirectory;
|
||||
exePath = Path.Combine(exePath, "..");
|
||||
}
|
||||
|
||||
string monacoPath = Path.Combine(exePath, "Assets", "Monaco");
|
||||
|
||||
return Directory.Exists(monacoPath) ?
|
||||
monacoPath :
|
||||
throw new DirectoryNotFoundException($"Monaco assets directory not found at {monacoPath}");
|
||||
}
|
||||
|
||||
public static JsonDocument GetLanguages()
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using PowerToys.Interop;
|
||||
|
||||
@@ -52,16 +53,18 @@ namespace ManagedCommon
|
||||
Trace.AutoFlush = true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void LogError(string message)
|
||||
{
|
||||
Log(message, Error);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void LogError(string message, Exception ex)
|
||||
{
|
||||
if (ex == null)
|
||||
{
|
||||
LogError(message);
|
||||
Log(message, Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -84,26 +87,31 @@ namespace ManagedCommon
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void LogWarning(string message)
|
||||
{
|
||||
Log(message, Warning);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void LogInfo(string message)
|
||||
{
|
||||
Log(message, Info);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void LogDebug(string message)
|
||||
{
|
||||
Log(message, Debug);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void LogTrace()
|
||||
{
|
||||
Log(string.Empty, TraceFlag);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void Log(string message, string type)
|
||||
{
|
||||
Trace.WriteLine("[" + DateTime.Now.TimeOfDay + "] [" + type + "] " + GetCallerInfo());
|
||||
@@ -116,13 +124,49 @@ namespace ManagedCommon
|
||||
Trace.Unindent();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static string GetCallerInfo()
|
||||
{
|
||||
StackTrace stackTrace = new();
|
||||
|
||||
var methodName = stackTrace.GetFrame(3)?.GetMethod();
|
||||
var className = methodName?.DeclaringType.Name;
|
||||
return className + "::" + methodName?.Name;
|
||||
var callerMethod = GetCallerMethod(stackTrace);
|
||||
|
||||
return $"{callerMethod?.DeclaringType?.Name}::{callerMethod.Name}";
|
||||
}
|
||||
|
||||
private static MethodBase GetCallerMethod(StackTrace stackTrace)
|
||||
{
|
||||
const int topFrame = 3;
|
||||
|
||||
var topMethod = stackTrace.GetFrame(topFrame)?.GetMethod();
|
||||
|
||||
try
|
||||
{
|
||||
if (topMethod?.Name == nameof(IAsyncStateMachine.MoveNext) && typeof(IAsyncStateMachine).IsAssignableFrom(topMethod?.DeclaringType))
|
||||
{
|
||||
// Async method; return actual method as determined by heuristic:
|
||||
// "Nearest method on stack to async state-machine's MoveNext() in same namespace but in a different type".
|
||||
// There are tighter ways of determining the actual method, but this is good enough and probably faster.
|
||||
for (int deepFrame = topFrame + 1; deepFrame < stackTrace.FrameCount; deepFrame++)
|
||||
{
|
||||
var deepMethod = stackTrace.GetFrame(deepFrame)?.GetMethod();
|
||||
|
||||
if (deepMethod?.DeclaringType != topMethod?.DeclaringType && deepMethod?.DeclaringType?.Namespace == topMethod?.DeclaringType?.Namespace)
|
||||
{
|
||||
return deepMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignore exceptions in Release. The code above won't throw, but if it does, we don't want to crash the app.
|
||||
#if DEBUG
|
||||
throw;
|
||||
#endif
|
||||
}
|
||||
|
||||
return topMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,6 @@
|
||||
<PackageReference Include="Microsoft.Windows.Compatibility" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
|
||||
<PackageReference Include="OpenAI" />
|
||||
<PackageReference Include="ReverseMarkdown" />
|
||||
<PackageReference Include="StreamJsonRpc" />
|
||||
<PackageReference Include="WinUIEx" />
|
||||
|
||||
@@ -12,7 +12,6 @@ using Azure.AI.OpenAI;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using OpenAI.Moderations;
|
||||
using Windows.Security.Credentials;
|
||||
|
||||
namespace AdvancedPaste.Helpers
|
||||
@@ -77,28 +76,13 @@ namespace AdvancedPaste.Helpers
|
||||
{
|
||||
OpenAIClient azureAIClient = new OpenAIClient(_openAIKey);
|
||||
|
||||
string inputString = systemInstructions + "\n\n" + userMessage;
|
||||
|
||||
ModerationClient moderationClient = new("omni-moderation-latest", _openAIKey);
|
||||
|
||||
// TODO: Run this as async along with the chat completion result to maintain speed
|
||||
ModerationResult moderationResult = moderationClient.ClassifyText(inputString);
|
||||
|
||||
if (moderationResult.Flagged)
|
||||
{
|
||||
#pragma warning disable CA2201 // Use explicit type
|
||||
// TODO: Use a more explicit type and handle the error more gracefully
|
||||
throw new Exception("Flagged by moderation");
|
||||
#pragma warning restore CA2201 // Use explicit type
|
||||
}
|
||||
|
||||
var response = azureAIClient.GetCompletions(
|
||||
new CompletionsOptions()
|
||||
{
|
||||
DeploymentName = _modelName,
|
||||
Prompts =
|
||||
{
|
||||
inputString,
|
||||
systemInstructions + "\n\n" + userMessage,
|
||||
},
|
||||
Temperature = 0.01F,
|
||||
MaxTokens = 2000,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" />
|
||||
<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')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
@@ -141,7 +141,7 @@
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
@@ -153,7 +153,7 @@
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -4,5 +4,5 @@
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.22621.2428" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.6.240923002" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.6.241114003" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -2,8 +2,9 @@
|
||||
// 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.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
using System.Text;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MouseJump.Common.Helpers;
|
||||
using MouseJump.Common.Imaging;
|
||||
@@ -16,7 +17,7 @@ namespace MouseJump.Common.UnitTests.Helpers;
|
||||
public static class DrawingHelperTests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class GetPreviewLayoutTests
|
||||
public sealed class RenderPreviewTests
|
||||
{
|
||||
public sealed class TestCase
|
||||
{
|
||||
@@ -46,7 +47,7 @@ public static class DrawingHelperTests
|
||||
yield return new object[]
|
||||
{
|
||||
new TestCase(
|
||||
previewStyle: StyleHelper.DefaultPreviewStyle,
|
||||
previewStyle: StyleHelper.BezelledPreviewStyle,
|
||||
screens: new List<RectangleInfo>()
|
||||
{
|
||||
new(0, 0, 500, 500),
|
||||
@@ -62,7 +63,7 @@ public static class DrawingHelperTests
|
||||
yield return new object[]
|
||||
{
|
||||
new TestCase(
|
||||
previewStyle: StyleHelper.DefaultPreviewStyle,
|
||||
previewStyle: StyleHelper.BezelledPreviewStyle,
|
||||
screens: new List<RectangleInfo>()
|
||||
{
|
||||
new(5120, 349, 1920, 1080),
|
||||
@@ -79,7 +80,7 @@ public static class DrawingHelperTests
|
||||
public void RunTestCases(TestCase data)
|
||||
{
|
||||
// load the fake desktop image
|
||||
using var desktopImage = GetPreviewLayoutTests.LoadImageResource(data.DesktopImageFilename);
|
||||
using var desktopImage = RenderPreviewTests.LoadImageResource(data.DesktopImageFilename);
|
||||
|
||||
// draw the preview image
|
||||
var previewLayout = LayoutHelper.GetPreviewLayout(
|
||||
@@ -90,28 +91,29 @@ public static class DrawingHelperTests
|
||||
using var actual = DrawingHelper.RenderPreview(previewLayout, imageCopyService);
|
||||
|
||||
// load the expected image
|
||||
var expected = GetPreviewLayoutTests.LoadImageResource(data.ExpectedImageFilename);
|
||||
var expected = RenderPreviewTests.LoadImageResource(data.ExpectedImageFilename);
|
||||
|
||||
// compare the images
|
||||
var screens = System.Windows.Forms.Screen.AllScreens;
|
||||
AssertImagesEqual(expected, actual);
|
||||
}
|
||||
|
||||
private static Bitmap LoadImageResource(string filename)
|
||||
{
|
||||
// assume embedded resources are in the same source folder as this
|
||||
// class, and the namespace hierarchy matches the folder structure.
|
||||
// that way we can build resource names from the current namespace
|
||||
var resourcePrefix = typeof(DrawingHelperTests).Namespace;
|
||||
var resourceName = $"{resourcePrefix}.{filename}";
|
||||
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var assemblyName = new AssemblyName(assembly.FullName ?? throw new InvalidOperationException());
|
||||
var resourceName = $"{typeof(DrawingHelperTests).Namespace}.{filename.Replace("/", ".")}";
|
||||
var resourceNames = assembly.GetManifestResourceNames();
|
||||
if (!resourceNames.Contains(resourceName))
|
||||
{
|
||||
var message = $"Embedded resource '{resourceName}' does not exist. " +
|
||||
"Valid resource names are: \r\n" + string.Join("\r\n", resourceNames);
|
||||
throw new InvalidOperationException(message);
|
||||
var message = new StringBuilder();
|
||||
message.AppendLine(CultureInfo.InvariantCulture, $"Embedded resource '{resourceName}' does not exist.");
|
||||
message.AppendLine($"Known resources:");
|
||||
foreach (var name in resourceNames)
|
||||
{
|
||||
message.AppendLine(name);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(message.ToString());
|
||||
}
|
||||
|
||||
var stream = assembly.GetManifestResourceStream(resourceName)
|
||||
@@ -121,7 +123,7 @@ public static class DrawingHelperTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Naive / brute force image comparison - we can optimise this later :-)
|
||||
/// Naive / brute force image comparison - we can optimize this later :-)
|
||||
/// </summary>
|
||||
private static void AssertImagesEqual(Bitmap expected, Bitmap actual)
|
||||
{
|
||||
|
||||
@@ -129,7 +129,7 @@ public static class LayoutHelperTests
|
||||
public static IEnumerable<object[]> GetTestCases()
|
||||
{
|
||||
// happy path - single screen with 50% scaling,
|
||||
// *has* a preview borders but *no* screenshot borders
|
||||
// *has* a preview border but *no* screenshot borders
|
||||
//
|
||||
// +----------------+
|
||||
// | |
|
||||
@@ -160,7 +160,7 @@ public static class LayoutHelperTests
|
||||
new(0, 0, 1024, 768),
|
||||
};
|
||||
var activatedLocation = new PointInfo(512, 384);
|
||||
var previewLayout = new PreviewLayout(
|
||||
var expectedResult = new PreviewLayout(
|
||||
virtualScreen: new(0, 0, 1024, 768),
|
||||
screens: screens,
|
||||
activatedScreenIndex: 0,
|
||||
@@ -183,7 +183,7 @@ public static class LayoutHelperTests
|
||||
contentBounds: new(6, 6, 512, 384)
|
||||
),
|
||||
});
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, previewLayout) };
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, expectedResult) };
|
||||
|
||||
// happy path - single screen with 50% scaling,
|
||||
// *no* preview borders but *has* screenshot borders
|
||||
@@ -217,7 +217,7 @@ public static class LayoutHelperTests
|
||||
new(0, 0, 1024, 768),
|
||||
};
|
||||
activatedLocation = new PointInfo(512, 384);
|
||||
previewLayout = new PreviewLayout(
|
||||
expectedResult = new PreviewLayout(
|
||||
virtualScreen: new(0, 0, 1024, 768),
|
||||
screens: screens,
|
||||
activatedScreenIndex: 0,
|
||||
@@ -240,7 +240,59 @@ public static class LayoutHelperTests
|
||||
contentBounds: new(6, 6, 500, 372)
|
||||
),
|
||||
});
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, previewLayout) };
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, expectedResult) };
|
||||
|
||||
// rounding error check - single screen with 33% scaling,
|
||||
// no borders, check to make sure form scales to exactly
|
||||
// fill the canvas size with no rounding errors.
|
||||
//
|
||||
// in this test the preview width is 300 and the desktop is
|
||||
// 900, so the scaling factor is 1/3, but this gets rounded
|
||||
// to 0.3333333333333333333333333333, and 900 times this value
|
||||
// is 299.99999999999999999999999997. if we don't scale correctly
|
||||
// the resulting form width might only be 299 pixels instead of 300
|
||||
//
|
||||
// +----------------+
|
||||
// | |
|
||||
// | 0 |
|
||||
// | |
|
||||
// +----------------+
|
||||
previewStyle = new PreviewStyle(
|
||||
canvasSize: new(
|
||||
width: 300,
|
||||
height: 200
|
||||
),
|
||||
canvasStyle: BoxStyle.Empty,
|
||||
screenStyle: BoxStyle.Empty);
|
||||
screens = new List<RectangleInfo>
|
||||
{
|
||||
new(0, 0, 900, 200),
|
||||
};
|
||||
activatedLocation = new PointInfo(450, 100);
|
||||
expectedResult = new PreviewLayout(
|
||||
virtualScreen: new(0, 0, 900, 200),
|
||||
screens: screens,
|
||||
activatedScreenIndex: 0,
|
||||
formBounds: new(300, 66.5m, 300, 67),
|
||||
previewStyle: previewStyle,
|
||||
previewBounds: new(
|
||||
outerBounds: new(0, 0, 300, 67),
|
||||
marginBounds: new(0, 0, 300, 67),
|
||||
borderBounds: new(0, 0, 300, 67),
|
||||
paddingBounds: new(0, 0, 300, 67),
|
||||
contentBounds: new(0, 0, 300, 67)
|
||||
),
|
||||
screenshotBounds: new()
|
||||
{
|
||||
new(
|
||||
outerBounds: new(0, 0, 300, 67),
|
||||
marginBounds: new(0, 0, 300, 67),
|
||||
borderBounds: new(0, 0, 300, 67),
|
||||
paddingBounds: new(0, 0, 300, 67),
|
||||
contentBounds: new(0, 0, 300, 67)
|
||||
),
|
||||
});
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, expectedResult) };
|
||||
|
||||
// primary monitor not topmost / leftmost - if there are screens
|
||||
// that are further left or higher up than the primary monitor
|
||||
@@ -291,7 +343,7 @@ public static class LayoutHelperTests
|
||||
new(0, 0, 5120, 1440),
|
||||
};
|
||||
activatedLocation = new(-960, 60);
|
||||
previewLayout = new PreviewLayout(
|
||||
expectedResult = new PreviewLayout(
|
||||
virtualScreen: new(-1920, -480, 7040, 1920),
|
||||
screens: screens,
|
||||
activatedScreenIndex: 0,
|
||||
@@ -321,7 +373,7 @@ public static class LayoutHelperTests
|
||||
contentBounds: new(204, 60, 500, 132)
|
||||
),
|
||||
});
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, previewLayout) };
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, expectedResult) };
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 215 KiB After Width: | Height: | Size: 202 KiB |
@@ -15,45 +15,49 @@ public static class SizeInfoTests
|
||||
{
|
||||
public sealed class TestCase
|
||||
{
|
||||
public TestCase(SizeInfo obj, SizeInfo bounds, SizeInfo expectedResult)
|
||||
public TestCase(SizeInfo source, SizeInfo bounds, SizeInfo expectedResult, decimal scalingRatio)
|
||||
{
|
||||
this.Obj = obj;
|
||||
this.Source = source;
|
||||
this.Bounds = bounds;
|
||||
this.ExpectedResult = expectedResult;
|
||||
this.ScalingRatio = scalingRatio;
|
||||
}
|
||||
|
||||
public SizeInfo Obj { get; }
|
||||
public SizeInfo Source { get; }
|
||||
|
||||
public SizeInfo Bounds { get; }
|
||||
|
||||
public SizeInfo ExpectedResult { get; }
|
||||
|
||||
public decimal ScalingRatio { get; }
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetTestCases()
|
||||
{
|
||||
// identity tests
|
||||
yield return new object[] { new TestCase(new(512, 384), new(512, 384), new(512, 384)), };
|
||||
yield return new object[] { new TestCase(new(1024, 768), new(1024, 768), new(1024, 768)), };
|
||||
yield return new object[] { new TestCase(new(512, 384), new(512, 384), new(512, 384), 1), };
|
||||
yield return new object[] { new TestCase(new(1024, 768), new(1024, 768), new(1024, 768), 1), };
|
||||
|
||||
// general tests
|
||||
yield return new object[] { new TestCase(new(512, 384), new(2048, 1536), new(2048, 1536)), };
|
||||
yield return new object[] { new TestCase(new(2048, 1536), new(1024, 768), new(1024, 768)), };
|
||||
yield return new object[] { new TestCase(new(512, 384), new(2048, 1536), new(2048, 1536), 4), };
|
||||
yield return new object[] { new TestCase(new(2048, 1536), new(1024, 768), new(1024, 768), 0.5m), };
|
||||
|
||||
// scale to fit width
|
||||
yield return new object[] { new TestCase(new(512, 384), new(2048, 3072), new(2048, 1536)), };
|
||||
yield return new object[] { new TestCase(new(512, 384), new(2048, 3072), new(2048, 1536), 4), };
|
||||
|
||||
// scale to fit height
|
||||
yield return new object[] { new TestCase(new(512, 384), new(4096, 1536), new(2048, 1536)), };
|
||||
yield return new object[] { new TestCase(new(512, 384), new(4096, 1536), new(2048, 1536), 4), };
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)]
|
||||
public void RunTestCases(TestCase data)
|
||||
{
|
||||
var actual = data.Obj.ScaleToFit(data.Bounds);
|
||||
var actual = data.Source.ScaleToFit(data.Bounds, out var scalingRatio);
|
||||
var expected = data.ExpectedResult;
|
||||
Assert.AreEqual(expected.Width, actual.Width);
|
||||
Assert.AreEqual(expected.Height, actual.Height);
|
||||
Assert.AreEqual(scalingRatio, data.ScalingRatio);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// 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.Globalization;
|
||||
|
||||
namespace MouseJump.Common.Helpers;
|
||||
|
||||
public static class ConfigHelper
|
||||
{
|
||||
public static Color? ToUnnamedColor(Color? value)
|
||||
{
|
||||
if (!value.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var color = value.Value;
|
||||
return Color.FromArgb(color.A, color.R, color.G, color.B);
|
||||
}
|
||||
|
||||
public static string? SerializeToConfigColorString(Color? value)
|
||||
{
|
||||
if (!value.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var color = value.Value;
|
||||
return color switch
|
||||
{
|
||||
Color { IsNamedColor: true } =>
|
||||
$"{nameof(Color)}.{color.Name}",
|
||||
Color { IsSystemColor: true } =>
|
||||
$"{nameof(SystemColors)}.{color.Name}",
|
||||
_ =>
|
||||
$"#{color.R:X2}{color.G:X2}{color.B:X2}",
|
||||
};
|
||||
}
|
||||
|
||||
public static Color? DeserializeFromConfigColorString(string? value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// e.g. "#AABBCC"
|
||||
if (value.StartsWith('#'))
|
||||
{
|
||||
var culture = CultureInfo.InvariantCulture;
|
||||
if ((value.Length == 7)
|
||||
&& int.TryParse(value[1..3], NumberStyles.HexNumber, culture, out var r)
|
||||
&& int.TryParse(value[3..5], NumberStyles.HexNumber, culture, out var g)
|
||||
&& int.TryParse(value[5..7], NumberStyles.HexNumber, culture, out var b))
|
||||
{
|
||||
return Color.FromArgb(0xFF, r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
const StringComparison comparison = StringComparison.InvariantCulture;
|
||||
|
||||
// e.g. "Color.Red"
|
||||
const string colorPrefix = $"{nameof(Color)}.";
|
||||
if (value.StartsWith(colorPrefix, comparison))
|
||||
{
|
||||
var colorName = value[colorPrefix.Length..];
|
||||
var property = typeof(Color).GetProperties()
|
||||
.SingleOrDefault(property => property.Name == colorName);
|
||||
if (property is not null)
|
||||
{
|
||||
return (Color?)property.GetValue(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
// e.g. "SystemColors.Highlight"
|
||||
const string systemColorPrefix = $"{nameof(SystemColors)}.";
|
||||
if (value.StartsWith(systemColorPrefix, comparison))
|
||||
{
|
||||
var colorName = value[systemColorPrefix.Length..];
|
||||
var property = typeof(SystemColors).GetProperties()
|
||||
.SingleOrDefault(property => property.Name == colorName);
|
||||
if (property is not null)
|
||||
{
|
||||
return (Color?)property.GetValue(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -102,8 +102,13 @@ public static class DrawingHelper
|
||||
return;
|
||||
}
|
||||
|
||||
if (borderStyle.Color is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// draw the main box border
|
||||
using var borderBrush = new SolidBrush(borderStyle.Color);
|
||||
using var borderBrush = new SolidBrush(borderStyle.Color.Value);
|
||||
var borderRegion = new Region(boxBounds.BorderBounds.ToRectangle());
|
||||
borderRegion.Exclude(boxBounds.PaddingBounds.ToRectangle());
|
||||
graphics.FillRegion(borderBrush, borderRegion);
|
||||
|
||||
@@ -46,16 +46,13 @@ public static class LayoutHelper
|
||||
.Shrink(previewStyle.CanvasStyle.BorderStyle)
|
||||
.Shrink(previewStyle.CanvasStyle.PaddingStyle);
|
||||
|
||||
// scale the virtual screen to fit inside the content area
|
||||
var screenScalingRatio = builder.VirtualScreen.Size
|
||||
.ScaleToFitRatio(maxContentSize);
|
||||
|
||||
// work out the actual size of the "content area" by scaling the virtual screen
|
||||
// to fit inside the maximum content area while maintaining its aspect ration.
|
||||
// we'll also offset it to allow for any margins, borders and padding
|
||||
var contentBounds = builder.VirtualScreen.Size
|
||||
.Scale(screenScalingRatio)
|
||||
.Floor()
|
||||
.ScaleToFit(maxContentSize, out var scalingRatio)
|
||||
.Round()
|
||||
.Clamp(maxContentSize)
|
||||
.PlaceAt(0, 0)
|
||||
.Offset(previewStyle.CanvasStyle.MarginStyle.Left, previewStyle.CanvasStyle.MarginStyle.Top)
|
||||
.Offset(previewStyle.CanvasStyle.BorderStyle.Left, previewStyle.CanvasStyle.BorderStyle.Top)
|
||||
@@ -82,16 +79,16 @@ public static class LayoutHelper
|
||||
screen => LayoutHelper.GetBoxBoundsFromOuterBounds(
|
||||
screen
|
||||
.Offset(builder.VirtualScreen.Location.ToSize().Invert())
|
||||
.Scale(screenScalingRatio)
|
||||
.Scale(scalingRatio)
|
||||
.Offset(builder.PreviewBounds.ContentBounds.Location.ToSize())
|
||||
.Truncate(),
|
||||
.Round(),
|
||||
previewStyle.ScreenStyle))
|
||||
.ToList();
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
internal static RectangleInfo GetCombinedScreenBounds(List<RectangleInfo> screens)
|
||||
public static RectangleInfo GetCombinedScreenBounds(List<RectangleInfo> screens)
|
||||
{
|
||||
return screens.Skip(1).Aggregate(
|
||||
seed: screens.First(),
|
||||
|
||||
@@ -102,7 +102,7 @@ public static class MouseHelper
|
||||
/// See https://github.com/microsoft/PowerToys/issues/24523
|
||||
/// https://github.com/microsoft/PowerToys/pull/24527
|
||||
/// </remarks>
|
||||
internal static void SimulateMouseMovementEvent(PointInfo location)
|
||||
private static void SimulateMouseMovementEvent(PointInfo location)
|
||||
{
|
||||
var inputs = new User32.INPUT[]
|
||||
{
|
||||
|
||||
@@ -10,49 +10,9 @@ namespace MouseJump.Common.Helpers;
|
||||
public static class StyleHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Default v2 preview style
|
||||
/// Compact (legacy) preview style
|
||||
/// </summary>
|
||||
public static readonly PreviewStyle DefaultPreviewStyle = new(
|
||||
canvasSize: new(
|
||||
width: 1600,
|
||||
height: 1200
|
||||
),
|
||||
canvasStyle: new(
|
||||
marginStyle: MarginStyle.Empty,
|
||||
borderStyle: new(
|
||||
color: SystemColors.Highlight,
|
||||
all: 6,
|
||||
depth: 0
|
||||
),
|
||||
paddingStyle: new(
|
||||
all: 4
|
||||
),
|
||||
backgroundStyle: new(
|
||||
color1: Color.FromArgb(0xFF, 0x0D, 0x57, 0xD2),
|
||||
color2: Color.FromArgb(0xFF, 0x03, 0x44, 0xC0)
|
||||
)
|
||||
),
|
||||
screenStyle: new(
|
||||
marginStyle: new(
|
||||
all: 4
|
||||
),
|
||||
borderStyle: new(
|
||||
color: Color.FromArgb(0xFF, 0x22, 0x22, 0x22),
|
||||
all: 12,
|
||||
depth: 4
|
||||
),
|
||||
paddingStyle: PaddingStyle.Empty,
|
||||
backgroundStyle: new(
|
||||
color1: Color.MidnightBlue,
|
||||
color2: Color.MidnightBlue
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Legacy preview style
|
||||
/// </summary>
|
||||
public static readonly PreviewStyle LegacyPreviewStyle = new(
|
||||
public static readonly PreviewStyle CompactPreviewStyle = new(
|
||||
canvasSize: new(
|
||||
width: 1600,
|
||||
height: 1200
|
||||
@@ -89,6 +49,46 @@ public static class StyleHelper
|
||||
)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Bezelled preview style
|
||||
/// </summary>
|
||||
public static readonly PreviewStyle BezelledPreviewStyle = new(
|
||||
canvasSize: new(
|
||||
width: 1600,
|
||||
height: 1200
|
||||
),
|
||||
canvasStyle: new(
|
||||
marginStyle: MarginStyle.Empty,
|
||||
borderStyle: new(
|
||||
color: SystemColors.Highlight,
|
||||
all: 6,
|
||||
depth: 0
|
||||
),
|
||||
paddingStyle: new(
|
||||
all: 4
|
||||
),
|
||||
backgroundStyle: new(
|
||||
color1: Color.FromArgb(0xFF, 0x0D, 0x57, 0xD2),
|
||||
color2: Color.FromArgb(0xFF, 0x03, 0x44, 0xC0)
|
||||
)
|
||||
),
|
||||
screenStyle: new(
|
||||
marginStyle: new(
|
||||
all: 4
|
||||
),
|
||||
borderStyle: new(
|
||||
color: Color.FromArgb(0xFF, 0x22, 0x22, 0x22),
|
||||
all: 12,
|
||||
depth: 4
|
||||
),
|
||||
paddingStyle: PaddingStyle.Empty,
|
||||
backgroundStyle: new(
|
||||
color1: Color.MidnightBlue,
|
||||
color2: Color.MidnightBlue
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
public static PreviewStyle WithCanvasSize(this PreviewStyle previewStyle, SizeInfo canvasSize)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(previewStyle);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// 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.Drawing.Drawing2D;
|
||||
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
|
||||
namespace MouseJump.Common.Imaging;
|
||||
@@ -31,6 +33,11 @@ public sealed class StaticImageRegionCopyService : IImageRegionCopyService
|
||||
RectangleInfo sourceBounds,
|
||||
RectangleInfo targetBounds)
|
||||
{
|
||||
// prevent the background bleeding through into screen images
|
||||
// (see https://github.com/mikeclayton/FancyMouse/issues/44)
|
||||
targetGraphics.PixelOffsetMode = PixelOffsetMode.Half;
|
||||
targetGraphics.InterpolationMode = InterpolationMode.NearestNeighbor;
|
||||
|
||||
targetGraphics.DrawImage(
|
||||
image: this.SourceImage,
|
||||
destRect: targetBounds.ToRectangle(),
|
||||
|
||||
@@ -203,6 +203,15 @@ public sealed class RectangleInfo
|
||||
public RectangleInfo Offset(decimal dx, decimal dy) =>
|
||||
new(this.X + dx, this.Y + dy, this.Width, this.Height);
|
||||
|
||||
public RectangleInfo Round() =>
|
||||
this.Round(0);
|
||||
|
||||
public RectangleInfo Round(int decimals) => new(
|
||||
Math.Round(this.X, decimals),
|
||||
Math.Round(this.Y, decimals),
|
||||
Math.Round(this.Width, decimals),
|
||||
Math.Round(this.Height, decimals));
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="RectangleInfo"/> that is a scaled version of the current rectangle.
|
||||
/// The dimensions of the new rectangle are calculated by multiplying the current rectangle's dimensions by the scaling factor.
|
||||
|
||||
@@ -12,6 +12,8 @@ public sealed class ScreenInfo
|
||||
{
|
||||
public ScreenInfo(int handle, bool primary, RectangleInfo displayArea, RectangleInfo workingArea)
|
||||
{
|
||||
// this.Handle is a HMONITOR that has been cast to an int because we don't want
|
||||
// to expose the HMONITOR type outside the current assembly.
|
||||
this.Handle = handle;
|
||||
this.Primary = primary;
|
||||
this.DisplayArea = displayArea ?? throw new ArgumentNullException(nameof(displayArea));
|
||||
|
||||
@@ -33,6 +33,20 @@ public sealed class SizeInfo
|
||||
get;
|
||||
}
|
||||
|
||||
public SizeInfo Clamp(SizeInfo max)
|
||||
{
|
||||
return new(
|
||||
width: Math.Clamp(this.Width, 0, max.Width),
|
||||
height: Math.Clamp(this.Height, 0, max.Height));
|
||||
}
|
||||
|
||||
public SizeInfo Clamp(decimal maxWidth, decimal maxHeight)
|
||||
{
|
||||
return new(
|
||||
width: Math.Clamp(this.Width, 0, maxWidth),
|
||||
height: Math.Clamp(this.Height, 0, maxHeight));
|
||||
}
|
||||
|
||||
public SizeInfo Enlarge(BorderStyle border) =>
|
||||
new(
|
||||
this.Width + border.Horizontal,
|
||||
@@ -43,6 +57,17 @@ public sealed class SizeInfo
|
||||
this.Width + padding.Horizontal,
|
||||
this.Height + padding.Vertical);
|
||||
|
||||
/// <summary>
|
||||
/// Rounds down the width and height of this size to the nearest whole number.
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="SizeInfo"/> instance with floored dimensions.</returns>
|
||||
public SizeInfo Floor()
|
||||
{
|
||||
return new SizeInfo(
|
||||
Math.Floor(this.Width),
|
||||
Math.Floor(this.Height));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the intersection of this size with another size, resulting in a size that represents
|
||||
/// the overlapping dimensions. Both sizes must be non-negative.
|
||||
@@ -69,19 +94,6 @@ public sealed class SizeInfo
|
||||
public SizeInfo Invert() =>
|
||||
new(-this.Width, -this.Height);
|
||||
|
||||
public SizeInfo Scale(decimal scalingFactor) => new(
|
||||
this.Width * scalingFactor,
|
||||
this.Height * scalingFactor);
|
||||
|
||||
public SizeInfo Shrink(BorderStyle border) =>
|
||||
new(this.Width - border.Horizontal, this.Height - border.Vertical);
|
||||
|
||||
public SizeInfo Shrink(MarginStyle margin) =>
|
||||
new(this.Width - margin.Horizontal, this.Height - margin.Vertical);
|
||||
|
||||
public SizeInfo Shrink(PaddingStyle padding) =>
|
||||
new(this.Width - padding.Horizontal, this.Height - padding.Vertical);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="RectangleInfo"/> instance representing a rectangle with this size,
|
||||
/// positioned at the specified coordinates.
|
||||
@@ -92,32 +104,39 @@ public sealed class SizeInfo
|
||||
public RectangleInfo PlaceAt(decimal x, decimal y) =>
|
||||
new(x, y, this.Width, this.Height);
|
||||
|
||||
public SizeInfo Round() =>
|
||||
this.Round(0);
|
||||
|
||||
public SizeInfo Round(int decimals) => new(
|
||||
Math.Round(this.Width, decimals),
|
||||
Math.Round(this.Height, decimals));
|
||||
|
||||
public SizeInfo Scale(decimal scalingFactor) => new(
|
||||
this.Width * scalingFactor,
|
||||
this.Height * scalingFactor);
|
||||
|
||||
/// <summary>
|
||||
/// Scales this size to fit within the bounds of another size, while maintaining the aspect ratio.
|
||||
/// </summary>
|
||||
/// <param name="bounds">The size to fit this size into.</param>
|
||||
/// <returns>A new <see cref="SizeInfo"/> instance representing the scaled size.</returns>
|
||||
public SizeInfo ScaleToFit(SizeInfo bounds)
|
||||
public SizeInfo ScaleToFit(SizeInfo bounds, out decimal scalingRatio)
|
||||
{
|
||||
var widthRatio = bounds.Width / this.Width;
|
||||
var heightRatio = bounds.Height / this.Height;
|
||||
return widthRatio.CompareTo(heightRatio) switch
|
||||
switch (widthRatio.CompareTo(heightRatio))
|
||||
{
|
||||
< 0 => new(bounds.Width, this.Height * widthRatio),
|
||||
0 => bounds,
|
||||
> 0 => new(this.Width * heightRatio, bounds.Height),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rounds down the width and height of this size to the nearest whole number.
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="SizeInfo"/> instance with floored dimensions.</returns>
|
||||
public SizeInfo Floor()
|
||||
{
|
||||
return new SizeInfo(
|
||||
Math.Floor(this.Width),
|
||||
Math.Floor(this.Height));
|
||||
case < 0:
|
||||
scalingRatio = widthRatio;
|
||||
return new(bounds.Width, this.Height * widthRatio);
|
||||
case 0:
|
||||
// widthRatio and heightRatio are the same, so just pick one
|
||||
scalingRatio = widthRatio;
|
||||
return bounds;
|
||||
case > 0:
|
||||
scalingRatio = heightRatio;
|
||||
return new(this.Width * heightRatio, bounds.Height);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -140,6 +159,15 @@ public sealed class SizeInfo
|
||||
return scalingRatio;
|
||||
}
|
||||
|
||||
public SizeInfo Shrink(BorderStyle border) =>
|
||||
new(this.Width - border.Horizontal, this.Height - border.Vertical);
|
||||
|
||||
public SizeInfo Shrink(MarginStyle margin) =>
|
||||
new(this.Width - margin.Horizontal, this.Height - margin.Vertical);
|
||||
|
||||
public SizeInfo Shrink(PaddingStyle padding) =>
|
||||
new(this.Width - padding.Horizontal, this.Height - padding.Vertical);
|
||||
|
||||
public Size ToSize() => new((int)this.Width, (int)this.Height);
|
||||
|
||||
public Point ToPoint() => new((int)this.Width, (int)this.Height);
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// 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.
|
||||
|
||||
namespace MouseJump.Common.Models.Settings;
|
||||
|
||||
public enum PreviewType
|
||||
{
|
||||
Custom = 0,
|
||||
Compact = 1,
|
||||
Bezelled = 2,
|
||||
}
|
||||
@@ -9,14 +9,14 @@ namespace MouseJump.Common.Models.Styles;
|
||||
/// </summary>
|
||||
public sealed class BorderStyle
|
||||
{
|
||||
public static readonly BorderStyle Empty = new(Color.Transparent, 0, 0);
|
||||
public static readonly BorderStyle Empty = new(null, 0, 0);
|
||||
|
||||
public BorderStyle(Color color, decimal all, decimal depth)
|
||||
public BorderStyle(Color? color, decimal all, decimal depth)
|
||||
: this(color, all, all, all, all, depth)
|
||||
{
|
||||
}
|
||||
|
||||
public BorderStyle(Color color, decimal left, decimal top, decimal right, decimal bottom, decimal depth)
|
||||
public BorderStyle(Color? color, decimal left, decimal top, decimal right, decimal bottom, decimal depth)
|
||||
{
|
||||
this.Color = color;
|
||||
this.Left = left;
|
||||
@@ -26,7 +26,7 @@ public sealed class BorderStyle
|
||||
this.Depth = depth;
|
||||
}
|
||||
|
||||
public Color Color
|
||||
public Color? Color
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ using System.Threading;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
||||
using MouseJump.Common.Helpers;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
using MouseJump.Common.Models.Settings;
|
||||
using MouseJump.Common.Models.Styles;
|
||||
|
||||
namespace MouseJumpUI.Helpers;
|
||||
|
||||
@@ -93,4 +97,65 @@ internal sealed class SettingsHelper
|
||||
{
|
||||
this.CurrentSettings = this.LoadSettings();
|
||||
}
|
||||
|
||||
public static PreviewStyle GetActivePreviewStyle(MouseJumpSettings settings)
|
||||
{
|
||||
var previewType = Enum.TryParse<PreviewType>(settings.Properties.PreviewType, true, out var previewTypeResult)
|
||||
? previewTypeResult
|
||||
: PreviewType.Bezelled;
|
||||
|
||||
var canvasSize = new SizeInfo(
|
||||
settings.Properties.ThumbnailSize.Width,
|
||||
settings.Properties.ThumbnailSize.Height);
|
||||
|
||||
var properties = settings.Properties;
|
||||
|
||||
var previewStyle = previewType switch
|
||||
{
|
||||
PreviewType.Compact => StyleHelper.CompactPreviewStyle.WithCanvasSize(canvasSize),
|
||||
PreviewType.Bezelled => StyleHelper.BezelledPreviewStyle.WithCanvasSize(canvasSize),
|
||||
PreviewType.Custom => new PreviewStyle(
|
||||
canvasSize: canvasSize,
|
||||
canvasStyle: new(
|
||||
marginStyle: new(0),
|
||||
borderStyle: new(
|
||||
color: ConfigHelper.DeserializeFromConfigColorString(
|
||||
properties.BorderColor),
|
||||
all: properties.BorderThickness,
|
||||
depth: properties.Border3dDepth
|
||||
),
|
||||
paddingStyle: new(
|
||||
all: properties.BorderPadding
|
||||
),
|
||||
backgroundStyle: new(
|
||||
color1: ConfigHelper.DeserializeFromConfigColorString(
|
||||
properties.BackgroundColor1),
|
||||
color2: ConfigHelper.DeserializeFromConfigColorString(
|
||||
properties.BackgroundColor2)
|
||||
)
|
||||
),
|
||||
screenStyle: new(
|
||||
marginStyle: new(
|
||||
all: properties.ScreenMargin
|
||||
),
|
||||
borderStyle: new(
|
||||
color: ConfigHelper.DeserializeFromConfigColorString(
|
||||
properties.BezelColor),
|
||||
all: properties.BezelThickness,
|
||||
depth: properties.Bezel3dDepth
|
||||
),
|
||||
paddingStyle: new(0),
|
||||
backgroundStyle: new(
|
||||
color1: ConfigHelper.DeserializeFromConfigColorString(
|
||||
properties.ScreenColor1),
|
||||
color2: ConfigHelper.DeserializeFromConfigColorString(
|
||||
properties.ScreenColor2)
|
||||
)
|
||||
)),
|
||||
_ => throw new InvalidOperationException(
|
||||
$"Unhandled {nameof(PreviewType)} '{previewType}'"),
|
||||
};
|
||||
|
||||
return previewStyle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,12 +183,9 @@ internal sealed partial class MainForm : Form
|
||||
var appSettings = this.SettingsHelper.CurrentSettings ?? throw new InvalidOperationException();
|
||||
var screens = ScreenHelper.GetAllScreens().Select(screen => screen.DisplayArea).ToList();
|
||||
var activatedLocation = MouseHelper.GetCursorPosition();
|
||||
|
||||
this.PreviewLayout = LayoutHelper.GetPreviewLayout(
|
||||
previewStyle: StyleHelper.LegacyPreviewStyle.WithCanvasSize(
|
||||
new(
|
||||
appSettings.Properties.ThumbnailSize.Width,
|
||||
appSettings.Properties.ThumbnailSize.Height
|
||||
)),
|
||||
previewStyle: SettingsHelper.GetActivePreviewStyle(appSettings),
|
||||
screens: screens,
|
||||
activatedLocation: activatedLocation);
|
||||
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
// 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.Windows.Forms;
|
||||
|
||||
namespace MouseJumpUI.Models.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable version of a System.Windows.Forms.Padding object with some extra utility methods.
|
||||
/// </summary>
|
||||
public sealed class PaddingInfo
|
||||
{
|
||||
public PaddingInfo(decimal all)
|
||||
: this(all, all, all, all)
|
||||
{
|
||||
}
|
||||
|
||||
public PaddingInfo(decimal left, decimal top, decimal right, decimal bottom)
|
||||
{
|
||||
this.Left = left;
|
||||
this.Top = top;
|
||||
this.Right = right;
|
||||
this.Bottom = bottom;
|
||||
}
|
||||
|
||||
public decimal Left
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Top
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Right
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Bottom
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Horizontal => this.Left + this.Right;
|
||||
|
||||
public decimal Vertical => this.Top + this.Bottom;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "{" +
|
||||
$"{nameof(this.Left)}={this.Left}," +
|
||||
$"{nameof(this.Top)}={this.Top}," +
|
||||
$"{nameof(this.Right)}={this.Right}," +
|
||||
$"{nameof(this.Bottom)}={this.Bottom}" +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
// 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.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Models.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable version of a System.Drawing.Point object with some extra utility methods.
|
||||
/// </summary>
|
||||
public sealed class PointInfo
|
||||
{
|
||||
public PointInfo(decimal x, decimal y)
|
||||
{
|
||||
this.X = x;
|
||||
this.Y = y;
|
||||
}
|
||||
|
||||
public PointInfo(Point point)
|
||||
: this(point.X, point.Y)
|
||||
{
|
||||
}
|
||||
|
||||
public decimal X
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Y
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public SizeInfo ToSize()
|
||||
{
|
||||
return new((int)this.X, (int)this.Y);
|
||||
}
|
||||
|
||||
public PointInfo Scale(decimal scalingFactor) => new(this.X * scalingFactor, this.Y * scalingFactor);
|
||||
|
||||
public PointInfo Offset(PointInfo amount) => new(this.X + amount.X, this.Y + amount.Y);
|
||||
|
||||
public Point ToPoint() => new((int)this.X, (int)this.Y);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "{" +
|
||||
$"{nameof(this.X)}={this.X}," +
|
||||
$"{nameof(this.Y)}={this.Y}" +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
// 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;
|
||||
using System.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Models.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable version of a System.Drawing.Rectangle object with some extra utility methods.
|
||||
/// </summary>
|
||||
public sealed class RectangleInfo
|
||||
{
|
||||
public RectangleInfo(decimal x, decimal y, decimal width, decimal height)
|
||||
{
|
||||
this.X = x;
|
||||
this.Y = y;
|
||||
this.Width = width;
|
||||
this.Height = height;
|
||||
}
|
||||
|
||||
public RectangleInfo(Rectangle rectangle)
|
||||
: this(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height)
|
||||
{
|
||||
}
|
||||
|
||||
public RectangleInfo(Point location, SizeInfo size)
|
||||
: this(location.X, location.Y, size.Width, size.Height)
|
||||
{
|
||||
}
|
||||
|
||||
public RectangleInfo(SizeInfo size)
|
||||
: this(0, 0, size.Width, size.Height)
|
||||
{
|
||||
}
|
||||
|
||||
public decimal X
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Y
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Width
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Height
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Left => this.X;
|
||||
|
||||
public decimal Top => this.Y;
|
||||
|
||||
public decimal Right => this.X + this.Width;
|
||||
|
||||
public decimal Bottom => this.Y + this.Height;
|
||||
|
||||
public SizeInfo Size => new(this.Width, this.Height);
|
||||
|
||||
public PointInfo Location => new(this.X, this.Y);
|
||||
|
||||
public decimal Area => this.Width * this.Height;
|
||||
|
||||
/// <remarks>
|
||||
/// Adapted from https://github.com/dotnet/runtime
|
||||
/// See https://github.com/dotnet/runtime/blob/dfd618dc648ba9b11dd0f8034f78113d69f223cd/src/libraries/System.Drawing.Primitives/src/System/Drawing/Rectangle.cs
|
||||
/// </remarks>
|
||||
public bool Contains(RectangleInfo rect) =>
|
||||
(this.X <= rect.X) && (rect.X + rect.Width <= this.X + this.Width) &&
|
||||
(this.Y <= rect.Y) && (rect.Y + rect.Height <= this.Y + this.Height);
|
||||
|
||||
public RectangleInfo Enlarge(PaddingInfo padding) => new(
|
||||
this.X + padding.Left,
|
||||
this.Y + padding.Top,
|
||||
this.Width + padding.Horizontal,
|
||||
this.Height + padding.Vertical);
|
||||
|
||||
public RectangleInfo Offset(SizeInfo amount) => this.Offset(amount.Width, amount.Height);
|
||||
|
||||
public RectangleInfo Offset(decimal dx, decimal dy) => new(this.X + dx, this.Y + dy, this.Width, this.Height);
|
||||
|
||||
public RectangleInfo Scale(decimal scalingFactor) => new(
|
||||
this.X * scalingFactor,
|
||||
this.Y * scalingFactor,
|
||||
this.Width * scalingFactor,
|
||||
this.Height * scalingFactor);
|
||||
|
||||
public RectangleInfo Center(PointInfo point) => new(
|
||||
x: point.X - (this.Width / 2),
|
||||
y: point.Y - (this.Height / 2),
|
||||
width: this.Width,
|
||||
height: this.Height);
|
||||
|
||||
public PointInfo Midpoint => new(
|
||||
x: this.X + (this.Width / 2),
|
||||
y: this.Y + (this.Height / 2));
|
||||
|
||||
public RectangleInfo Clamp(RectangleInfo outer)
|
||||
{
|
||||
if ((this.Width > outer.Width) || (this.Height > outer.Height))
|
||||
{
|
||||
throw new ArgumentException($"Value cannot be larger than {nameof(outer)}.");
|
||||
}
|
||||
|
||||
return new(
|
||||
x: Math.Clamp(this.X, outer.X, outer.Right - this.Width),
|
||||
y: Math.Clamp(this.Y, outer.Y, outer.Bottom - this.Height),
|
||||
width: this.Width,
|
||||
height: this.Height);
|
||||
}
|
||||
|
||||
public Rectangle ToRectangle() => new((int)this.X, (int)this.Y, (int)this.Width, (int)this.Height);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "{" +
|
||||
$"{nameof(this.Left)}={this.Left}," +
|
||||
$"{nameof(this.Top)}={this.Top}," +
|
||||
$"{nameof(this.Width)}={this.Width}," +
|
||||
$"{nameof(this.Height)}={this.Height}" +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
// 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;
|
||||
using System.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Models.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable version of a System.Drawing.Size object with some extra utility methods.
|
||||
/// </summary>
|
||||
public sealed class SizeInfo
|
||||
{
|
||||
public SizeInfo(decimal width, decimal height)
|
||||
{
|
||||
this.Width = width;
|
||||
this.Height = height;
|
||||
}
|
||||
|
||||
public SizeInfo(Size size)
|
||||
: this(size.Width, size.Height)
|
||||
{
|
||||
}
|
||||
|
||||
public decimal Width
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Height
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public SizeInfo Negate() => new(-this.Width, -this.Height);
|
||||
|
||||
public SizeInfo Shrink(PaddingInfo padding) => new(this.Width - padding.Horizontal, this.Height - padding.Vertical);
|
||||
|
||||
public SizeInfo Intersect(SizeInfo size) => new(
|
||||
Math.Min(this.Width, size.Width),
|
||||
Math.Min(this.Height, size.Height));
|
||||
|
||||
public RectangleInfo PlaceAt(decimal x, decimal y) => new(x, y, this.Width, this.Height);
|
||||
|
||||
public SizeInfo ScaleToFit(SizeInfo bounds)
|
||||
{
|
||||
var widthRatio = bounds.Width / this.Width;
|
||||
var heightRatio = bounds.Height / this.Height;
|
||||
return widthRatio.CompareTo(heightRatio) switch
|
||||
{
|
||||
< 0 => new(bounds.Width, this.Height * widthRatio),
|
||||
0 => bounds,
|
||||
> 0 => new(this.Width * heightRatio, bounds.Height),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the scaling ratio to scale obj by so that it fits inside the specified bounds
|
||||
/// without distorting the aspect ratio.
|
||||
/// </summary>
|
||||
public decimal ScaleToFitRatio(SizeInfo bounds)
|
||||
{
|
||||
if (bounds.Width == 0 || bounds.Height == 0)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(bounds.Width)} or {nameof(bounds.Height)} cannot be zero", nameof(bounds));
|
||||
}
|
||||
|
||||
var widthRatio = bounds.Width / this.Width;
|
||||
var heightRatio = bounds.Height / this.Height;
|
||||
var scalingRatio = Math.Min(widthRatio, heightRatio);
|
||||
|
||||
return scalingRatio;
|
||||
}
|
||||
|
||||
public Size ToSize() => new((int)this.Width, (int)this.Height);
|
||||
|
||||
public Point ToPoint() => new((int)this.Width, (int)this.Height);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "{" +
|
||||
$"{nameof(this.Width)}={this.Width}," +
|
||||
$"{nameof(this.Height)}={this.Height}" +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
using MouseJumpUI.Models.Drawing;
|
||||
using MouseJumpUI.Models.Screen;
|
||||
|
||||
namespace MouseJumpUI.Models.Layout;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a collection of values needed for calculating the MainForm layout.
|
||||
/// </summary>
|
||||
public sealed class LayoutConfig
|
||||
{
|
||||
public LayoutConfig(
|
||||
RectangleInfo virtualScreenBounds,
|
||||
List<ScreenInfo> screens,
|
||||
PointInfo activatedLocation,
|
||||
int activatedScreenIndex,
|
||||
int activatedScreenNumber,
|
||||
SizeInfo maximumFormSize,
|
||||
PaddingInfo formPadding,
|
||||
PaddingInfo previewPadding)
|
||||
{
|
||||
// make sure the virtual screen entirely contains all of the individual screen bounds
|
||||
ArgumentNullException.ThrowIfNull(virtualScreenBounds);
|
||||
ArgumentNullException.ThrowIfNull(screens);
|
||||
if (screens.Any(screen => !virtualScreenBounds.Contains(screen.Bounds)))
|
||||
{
|
||||
throw new ArgumentException($"'{nameof(virtualScreenBounds)}' must contain all of the screens in '{nameof(screens)}'", nameof(virtualScreenBounds));
|
||||
}
|
||||
|
||||
this.VirtualScreenBounds = virtualScreenBounds;
|
||||
this.Screens = new(screens.ToList());
|
||||
this.ActivatedLocation = activatedLocation;
|
||||
this.ActivatedScreenIndex = activatedScreenIndex;
|
||||
this.ActivatedScreenNumber = activatedScreenNumber;
|
||||
this.MaximumFormSize = maximumFormSize;
|
||||
this.FormPadding = formPadding;
|
||||
this.PreviewPadding = previewPadding;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the coordinates of the entire virtual screen.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The Virtual Screen is the bounding rectangle of all the monitors.
|
||||
/// https://learn.microsoft.com/en-us/windows/win32/gdi/the-virtual-screen
|
||||
/// </remarks>
|
||||
public RectangleInfo VirtualScreenBounds
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection containing the individual screens connected to the system.
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<ScreenInfo> Screens
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the point where the cursor was located when the form was activated.
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// The preview form will be centered on this location unless there are any
|
||||
/// constraints such as being too close to edge of a screen, in which case
|
||||
/// the form will be displayed centered as close as possible to this location.
|
||||
/// </summary>
|
||||
public PointInfo ActivatedLocation
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the screen the cursor was on when the form was activated.
|
||||
/// The value is an index into the ScreenBounds array and is 0-indexed as a result.
|
||||
/// </summary>
|
||||
public int ActivatedScreenIndex
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the screen number the cursor was on when the form was activated.
|
||||
/// The value matches the screen numbering scheme in the "Display Settings" dialog
|
||||
/// and is 1-indexed as a result.
|
||||
/// </summary>
|
||||
public int ActivatedScreenNumber
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum size of the screen preview form.
|
||||
/// </summary>
|
||||
public SizeInfo MaximumFormSize
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the padding border around the screen preview form.
|
||||
/// </summary>
|
||||
public PaddingInfo FormPadding
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the padding border inside the screen preview image.
|
||||
/// </summary>
|
||||
public PaddingInfo PreviewPadding
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
using MouseJumpUI.Models.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Models.Layout;
|
||||
|
||||
public sealed class LayoutInfo
|
||||
{
|
||||
public sealed class Builder
|
||||
{
|
||||
public Builder()
|
||||
{
|
||||
this.ScreenBounds = new();
|
||||
}
|
||||
|
||||
public LayoutConfig? LayoutConfig
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public RectangleInfo? FormBounds
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public RectangleInfo? PreviewBounds
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public List<RectangleInfo> ScreenBounds
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public RectangleInfo? ActivatedScreenBounds
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public LayoutInfo Build()
|
||||
{
|
||||
return new LayoutInfo(
|
||||
layoutConfig: this.LayoutConfig ?? throw new InvalidOperationException(),
|
||||
formBounds: this.FormBounds ?? throw new InvalidOperationException(),
|
||||
previewBounds: this.PreviewBounds ?? throw new InvalidOperationException(),
|
||||
screenBounds: this.ScreenBounds ?? throw new InvalidOperationException(),
|
||||
activatedScreenBounds: this.ActivatedScreenBounds ?? throw new InvalidOperationException());
|
||||
}
|
||||
}
|
||||
|
||||
public LayoutInfo(
|
||||
LayoutConfig layoutConfig,
|
||||
RectangleInfo formBounds,
|
||||
RectangleInfo previewBounds,
|
||||
IEnumerable<RectangleInfo> screenBounds,
|
||||
RectangleInfo activatedScreenBounds)
|
||||
{
|
||||
this.LayoutConfig = layoutConfig ?? throw new ArgumentNullException(nameof(layoutConfig));
|
||||
this.FormBounds = formBounds ?? throw new ArgumentNullException(nameof(formBounds));
|
||||
this.PreviewBounds = previewBounds ?? throw new ArgumentNullException(nameof(previewBounds));
|
||||
this.ScreenBounds = new(
|
||||
(screenBounds ?? throw new ArgumentNullException(nameof(screenBounds)))
|
||||
.ToList());
|
||||
this.ActivatedScreenBounds = activatedScreenBounds ?? throw new ArgumentNullException(nameof(activatedScreenBounds));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the original LayoutConfig settings used to calculate coordinates.
|
||||
/// </summary>
|
||||
public LayoutConfig LayoutConfig
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size and location of the preview form.
|
||||
/// </summary>
|
||||
public RectangleInfo FormBounds
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size and location of the preview image.
|
||||
/// </summary>
|
||||
public RectangleInfo PreviewBounds
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ReadOnlyCollection<RectangleInfo> ScreenBounds
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public RectangleInfo ActivatedScreenBounds
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
// 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;
|
||||
|
||||
using MouseJumpUI.Models.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Models.Screen;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable version of a System.Windows.Forms.Screen object so we don't need to
|
||||
/// take a dependency on WinForms just for screen info.
|
||||
/// </summary>
|
||||
public sealed class ScreenInfo
|
||||
{
|
||||
internal ScreenInfo(int handle, bool primary, RectangleInfo displayArea, RectangleInfo workingArea)
|
||||
{
|
||||
this.Handle = handle;
|
||||
this.Primary = primary;
|
||||
this.DisplayArea = displayArea ?? throw new ArgumentNullException(nameof(displayArea));
|
||||
this.WorkingArea = workingArea ?? throw new ArgumentNullException(nameof(workingArea));
|
||||
}
|
||||
|
||||
public int Handle
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public bool Primary
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public RectangleInfo DisplayArea
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public RectangleInfo Bounds =>
|
||||
this.DisplayArea;
|
||||
|
||||
public RectangleInfo WorkingArea
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Threading;
|
||||
@@ -97,29 +95,4 @@ internal static class Program
|
||||
cancellationTokenSource.Cancel();
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
private static MouseJumpSettings ReadSettings()
|
||||
{
|
||||
var settingsUtils = new SettingsUtils();
|
||||
var settingsPath = settingsUtils.GetSettingsFilePath(MouseJumpSettings.ModuleName);
|
||||
if (!File.Exists(settingsPath))
|
||||
{
|
||||
var scaffoldSettings = new MouseJumpSettings();
|
||||
settingsUtils.SaveSettings(JsonSerializer.Serialize(scaffoldSettings), MouseJumpSettings.ModuleName);
|
||||
}
|
||||
|
||||
var settings = new MouseJumpSettings();
|
||||
try
|
||||
{
|
||||
settings = settingsUtils.GetSettings<MouseJumpSettings>(MouseJumpSettings.ModuleName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var errorMessage = $"There was a problem reading the configuration file. Error: {ex.GetType()} {ex.Message}";
|
||||
Logger.LogInfo(errorMessage);
|
||||
Logger.LogDebug(errorMessage);
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<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 new.base.rc new.rc" />
|
||||
</Target>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{0db0f63a-d2f8-4da3-a650-2d0b8724218e}</ProjectGuid>
|
||||
<RootNamespace>NewPlusShellExtensionWin10</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.22621.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</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)\WinUI3Apps\</OutDir>
|
||||
<TargetName>PowerToys.NewPlus.ShellExtension.win10</TargetName>
|
||||
<LinkIncremental />
|
||||
<IgnoreImportLibrary />
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;NEWPLUSSHELLEXTENSIONWIN10_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<AdditionalIncludeDirectories>..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories);..\NewShellExtensionContextMenu</AdditionalIncludeDirectories>
|
||||
<CompileAsWinRT>false</CompileAsWinRT>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalDependencies>runtimeobject.lib;$(CoreLibraryDependencies)</AdditionalDependencies>
|
||||
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;NEWPLUSSHELLEXTENSIONWIN10_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<AdditionalIncludeDirectories>..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories);..\NewShellExtensionContextMenu</AdditionalIncludeDirectories>
|
||||
<CompileAsWinRT>false</CompileAsWinRT>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalDependencies>runtimeobject.lib;$(CoreLibraryDependencies)</AdditionalDependencies>
|
||||
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\constants.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\new_utilities.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\settings.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu_item.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\template_folder.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\template_item.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\trace.h" />
|
||||
<ClInclude Include="dll_main.h" />
|
||||
<ClInclude Include="Generated Files\resource.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.base.h" />
|
||||
<ClInclude Include="shell_context_menu_win10.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\new_utilities.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\powertoys_module.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\settings.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\shell_context_sub_menu_item.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\template_folder.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\template_item.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\trace.cpp" />
|
||||
<ClCompile Include="dll_main.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="shell_context_menu_win10.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Generated Files\new.rc" />
|
||||
<None Include="new.base.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="resources.resx">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\common\Telemetry\EtwTrace\EtwTrace.vcxproj">
|
||||
<Project>{8f021b46-362b-485c-bfba-ccf83e820cbd}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
|
||||
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="dll.def" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,116 @@
|
||||
<?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>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Generated Files">
|
||||
<UniqueIdentifier>{4cea4fff-ccef-4b62-9e46-f33da2b9a0cc}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="shell_context_menu_win10.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.base.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Generated Files\resource.h">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\new_utilities.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu_item.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\template_folder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\template_item.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\trace.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\constants.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="dll_main.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\settings.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="dll_main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="shell_context_menu_win10.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\template_item.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\template_folder.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\shell_context_sub_menu_item.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\powertoys_module.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\settings.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\trace.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\new_utilities.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Generated Files\new.rc">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="dll.def">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
<None Include="new.base.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
<None Include="resources.resx">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,6 @@
|
||||
LIBRARY
|
||||
|
||||
EXPORTS
|
||||
DllGetClassObject PRIVATE
|
||||
DllCanUnloadNow PRIVATE
|
||||
DllGetActivationFactory PRIVATE
|
||||
@@ -0,0 +1,44 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "shell_context_menu_win10.h"
|
||||
#include "dll_main.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include <common/Telemetry/EtwTrace/EtwTrace.h>
|
||||
|
||||
HMODULE module_instance_handle = 0;
|
||||
Shared::Trace::ETWTrace trace(L"NewPlusShellExtension_Win10");
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE module_handle, DWORD ul_reason_for_call, LPVOID reserved)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
module_instance_handle = module_handle;
|
||||
Trace::RegisterProvider();
|
||||
newplus::utilities::init_logger();
|
||||
break;
|
||||
|
||||
case DLL_PROCESS_DETACH:
|
||||
Trace::UnregisterProvider();
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
STDAPI DllGetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ IActivationFactory** factory)
|
||||
{
|
||||
return Module<ModuleType::InProc>::GetModule().GetActivationFactory(activatableClassId, factory);
|
||||
}
|
||||
|
||||
STDAPI DllCanUnloadNow()
|
||||
{
|
||||
return Module<InProc>::GetModule().GetObjectCount() == 0 ? S_OK : S_FALSE;
|
||||
}
|
||||
|
||||
STDAPI DllGetClassObject(_In_ REFCLSID ref_class_id, _In_ REFIID ref_interface_id, _Outptr_ LPVOID FAR* object)
|
||||
{
|
||||
return Module<InProc>::GetModule().GetClassObject(ref_class_id, ref_interface_id, object);
|
||||
}
|
||||
|
||||
CoCreatableClass(shell_context_menu_win10)
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/Telemetry/EtwTrace/EtwTrace.h>
|
||||
|
||||
extern HMODULE module_instance_handle;
|
||||
extern Shared::Trace::ETWTrace trace;
|
||||
@@ -0,0 +1,54 @@
|
||||
#include <windows.h>
|
||||
#include "Generated Files/resource.h"
|
||||
#include "../../../../common/version/version.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
#include "winres.h"
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION FILE_VERSION
|
||||
PRODUCTVERSION PRODUCT_VERSION
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", COMPANY_NAME
|
||||
VALUE "FileDescription", FILE_DESCRIPTION
|
||||
VALUE "FileVersion", FILE_VERSION_STRING
|
||||
VALUE "InternalName", INTERNAL_NAME
|
||||
VALUE "LegalCopyright", COPYRIGHT_NOTE
|
||||
VALUE "OriginalFilename", ORIGINAL_FILENAME
|
||||
VALUE "ProductName", PRODUCT_NAME
|
||||
VALUE "ProductVersion", PRODUCT_VERSION_STRING
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -0,0 +1,3 @@
|
||||
// pch.cpp: source file corresponding to the pre-compiled header
|
||||
|
||||
#include "pch.h"
|
||||
39
src/modules/NewPlus/NewShellExtensionContextMenu.win10/pch.h
Normal file
39
src/modules/NewPlus/NewShellExtensionContextMenu.win10/pch.h
Normal file
@@ -0,0 +1,39 @@
|
||||
// Precompiled header file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMCX
|
||||
#define NOHELP
|
||||
#define NOCOMM
|
||||
|
||||
// Windows and STL
|
||||
#include <Shobjidl.h>
|
||||
#include <shlwapi.h>
|
||||
#include <shellapi.h>
|
||||
#include <Windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <vector>
|
||||
#include <system_error>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <atlbase.h>
|
||||
#include <wrl.h>
|
||||
#include <wrl/module.h>
|
||||
#include <wrl/client.h>
|
||||
#include <unknwn.h>
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
// PowerToys project common
|
||||
#include <ProjectTelemetry.h>
|
||||
#include <common/utils/resources.h>
|
||||
#include <common/logger/logger.h>
|
||||
#include <common/logger/logger_settings.h>
|
||||
#include <common/utils/logger_helper.h>
|
||||
#include <common/Themes/theme_helpers.h>
|
||||
|
||||
// New project specific
|
||||
#include "dll_main.h"
|
||||
#include "template_folder.h"
|
||||
#include "settings.h"
|
||||
#include "new_utilities.h"
|
||||
@@ -0,0 +1,12 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
|
||||
//////////////////////////////
|
||||
// Non-localizable
|
||||
|
||||
#define FILE_DESCRIPTION "PowerToys.New+"
|
||||
#define INTERNAL_NAME "PowerToys.New+"
|
||||
#define ORIGINAL_FILENAME "PowerToys.NewPlus.ShellExtension.win10.dll"
|
||||
|
||||
// Non-localizable
|
||||
//////////////////////////////
|
||||
@@ -0,0 +1,132 @@
|
||||
<?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="context_menu_item_new" xml:space="preserve">
|
||||
<value>New+</value>
|
||||
<comment>The main context menu item that users click on. This should be localized to match New in Windows. e.g. Danish it would become Ny+</comment>
|
||||
</data>
|
||||
<data name="context_menu_item_open_templates" xml:space="preserve">
|
||||
<value>Open templates</value>
|
||||
<comment>The menu item in the context menu that enables user to open the folder that contains their templates.</comment>
|
||||
</data>
|
||||
<data name="default_template_sub_folder_name_where_templates_are_stored" xml:space="preserve">
|
||||
<value>Templates</value>
|
||||
<comment>Default subfolder name where templates are stored.</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,270 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "shell_context_menu_win10.h"
|
||||
#include "shell_context_sub_menu.h"
|
||||
#include "shell_context_sub_menu_item.h"
|
||||
#include "new_utilities.h"
|
||||
#include "settings.h"
|
||||
#include "trace.h"
|
||||
#include "Generated Files/resource.h"
|
||||
#include <common/Themes/icon_helpers.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace newplus;
|
||||
|
||||
shell_context_menu_win10::~shell_context_menu_win10()
|
||||
{
|
||||
for (const auto& handle : bitmap_handles)
|
||||
{
|
||||
DeleteObject(handle);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma region IShellExtInit
|
||||
IFACEMETHODIMP shell_context_menu_win10::Initialize(PCIDLIST_ABSOLUTE, IDataObject*, HKEY)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IContextMenu
|
||||
IFACEMETHODIMP shell_context_menu_win10::QueryContextMenu(HMENU menu_handle, UINT menu_index, UINT menu_first_cmd_id, UINT, UINT menu_flags)
|
||||
{
|
||||
if (!NewSettingsInstance().GetEnabled()
|
||||
|| package::IsWin11OrGreater()
|
||||
)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
if (menu_flags & CMF_DEFAULTONLY)
|
||||
{
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Create the initial context popup menu containing the list of templates and open templates action
|
||||
int menu_id = menu_first_cmd_id;
|
||||
MENUITEMINFO newplus_main_context_menu_item;
|
||||
HMENU sub_menu_of_templates = CreatePopupMenu();
|
||||
int sub_menu_index = 0;
|
||||
|
||||
// Determine the New+ Template folder location
|
||||
const std::filesystem::path template_folder_root = utilities::get_new_template_folder_location();
|
||||
|
||||
// Create the New+ Template folder location if it doesn't exist (very rare scenario)
|
||||
utilities::create_folder_if_not_exist(template_folder_root);
|
||||
|
||||
// Scan the folder for any files and folders (the templates)
|
||||
templates = new template_folder(template_folder_root);
|
||||
templates->rescan_template_folder();
|
||||
const auto number_of_templates = templates->list_of_templates.size();
|
||||
|
||||
// Create the New+ menu item and point to the initial context popup menu
|
||||
static const std::wstring localized_context_menu_item =
|
||||
GET_RESOURCE_STRING_FALLBACK(IDS_CONTEXT_MENU_ITEM_NEW, L"New+");
|
||||
wchar_t newplus_menu_name[20] = { 0 };
|
||||
wcscpy_s(newplus_menu_name, ARRAYSIZE(newplus_menu_name), localized_context_menu_item.c_str());
|
||||
newplus_main_context_menu_item.cbSize = sizeof(MENUITEMINFOW);
|
||||
newplus_main_context_menu_item.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_SUBMENU;
|
||||
newplus_main_context_menu_item.wID = menu_id;
|
||||
newplus_main_context_menu_item.fType = MFT_STRING;
|
||||
newplus_main_context_menu_item.dwTypeData = (PWSTR)newplus_menu_name;
|
||||
newplus_main_context_menu_item.hSubMenu = sub_menu_of_templates;
|
||||
const auto newplus_icon_index = 0;
|
||||
|
||||
if (bitmap_handles.size() == 0)
|
||||
{
|
||||
const std::wstring icon_file = utilities::get_new_icon_resource_filepath(
|
||||
module_instance_handle, ThemeHelpers::GetAppTheme())
|
||||
.c_str();
|
||||
HICON local_icon_handle = static_cast<HICON>(
|
||||
LoadImage(NULL, icon_file.c_str(), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_LOADFROMFILE));
|
||||
|
||||
if (local_icon_handle)
|
||||
{
|
||||
bitmap_handles.push_back(CreateBitmapFromIcon(local_icon_handle));
|
||||
DestroyIcon(local_icon_handle);
|
||||
}
|
||||
}
|
||||
if (bitmap_handles.size() > newplus_icon_index && bitmap_handles[newplus_icon_index])
|
||||
{
|
||||
newplus_main_context_menu_item.fMask |= MIIM_BITMAP;
|
||||
newplus_main_context_menu_item.hbmpItem = bitmap_handles[newplus_icon_index];
|
||||
}
|
||||
|
||||
menu_id++;
|
||||
|
||||
// Add template items to context menu
|
||||
int index = 0;
|
||||
for (; index < number_of_templates; index++)
|
||||
{
|
||||
const auto template_item = templates->get_template_item(index);
|
||||
add_template_item_to_context_menu(sub_menu_of_templates, sub_menu_index, template_item, menu_id, index);
|
||||
menu_id++;
|
||||
sub_menu_index++;
|
||||
}
|
||||
|
||||
// Add separator to context menu
|
||||
add_separator_to_context_menu(sub_menu_of_templates, sub_menu_index);
|
||||
sub_menu_index++;
|
||||
|
||||
// Add "Open templates" item to context menu
|
||||
add_open_templates_to_context_menu(sub_menu_of_templates, sub_menu_index, template_folder_root, menu_id, index);
|
||||
menu_id++;
|
||||
|
||||
if (!InsertMenuItem(menu_handle, menu_index, TRUE, &newplus_main_context_menu_item))
|
||||
{
|
||||
Logger::error(L"QueryContextMenu() failed. {}", get_last_error_or_default(GetLastError()));
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return the amount if entries inserted
|
||||
const auto number_of_items_inserted = menu_id - menu_first_cmd_id;
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, number_of_items_inserted);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
Logger::error(ex.what());
|
||||
}
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
void shell_context_menu_win10::add_open_templates_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, const std::filesystem::path& template_folder_root, int menu_id, int index)
|
||||
{
|
||||
static const std::wstring localized_context_menu_item_open_templates =
|
||||
GET_RESOURCE_STRING_FALLBACK(IDS_CONTEXT_MENU_ITEM_OPEN_TEMPLATES, L"Open templates");
|
||||
wchar_t menu_name_open[256] = { 0 };
|
||||
wcscpy_s(menu_name_open, ARRAYSIZE(menu_name_open), localized_context_menu_item_open_templates.c_str());
|
||||
const auto open_folder_item = Make<template_folder_context_menu_item>(template_folder_root);
|
||||
MENUITEMINFO newplus_menu_item_open_templates;
|
||||
newplus_menu_item_open_templates.cbSize = sizeof(MENUITEMINFO);
|
||||
newplus_menu_item_open_templates.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
|
||||
newplus_menu_item_open_templates.wID = menu_id;
|
||||
newplus_menu_item_open_templates.fType = MFT_STRING;
|
||||
newplus_menu_item_open_templates.dwTypeData = (PWSTR)menu_name_open;
|
||||
|
||||
const auto open_templates_icon_index = index + 1;
|
||||
if (bitmap_handles.size() <= open_templates_icon_index)
|
||||
{
|
||||
const std::wstring icon_file = utilities::get_open_templates_icon_resource_filepath(
|
||||
module_instance_handle, ThemeHelpers::GetAppTheme())
|
||||
.c_str();
|
||||
HICON open_template_icon_handle = static_cast<HICON>(
|
||||
LoadImage(NULL, icon_file.c_str(), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_LOADFROMFILE));
|
||||
if (open_template_icon_handle)
|
||||
{
|
||||
bitmap_handles.push_back(CreateBitmapFromIcon(open_template_icon_handle));
|
||||
DestroyIcon(open_template_icon_handle);
|
||||
}
|
||||
}
|
||||
if (bitmap_handles.size() > open_templates_icon_index && bitmap_handles[open_templates_icon_index])
|
||||
{
|
||||
newplus_menu_item_open_templates.fMask |= MIIM_BITMAP;
|
||||
newplus_menu_item_open_templates.hbmpItem = bitmap_handles[open_templates_icon_index];
|
||||
}
|
||||
|
||||
InsertMenuItem(sub_menu_of_templates, sub_menu_index, TRUE, &newplus_menu_item_open_templates);
|
||||
}
|
||||
|
||||
void shell_context_menu_win10::add_separator_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index)
|
||||
{
|
||||
MENUITEMINFO menu_item_separator;
|
||||
menu_item_separator.cbSize = sizeof(MENUITEMINFO);
|
||||
menu_item_separator.fMask = MIIM_FTYPE;
|
||||
menu_item_separator.fType = MFT_SEPARATOR;
|
||||
InsertMenuItem(sub_menu_of_templates, sub_menu_index, TRUE, &menu_item_separator);
|
||||
}
|
||||
|
||||
void shell_context_menu_win10::add_template_item_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, newplus::template_item* const template_item, int menu_id, int index)
|
||||
{
|
||||
wchar_t menu_name[256] = { 0 };
|
||||
wcscpy_s(menu_name, ARRAYSIZE(menu_name), template_item->get_menu_title(!utilities::get_newplus_setting_hide_extension(), !utilities::get_newplus_setting_hide_starting_digits()).c_str());
|
||||
MENUITEMINFO newplus_menu_item_template;
|
||||
newplus_menu_item_template.cbSize = sizeof(MENUITEMINFO);
|
||||
newplus_menu_item_template.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_DATA;
|
||||
newplus_menu_item_template.wID = menu_id;
|
||||
newplus_menu_item_template.fType = MFT_STRING;
|
||||
newplus_menu_item_template.dwTypeData = (PWSTR)menu_name;
|
||||
const auto current_template_icon_index = index + 1;
|
||||
if (bitmap_handles.size() <= current_template_icon_index)
|
||||
{
|
||||
HICON template_icon_handle = template_item->get_explorer_icon_handle();
|
||||
if (template_icon_handle)
|
||||
{
|
||||
bitmap_handles.push_back(CreateBitmapFromIcon(template_icon_handle));
|
||||
DestroyIcon(template_icon_handle);
|
||||
}
|
||||
}
|
||||
if (bitmap_handles.size() > current_template_icon_index && bitmap_handles[current_template_icon_index])
|
||||
{
|
||||
newplus_menu_item_template.fMask |= MIIM_BITMAP;
|
||||
newplus_menu_item_template.hbmpItem = bitmap_handles[current_template_icon_index];
|
||||
}
|
||||
|
||||
InsertMenuItem(sub_menu_of_templates, sub_menu_index, TRUE, &newplus_menu_item_template);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_menu_win10::InvokeCommand(CMINVOKECOMMANDINFO* params)
|
||||
{
|
||||
if (!params)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// Get selected menu item (a template or the "Open templates" item)
|
||||
const auto selected_menu_item_index = LOWORD(params->lpVerb) - 1;
|
||||
if (selected_menu_item_index < 0)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
const auto number_of_templates = templates->list_of_templates.size();
|
||||
const bool is_template_item = selected_menu_item_index < number_of_templates;
|
||||
|
||||
// Save how many item templates we have so it can be sent later when we do something with New+.
|
||||
// It will be sent when the user does something, similar to Windows 11 context menu.
|
||||
newplus::utilities::set_saved_number_of_templates(static_cast<size_t>(number_of_templates));
|
||||
|
||||
if (is_template_item)
|
||||
{
|
||||
// It's a template menu item
|
||||
const auto template_entry = templates->get_template_item(selected_menu_item_index);
|
||||
|
||||
return newplus::utilities::copy_template(template_entry, site_of_folder);
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's the "Open templates" menu item
|
||||
const std::filesystem::path template_folder_root = utilities::get_new_template_folder_location();
|
||||
|
||||
return newplus::utilities::open_template_folder(template_folder_root);
|
||||
}
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_menu_win10::GetCommandString(UINT_PTR, UINT, UINT*, CHAR*, UINT)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IObjectWithSite
|
||||
IFACEMETHODIMP shell_context_menu_win10::SetSite(_In_ IUnknown* site) noexcept
|
||||
{
|
||||
this->site_of_folder = site;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
IFACEMETHODIMP shell_context_menu_win10::GetSite(_In_ REFIID riid, _COM_Outptr_ void** returned_site) noexcept
|
||||
{
|
||||
return this->site_of_folder.CopyTo(riid, returned_site);
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include <template_folder.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
#define NEW_SHELL_EXTENSION_EXPLORER_COMMAND_WIN10_UUID "FF90D477-E32A-4BE8-8CC5-A502A97F5401"
|
||||
|
||||
// File Explorer context menu "New+" for Windows 10
|
||||
class __declspec(uuid(NEW_SHELL_EXTENSION_EXPLORER_COMMAND_WIN10_UUID)) shell_context_menu_win10 :
|
||||
public RuntimeClass<
|
||||
RuntimeClassFlags<ClassicCom>,
|
||||
IShellExtInit,
|
||||
IContextMenu,
|
||||
IObjectWithSite>
|
||||
{
|
||||
public:
|
||||
~shell_context_menu_win10();
|
||||
|
||||
#pragma region IShellExtInit
|
||||
IFACEMETHODIMP Initialize(_In_opt_ PCIDLIST_ABSOLUTE, _In_ IDataObject*, HKEY);
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IContextMenu
|
||||
IFACEMETHODIMP QueryContextMenu(HMENU menu_handle, UINT menu_index, UINT menu_first_cmd_id, UINT, UINT menu_flags);
|
||||
IFACEMETHODIMP InvokeCommand(CMINVOKECOMMANDINFO* pici);
|
||||
IFACEMETHODIMP GetCommandString(UINT_PTR, UINT, UINT*, CHAR*, UINT);
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IObjectWithSite
|
||||
IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept;
|
||||
IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept;
|
||||
#pragma endregion
|
||||
|
||||
protected:
|
||||
void add_open_templates_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, const std::filesystem::path& template_folder_root, int menu_id, int index);
|
||||
void add_separator_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index);
|
||||
void add_template_item_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, newplus::template_item* const template_item, int menu_id, int index);
|
||||
|
||||
HINSTANCE instance_handle = 0;
|
||||
ComPtr<IUnknown> site_of_folder;
|
||||
newplus::template_folder* templates = nullptr;
|
||||
std::vector<HBITMAP> bitmap_handles;
|
||||
};
|
||||
@@ -128,6 +128,7 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
|
||||
<ClInclude Include="template_item.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="new_utilities.cpp" />
|
||||
<ClCompile Include="shell_context_menu.cpp" />
|
||||
<ClCompile Include="shell_context_sub_menu.cpp" />
|
||||
<ClCompile Include="shell_context_sub_menu_item.cpp" />
|
||||
@@ -227,7 +228,7 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
<ClCompile Include="trace.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="new_utilities.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="template_folder.h">
|
||||
@@ -123,9 +126,6 @@
|
||||
<None Include="AppxManifest.xml">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\New.ico">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\SmallTile.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
@@ -190,14 +190,8 @@
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="TemplateExamples\Example folder\Another example txt file.txt">
|
||||
<Filter>Template Examples\Example folder</Filter>
|
||||
</Text>
|
||||
<Text Include="TemplateExamples\Example folder\Example txt file.txt">
|
||||
<Filter>Template Examples\Example folder</Filter>
|
||||
</Text>
|
||||
<Text Include="TemplateExamples\Any files or folders placed in the template folder are available via New+.txt">
|
||||
<Filter>Template Examples</Filter>
|
||||
</Text>
|
||||
<CopyFileToFolders Include="TemplateExamples\Any files or folders placed in the template folder are available via New+.txt" />
|
||||
<CopyFileToFolders Include="TemplateExamples\Example folder\Example txt file.txt" />
|
||||
<CopyFileToFolders Include="TemplateExamples\Example folder\Another example txt file.txt" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "pch.h"
|
||||
#include "new_utilities.h"
|
||||
|
||||
// HACK: Store number of templates when generating the menu entries to send later.
|
||||
size_t saved_number_of_templates = -1;
|
||||
size_t newplus::utilities::get_saved_number_of_templates()
|
||||
{
|
||||
return saved_number_of_templates;
|
||||
}
|
||||
void newplus::utilities::set_saved_number_of_templates(size_t templates)
|
||||
{
|
||||
saved_number_of_templates = templates;
|
||||
}
|
||||
@@ -7,11 +7,17 @@
|
||||
|
||||
#include "constants.h"
|
||||
#include "settings.h"
|
||||
#include "template_item.h"
|
||||
#include "trace.h"
|
||||
|
||||
#pragma comment(lib, "Shlwapi.lib")
|
||||
|
||||
using namespace newplus;
|
||||
|
||||
namespace newplus::utilities
|
||||
{
|
||||
size_t get_saved_number_of_templates();
|
||||
void set_saved_number_of_templates(size_t templates);
|
||||
|
||||
inline std::wstring get_explorer_icon(std::filesystem::path path)
|
||||
{
|
||||
@@ -39,6 +45,33 @@ namespace newplus::utilities
|
||||
return icon_resource;
|
||||
}
|
||||
|
||||
inline HICON get_explorer_icon_handle(std::filesystem::path path)
|
||||
{
|
||||
SHFILEINFO shell_file_info = { 0 };
|
||||
const std::wstring filepath = path.wstring();
|
||||
DWORD_PTR result = SHGetFileInfo(filepath.c_str(), 0, &shell_file_info, sizeof(shell_file_info), SHGFI_ICON);
|
||||
if (shell_file_info.hIcon)
|
||||
{
|
||||
return shell_file_info.hIcon;
|
||||
}
|
||||
|
||||
WCHAR icon_resource_specifier[MAX_PATH] = { 0 };
|
||||
DWORD buffer_length = MAX_PATH;
|
||||
const std::wstring extension = path.extension().wstring();
|
||||
const HRESULT hr = AssocQueryString(ASSOCF_INIT_IGNOREUNKNOWN,
|
||||
ASSOCSTR_DEFAULTICON,
|
||||
extension.c_str(),
|
||||
NULL,
|
||||
icon_resource_specifier,
|
||||
&buffer_length);
|
||||
const std::wstring icon_resource = icon_resource_specifier;
|
||||
|
||||
const auto icon_x = GetSystemMetrics(SM_CXSMICON);
|
||||
const auto icon_y = GetSystemMetrics(SM_CYSMICON);
|
||||
HICON hIcon = static_cast<HICON>(LoadImage(NULL, icon_resource.c_str(), IMAGE_ICON, icon_x, icon_y, LR_LOADFROMFILE));
|
||||
return hIcon;
|
||||
}
|
||||
|
||||
inline bool is_hidden(const std::filesystem::path path)
|
||||
{
|
||||
const std::filesystem::path::string_type name = path.filename();
|
||||
@@ -180,4 +213,227 @@ namespace newplus::utilities
|
||||
return path;
|
||||
}
|
||||
|
||||
inline bool is_desktop_folder(const std::filesystem::path target_fullpath)
|
||||
{
|
||||
TCHAR desktopPath[MAX_PATH];
|
||||
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktopPath)))
|
||||
{
|
||||
return StrCmpIW(target_fullpath.c_str(), desktopPath) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void explorer_enter_rename_mode(const std::filesystem::path target_fullpath_of_new_instance)
|
||||
{
|
||||
const std::filesystem::path path_without_new_file_or_dir = target_fullpath_of_new_instance.parent_path();
|
||||
const std::filesystem::path new_file_or_dir_without_path = target_fullpath_of_new_instance.filename();
|
||||
|
||||
ComPtr<IShellWindows> shell_windows;
|
||||
|
||||
HRESULT hr;
|
||||
if (FAILED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL, IID_PPV_ARGS(&shell_windows))))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long window_handle;
|
||||
ComPtr<IDispatch> shell_window;
|
||||
const bool object_created_on_desktop = is_desktop_folder(path_without_new_file_or_dir.c_str());
|
||||
if (object_created_on_desktop)
|
||||
{
|
||||
// Special handling for desktop folder
|
||||
VARIANT empty_yet_needed_incl_init;
|
||||
VariantInit(&empty_yet_needed_incl_init);
|
||||
|
||||
if (FAILED(shell_windows->FindWindowSW(&empty_yet_needed_incl_init, &empty_yet_needed_incl_init, SWC_DESKTOP, &window_handle, SWFO_NEEDDISPATCH, &shell_window)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
long count_of_shell_windows = 0;
|
||||
shell_windows->get_Count(&count_of_shell_windows);
|
||||
|
||||
for (long i = 0; i < count_of_shell_windows; ++i)
|
||||
{
|
||||
ComPtr<IWebBrowserApp> web_browser_app;
|
||||
VARIANT v;
|
||||
V_VT(&v) = VT_I4;
|
||||
V_I4(&v) = i;
|
||||
hr = shell_windows->Item(v, &shell_window);
|
||||
if (SUCCEEDED(hr) && shell_window)
|
||||
{
|
||||
hr = shell_window.As(&web_browser_app);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
BSTR folder_view_location;
|
||||
hr = web_browser_app->get_LocationURL(&folder_view_location);
|
||||
if (SUCCEEDED(hr) && folder_view_location)
|
||||
{
|
||||
wchar_t path[MAX_PATH];
|
||||
DWORD pathLength = ARRAYSIZE(path);
|
||||
hr = PathCreateFromUrl(folder_view_location, path, &pathLength, 0);
|
||||
SysFreeString(folder_view_location);
|
||||
if (SUCCEEDED(hr) && StrCmpIW(path_without_new_file_or_dir.c_str(), path) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
shell_window = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shell_window)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ComPtr<IServiceProvider> service_provider;
|
||||
shell_window.As(&service_provider);
|
||||
ComPtr<IShellBrowser> shell_browser;
|
||||
service_provider->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&shell_browser));
|
||||
ComPtr<IShellView> shell_view;
|
||||
shell_browser->QueryActiveShellView(&shell_view);
|
||||
ComPtr<IFolderView> folder_view;
|
||||
shell_view.As(&folder_view);
|
||||
|
||||
// Find the newly created object (file or folder)
|
||||
// And put object into edit mode (SVSI_EDIT) and if desktop also reposition
|
||||
int number_of_objects_in_view = 0;
|
||||
bool done = false;
|
||||
folder_view->ItemCount(SVGIO_ALLVIEW, &number_of_objects_in_view);
|
||||
for (int i = 0; i < number_of_objects_in_view && !done; ++i)
|
||||
{
|
||||
std::wstring path_of_item(MAX_PATH, 0);
|
||||
LPITEMIDLIST shell_item_ids;
|
||||
|
||||
folder_view->Item(i, &shell_item_ids);
|
||||
SHGetPathFromIDList(shell_item_ids, &path_of_item[0]);
|
||||
|
||||
const std::wstring current_filename = std::filesystem::path(path_of_item.c_str()).filename();
|
||||
|
||||
if (utilities::wstring_same_when_comparing_ignore_case(new_file_or_dir_without_path, current_filename))
|
||||
{
|
||||
const DWORD common_select_flags = SVSI_EDIT | SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE | SVSI_FOCUSED;
|
||||
|
||||
if (object_created_on_desktop)
|
||||
{
|
||||
// Newly created object is on the desktop -- reposition under mouse and enter rename mode
|
||||
LPCITEMIDLIST shell_item_to_select_and_position[] = { shell_item_ids };
|
||||
POINT mouse_position;
|
||||
GetCursorPos(&mouse_position);
|
||||
mouse_position.x -= GetSystemMetrics(SM_CXMENUSIZE);
|
||||
mouse_position.x = max(mouse_position.x, 20);
|
||||
mouse_position.y -= GetSystemMetrics(SM_CXMENUSIZE)/2;
|
||||
mouse_position.y = max(mouse_position.y, 20);
|
||||
POINT position[] = { mouse_position };
|
||||
folder_view->SelectAndPositionItems(1, shell_item_to_select_and_position, position, common_select_flags | SVSI_POSITIONITEM);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Enter rename mode
|
||||
folder_view->SelectItem(i, common_select_flags);
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
CoTaskMemFree(shell_item_ids);
|
||||
}
|
||||
}
|
||||
|
||||
inline HRESULT copy_template(const template_item* template_entry, const ComPtr<IUnknown> site_of_folder)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
try
|
||||
{
|
||||
Logger::info(L"Copying template");
|
||||
|
||||
if (newplus::utilities::get_saved_number_of_templates() >= 0)
|
||||
{
|
||||
// Log that context menu was shown and with how many items
|
||||
trace.UpdateState(true);
|
||||
Trace::EventShowTemplateItems(newplus::utilities::get_saved_number_of_templates());
|
||||
trace.Flush();
|
||||
trace.UpdateState(false);
|
||||
}
|
||||
|
||||
// Determine target path of where context menu was displayed
|
||||
const auto target_path_name = utilities::get_path_from_unknown_site(site_of_folder);
|
||||
|
||||
// Determine initial filename
|
||||
std::filesystem::path source_fullpath = template_entry->path;
|
||||
std::filesystem::path target_fullpath = std::wstring(target_path_name);
|
||||
|
||||
// Only append name to target if source is not a directory
|
||||
if (!utilities::is_directory(source_fullpath))
|
||||
{
|
||||
target_fullpath.append(template_entry->get_target_filename(!utilities::get_newplus_setting_hide_starting_digits()));
|
||||
}
|
||||
|
||||
// Copy file and determine final filename
|
||||
std::filesystem::path target_final_fullpath = template_entry->copy_object_to(GetActiveWindow(), target_fullpath);
|
||||
|
||||
trace.UpdateState(true);
|
||||
Trace::EventCopyTemplate(target_final_fullpath.extension().c_str());
|
||||
trace.Flush();
|
||||
trace.UpdateState(false);
|
||||
|
||||
// Refresh folder items
|
||||
template_entry->refresh_target(target_final_fullpath);
|
||||
|
||||
// Enter rename mode
|
||||
template_entry->enter_rename_mode(target_final_fullpath);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
Logger::error(ex.what());
|
||||
|
||||
hr = S_FALSE;
|
||||
}
|
||||
|
||||
trace.UpdateState(true);
|
||||
Trace::EventCopyTemplateResult(hr);
|
||||
trace.Flush();
|
||||
trace.UpdateState(false);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
inline HRESULT open_template_folder(const std::filesystem::path template_folder)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
try
|
||||
{
|
||||
Logger::info(L"Open templates folder");
|
||||
|
||||
if (newplus::utilities::get_saved_number_of_templates() >= 0)
|
||||
{
|
||||
// Log that context menu was shown and with how many items
|
||||
trace.UpdateState(true);
|
||||
Trace::EventShowTemplateItems(newplus::utilities::get_saved_number_of_templates());
|
||||
trace.Flush();
|
||||
trace.UpdateState(false);
|
||||
}
|
||||
|
||||
const std::wstring verb_hardcoded_do_not_change = L"open";
|
||||
ShellExecute(nullptr, verb_hardcoded_do_not_change.c_str(), template_folder.c_str(), NULL, NULL, SW_SHOWNORMAL);
|
||||
|
||||
trace.UpdateState(true);
|
||||
Trace::EventOpenTemplates();
|
||||
trace.Flush();
|
||||
trace.UpdateState(false);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
Logger::error(ex.what());
|
||||
|
||||
hr = S_FALSE;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ public:
|
||||
|
||||
#pragma region IObjectWithSite
|
||||
IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept;
|
||||
|
||||
IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept;
|
||||
#pragma endregion
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "pch.h"
|
||||
#include "shell_context_sub_menu.h"
|
||||
#include "trace.h"
|
||||
#include "new_utilities.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
// // Sub context menu command enumerator
|
||||
shell_context_sub_menu::shell_context_sub_menu(const ComPtr<IUnknown> site_of_folder)
|
||||
{
|
||||
trace.UpdateState(true);
|
||||
this->site_of_folder = site_of_folder;
|
||||
|
||||
// Determine the New+ Template folder location
|
||||
@@ -36,8 +36,9 @@ shell_context_sub_menu::shell_context_sub_menu(const ComPtr<IUnknown> site_of_fo
|
||||
|
||||
current_command = explorer_menu_item_commands.cbegin();
|
||||
|
||||
// Log that context menu was shown and with how many items
|
||||
Trace::EventShowTemplateItems(number_of_templates);
|
||||
// Save how many item templates we have so it can be sent later when we do something with New+.
|
||||
// We don't send it here or it would send an event every time we open a context menu.
|
||||
newplus::utilities::set_saved_number_of_templates(static_cast<size_t>(number_of_templates));
|
||||
}
|
||||
|
||||
// IEnumExplorerCommand
|
||||
|
||||
@@ -63,49 +63,7 @@ IFACEMETHODIMP shell_context_sub_menu_item::GetState(_In_opt_ IShellItemArray* s
|
||||
|
||||
IFACEMETHODIMP shell_context_sub_menu_item::Invoke(_In_opt_ IShellItemArray*, _In_opt_ IBindCtx*) noexcept
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
try
|
||||
{
|
||||
trace.UpdateState(true);
|
||||
|
||||
// Determine target path of where context menu was displayed
|
||||
const auto target_path_name = utilities::get_path_from_unknown_site(site_of_folder);
|
||||
|
||||
// Determine initial filename
|
||||
std::filesystem::path source_fullpath = template_entry->path;
|
||||
std::filesystem::path target_fullpath = std::wstring(target_path_name);
|
||||
|
||||
// Only append name to target if source is not a directory
|
||||
if (!utilities::is_directory(target_fullpath))
|
||||
{
|
||||
target_fullpath.append(this->template_entry->get_target_filename(!utilities::get_newplus_setting_hide_starting_digits()));
|
||||
}
|
||||
|
||||
// Copy file and determine final filename
|
||||
std::filesystem::path target_final_fullpath = this->template_entry->copy_object_to(GetActiveWindow(), target_fullpath);
|
||||
|
||||
Trace::EventCopyTemplate(target_final_fullpath.extension().c_str());
|
||||
|
||||
// Refresh folder items
|
||||
SHChangeNotify(SHCNE_CREATE, SHCNF_PATH | SHCNF_FLUSH, target_final_fullpath.wstring().c_str(), NULL);
|
||||
|
||||
// Enter rename mode
|
||||
this->template_entry->enter_rename_mode(site_of_folder, target_final_fullpath);
|
||||
|
||||
Trace::EventCopyTemplateResult(S_OK);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
Trace::EventCopyTemplateResult(S_FALSE);
|
||||
Logger::error(ex.what());
|
||||
|
||||
hr = S_FALSE;
|
||||
}
|
||||
|
||||
trace.Flush();
|
||||
trace.UpdateState(false);
|
||||
|
||||
return hr;
|
||||
return newplus::utilities::copy_template(template_entry, site_of_folder);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_sub_menu_item::GetFlags(_Out_ EXPCMDFLAGS* returned_flags)
|
||||
@@ -162,9 +120,5 @@ IFACEMETHODIMP template_folder_context_menu_item::GetIcon(_In_opt_ IShellItemArr
|
||||
|
||||
IFACEMETHODIMP template_folder_context_menu_item::Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept
|
||||
{
|
||||
Logger::info(L"Open templates folder");
|
||||
const std::wstring verb_hardcoded_do_not_change = L"open";
|
||||
ShellExecute(nullptr, verb_hardcoded_do_not_change.c_str(), shell_template_folder.c_str(), NULL, NULL, SW_SHOWNORMAL);
|
||||
|
||||
return S_OK;
|
||||
return newplus::utilities::open_template_folder(shell_template_folder);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,11 @@ template_folder::template_folder(const std::filesystem::path newplus_template_fo
|
||||
this->template_folder_path = newplus_template_folder;
|
||||
}
|
||||
|
||||
template_folder::~template_folder()
|
||||
{
|
||||
list_of_templates.clear();
|
||||
}
|
||||
|
||||
void template_folder::init()
|
||||
{
|
||||
rescan_template_folder();
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace newplus
|
||||
{
|
||||
public:
|
||||
template_folder(const std::filesystem::path newplus_template_folder);
|
||||
~template_folder();
|
||||
|
||||
void rescan_template_folder();
|
||||
|
||||
std::filesystem::path template_folder_path;
|
||||
|
||||
@@ -63,6 +63,11 @@ std::wstring template_item::get_explorer_icon() const
|
||||
return utilities::get_explorer_icon(path);
|
||||
}
|
||||
|
||||
HICON template_item::get_explorer_icon_handle() const
|
||||
{
|
||||
return utilities::get_explorer_icon_handle(path);
|
||||
}
|
||||
|
||||
std::filesystem::path template_item::copy_object_to(const HWND window_handle, const std::filesystem::path destination) const
|
||||
{
|
||||
// SHFILEOPSTRUCT wants the from and to paths to be terminated with two NULLs,
|
||||
@@ -86,6 +91,14 @@ std::filesystem::path template_item::copy_object_to(const HWND window_handle, co
|
||||
if (!file_operation_params.hNameMappings)
|
||||
{
|
||||
// No file name collision on copy
|
||||
if (utilities::is_directory(this->path))
|
||||
{
|
||||
// Append dir for consistency on directory naming inclusion for with and without collision
|
||||
std::filesystem::path with_dir = destination;
|
||||
with_dir /= this->path.filename();
|
||||
return with_dir;
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
@@ -104,44 +117,23 @@ std::filesystem::path template_item::copy_object_to(const HWND window_handle, co
|
||||
return final_path;
|
||||
}
|
||||
|
||||
void template_item::enter_rename_mode(const ComPtr<IUnknown> site, const std::filesystem::path target_fullpath) const
|
||||
void template_item::refresh_target(const std::filesystem::path target_final_fullpath) const
|
||||
{
|
||||
std::thread thread_for_renaming_workaround(rename_on_other_thread_workaround, site, target_fullpath);
|
||||
SHChangeNotify(SHCNE_CREATE, SHCNF_PATH | SHCNF_FLUSH, target_final_fullpath.wstring().c_str(), NULL);
|
||||
}
|
||||
|
||||
void template_item::enter_rename_mode(const std::filesystem::path target_fullpath) const
|
||||
{
|
||||
std::thread thread_for_renaming_workaround(rename_on_other_thread_workaround, target_fullpath);
|
||||
thread_for_renaming_workaround.detach();
|
||||
}
|
||||
|
||||
void template_item::rename_on_other_thread_workaround(const ComPtr<IUnknown> site, const std::filesystem::path target_fullpath)
|
||||
void template_item::rename_on_other_thread_workaround(const std::filesystem::path target_fullpath)
|
||||
{
|
||||
// Have been unable to have Windows Explorer Shell enter rename mode from the main thread
|
||||
// Sleep for a bit to only enter rename mode when icon has been drawn. Not strictly needed.
|
||||
const std::chrono::milliseconds approx_wait_for_icon_redraw_not_needed{ 350 };
|
||||
// Sleep for a bit to only enter rename mode when icon has been drawn.
|
||||
const std::chrono::milliseconds approx_wait_for_icon_redraw_not_needed{ 50 };
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(approx_wait_for_icon_redraw_not_needed));
|
||||
|
||||
const std::wstring filename = target_fullpath.filename();
|
||||
|
||||
ComPtr<IServiceProvider> service_provider;
|
||||
site->QueryInterface(IID_PPV_ARGS(&service_provider));
|
||||
ComPtr<IFolderView> folder_view;
|
||||
service_provider->QueryService(__uuidof(IFolderView), IID_PPV_ARGS(&folder_view));
|
||||
|
||||
int count = 0;
|
||||
folder_view->ItemCount(SVGIO_ALLVIEW, &count);
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
std::wstring path_of_item(MAX_PATH, 0);
|
||||
LPITEMIDLIST pidl;
|
||||
|
||||
folder_view->Item(i, &pidl);
|
||||
SHGetPathFromIDList(pidl, &path_of_item[0]);
|
||||
CoTaskMemFree(pidl);
|
||||
|
||||
std::wstring current_filename = std::filesystem::path(path_of_item.c_str()).filename();
|
||||
|
||||
if (utilities::wstring_same_when_comparing_ignore_case(filename, current_filename))
|
||||
{
|
||||
folder_view->SelectItem(i, SVSI_EDIT | SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE | SVSI_FOCUSED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
newplus::utilities::explorer_enter_rename_mode(target_fullpath);
|
||||
}
|
||||
|
||||
@@ -20,15 +20,19 @@ namespace newplus
|
||||
std::wstring get_target_filename(const bool include_starting_digits) const;
|
||||
|
||||
std::wstring get_explorer_icon() const;
|
||||
|
||||
HICON get_explorer_icon_handle() const;
|
||||
|
||||
std::filesystem::path copy_object_to(const HWND window_handle, const std::filesystem::path destination) const;
|
||||
|
||||
void enter_rename_mode(const ComPtr<IUnknown> site, const std::filesystem::path target_folder) const;
|
||||
void refresh_target(const std::filesystem::path target_final_fullpath) const;
|
||||
|
||||
void enter_rename_mode(const std::filesystem::path target_fullpath) const;
|
||||
|
||||
std::filesystem::path path;
|
||||
|
||||
private:
|
||||
static void rename_on_other_thread_workaround(const ComPtr<IUnknown> site, const std::filesystem::path target_fullpath);
|
||||
static void rename_on_other_thread_workaround(const std::filesystem::path target_fullpath);
|
||||
|
||||
std::wstring remove_starting_digits_from_filename(std::wstring filename) const;
|
||||
};
|
||||
|
||||
@@ -58,3 +58,12 @@ void Trace::EventCopyTemplateResult(_In_ const HRESULT hr) noexcept
|
||||
TraceLoggingHResult(hr),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
void Trace::EventOpenTemplates() noexcept
|
||||
{
|
||||
TraceLoggingWriteWrapper(
|
||||
g_hProvider,
|
||||
"NewPlus_EventOpenTemplates",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
@@ -12,4 +12,5 @@ public:
|
||||
static void EventShowTemplateItems(_In_ const size_t number_of_templates) noexcept;
|
||||
static void EventCopyTemplate(_In_ const std::wstring template_file_extension) noexcept;
|
||||
static void EventCopyTemplateResult(_In_ const HRESULT hr) noexcept;
|
||||
static void EventOpenTemplates() noexcept;
|
||||
};
|
||||
|
||||
@@ -68,6 +68,7 @@ namespace Microsoft.Plugin.WindowWalker.Components
|
||||
searchResult.Result.SwitchToWindow();
|
||||
return true;
|
||||
},
|
||||
Score = searchResult.Score,
|
||||
|
||||
// For debugging you can set the second parameter to true to see more information.
|
||||
ToolTipData = GetToolTip(searchResult.Result, false),
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -22,6 +23,7 @@ using Peek.FilePreviewer.Models;
|
||||
using Peek.FilePreviewer.Previewers.Helpers;
|
||||
using Peek.FilePreviewer.Previewers.Interfaces;
|
||||
using Windows.Foundation;
|
||||
using Windows.Graphics.Imaging;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
@@ -58,6 +60,12 @@ namespace Peek.FilePreviewer.Previewers
|
||||
|
||||
private DispatcherQueue Dispatcher { get; }
|
||||
|
||||
private static readonly HashSet<string> _supportedFileTypes =
|
||||
BitmapDecoder.GetDecoderInformationEnumerator()
|
||||
.SelectMany(di => di.FileExtensions)
|
||||
.Union([".svg", ".qoi"])
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public static bool IsItemSupported(IFileSystemItem item)
|
||||
{
|
||||
return _supportedFileTypes.Contains(item.Extension);
|
||||
@@ -199,74 +207,5 @@ namespace Peek.FilePreviewer.Previewers
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> _supportedFileTypes = new HashSet<string>
|
||||
{
|
||||
// Image types
|
||||
".bmp",
|
||||
".gif",
|
||||
".jpg",
|
||||
".jfif",
|
||||
".jfi",
|
||||
".jif",
|
||||
".jpeg",
|
||||
".jpe",
|
||||
".png",
|
||||
".tif", // very slow for large files: no thumbnail?
|
||||
".tiff", // NEED TO TEST
|
||||
".dib", // NEED TO TEST
|
||||
".heic",
|
||||
".heif",
|
||||
".hif", // NEED TO TEST
|
||||
".avif", // NEED TO TEST
|
||||
".jxr",
|
||||
".wdp",
|
||||
".ico", // NEED TO TEST
|
||||
".thumb", // NEED TO TEST
|
||||
".webp",
|
||||
|
||||
// Raw types
|
||||
".arw",
|
||||
".cr2",
|
||||
".crw",
|
||||
".erf",
|
||||
".kdc", // NEED TO TEST
|
||||
".mrw",
|
||||
".nef",
|
||||
".nrw",
|
||||
".orf",
|
||||
".pef",
|
||||
".raf",
|
||||
".raw",
|
||||
".rw2",
|
||||
".rwl",
|
||||
".sr2",
|
||||
".srw",
|
||||
".srf",
|
||||
".dcs", // NEED TO TEST
|
||||
".dcr",
|
||||
".drf", // NEED TO TEST
|
||||
".k25",
|
||||
".3fr",
|
||||
".ari", // NEED TO TEST
|
||||
".bay", // NEED TO TEST
|
||||
".cap", // NEED TO TEST
|
||||
".iiq",
|
||||
".eip", // NEED TO TEST
|
||||
".fff",
|
||||
".mef",
|
||||
|
||||
// ".mdc", // Crashes in GetFullBitmapFromPathAsync
|
||||
".mos",
|
||||
".R3D",
|
||||
".rwz", // NEED TO TEST
|
||||
".x3f",
|
||||
".ori",
|
||||
".cr3",
|
||||
|
||||
".svg",
|
||||
|
||||
".qoi",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,11 @@ namespace Peek.FilePreviewer.Previewers
|
||||
|
||||
public static HashSet<string> GetExtensions()
|
||||
{
|
||||
HashSet<string> set = new HashSet<string>();
|
||||
HashSet<string> set = [];
|
||||
|
||||
try
|
||||
{
|
||||
JsonDocument languageListDocument = Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.GetLanguages();
|
||||
using JsonDocument languageListDocument = Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.GetLanguages();
|
||||
JsonElement languageList = languageListDocument.RootElement.GetProperty("list");
|
||||
foreach (JsonElement e in languageList.EnumerateArray())
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" />
|
||||
<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')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
@@ -209,7 +209,7 @@
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
@@ -223,8 +223,8 @@
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
</Target>
|
||||
<Target Name="AddWildCardItems" AfterTargets="BuildGenerateSources">
|
||||
<ItemGroup>
|
||||
|
||||
@@ -65,4 +65,7 @@
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="PowerRenameUI.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -6,5 +6,5 @@
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.22621.2428" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.6.240923002" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.6.241114003" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -14,10 +14,113 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x44);
|
||||
|
||||
[JsonPropertyName("activation_shortcut")]
|
||||
public HotkeySettings ActivationShortcut { get; set; }
|
||||
public HotkeySettings ActivationShortcut
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("thumbnail_size")]
|
||||
public MouseJumpThumbnailSize ThumbnailSize { get; set; }
|
||||
public MouseJumpThumbnailSize ThumbnailSize
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the preview type.
|
||||
/// Allowed values are "compact", "bezelled", "custom"
|
||||
/// </summary>
|
||||
[JsonPropertyName("preview_type")]
|
||||
public string PreviewType
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("background_color_1")]
|
||||
public string BackgroundColor1
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("background_color_2")]
|
||||
public string BackgroundColor2
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("border_thickness")]
|
||||
public int BorderThickness
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("border_color")]
|
||||
public string BorderColor
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("border_3d_depth")]
|
||||
public int Border3dDepth
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("border_padding")]
|
||||
public int BorderPadding
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("bezel_thickness")]
|
||||
public int BezelThickness
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("bezel_color")]
|
||||
public string BezelColor
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("bezel_3d_depth")]
|
||||
public int Bezel3dDepth
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("screen_margin")]
|
||||
public int ScreenMargin
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("screen_color_1")]
|
||||
public string ScreenColor1
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("screen_color_2")]
|
||||
public string ScreenColor2
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public MouseJumpProperties()
|
||||
{
|
||||
|
||||
@@ -7,6 +7,8 @@ using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using MouseJump.Common.Helpers;
|
||||
using MouseJump.Common.Models.Settings;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
@@ -26,7 +28,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
Name = ModuleName;
|
||||
Properties = new MouseJumpProperties();
|
||||
Version = "1.0";
|
||||
Version = "1.1";
|
||||
}
|
||||
|
||||
public void Save(ISettingsUtils settingsUtils)
|
||||
@@ -47,7 +49,74 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
// This can be utilized in the future if the settings.json file is to be modified/deleted.
|
||||
public bool UpgradeSettingsConfiguration()
|
||||
{
|
||||
return false;
|
||||
/*
|
||||
v1.0 - initial version
|
||||
|
||||
* DefaultActivationShortcut
|
||||
* activation_shortcut
|
||||
* thumbnail_size
|
||||
* name
|
||||
* version
|
||||
*/
|
||||
var upgraded = false;
|
||||
|
||||
if (this.Version == "1.0")
|
||||
{
|
||||
/*
|
||||
v1.1 - added preview style settings
|
||||
|
||||
* preview_type
|
||||
* background_color_1
|
||||
* background_color_2
|
||||
* border_thickness
|
||||
* border_color
|
||||
* border_3d_depth
|
||||
* border_padding
|
||||
* bezel_thickness
|
||||
* bezel_color
|
||||
* bezel_3d_depth
|
||||
* screen_margin
|
||||
* screen_color_1
|
||||
* screen_color_2
|
||||
*/
|
||||
this.Version = "1.1";
|
||||
|
||||
// note - there's an issue where ITwoWayPipeMessageIPCManagedMethods.Send overwrites
|
||||
// the settings file version as "1.0" regardless of the actual version. as a result,
|
||||
// the UpgradeSettingsConfiguration can get triggered even if the config has already
|
||||
// been upgraded, so we need to do an additional check to make sure values haven't
|
||||
// already been upgraded before we overwrite them with default values.
|
||||
if (string.IsNullOrEmpty(this.Properties.PreviewType))
|
||||
{
|
||||
// set default values for custom preview style
|
||||
var previewStyle = StyleHelper.BezelledPreviewStyle;
|
||||
this.Properties.PreviewType = PreviewType.Bezelled.ToString();
|
||||
this.Properties.BackgroundColor1 = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(previewStyle.CanvasStyle.BackgroundStyle.Color1));
|
||||
this.Properties.BackgroundColor2 = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(previewStyle.CanvasStyle.BackgroundStyle.Color2));
|
||||
this.Properties.BorderThickness = (int)previewStyle.CanvasStyle.BorderStyle.Top;
|
||||
this.Properties.BorderColor = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(previewStyle.CanvasStyle.BorderStyle.Color));
|
||||
this.Properties.Border3dDepth = (int)previewStyle.CanvasStyle.BorderStyle.Depth;
|
||||
this.Properties.BorderPadding = (int)previewStyle.CanvasStyle.PaddingStyle.Top;
|
||||
this.Properties.BezelThickness = (int)previewStyle.ScreenStyle.BorderStyle.Top;
|
||||
this.Properties.BezelColor = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(previewStyle.ScreenStyle.BorderStyle.Color));
|
||||
this.Properties.Bezel3dDepth = (int)previewStyle.ScreenStyle.BorderStyle.Depth;
|
||||
this.Properties.ScreenMargin = (int)previewStyle.ScreenStyle.MarginStyle.Top;
|
||||
this.Properties.ScreenColor1 = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(previewStyle.ScreenStyle.BackgroundStyle.Color1));
|
||||
this.Properties.ScreenColor2 = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(previewStyle.ScreenStyle.BackgroundStyle.Color2));
|
||||
}
|
||||
|
||||
// we still need to flag the settings as "upgraded" so that the new version gets written
|
||||
// back to the config file, even if we didn't actually change and setting values
|
||||
upgraded = true;
|
||||
}
|
||||
|
||||
return upgraded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<ProjectReference Include="..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||
<ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
|
||||
<ProjectReference Include="..\..\modules\MouseUtils\MouseJump.Common\MouseJump.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// 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;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using MouseJump.Common.Models.Settings;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Converters
|
||||
{
|
||||
public sealed partial class MouseJumpPreviewTypeConverter : IValueConverter
|
||||
{
|
||||
private static readonly PreviewType[] PreviewTypeOrder =
|
||||
[
|
||||
PreviewType.Compact, PreviewType.Bezelled, PreviewType.Custom,
|
||||
];
|
||||
|
||||
private static readonly PreviewType DefaultPreviewType = PreviewType.Bezelled;
|
||||
|
||||
// Receives a string as a parameter and returns an int representing the index
|
||||
// to select in the Segmented control on the Mouse Jump settings page
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
var previewType = MouseJumpPreviewTypeConverter.DefaultPreviewType;
|
||||
|
||||
if (value is not string previewTypeName)
|
||||
{
|
||||
// the value isn't a string so just use the default preview type
|
||||
}
|
||||
else if (Enum.IsDefined(typeof(PreviewType), previewTypeName))
|
||||
{
|
||||
// there's a case-sensitive match for the value
|
||||
previewType = Enum.Parse<PreviewType>(previewTypeName);
|
||||
}
|
||||
else if (Enum.TryParse<PreviewType>(previewTypeName, true, out var previewTypeResult))
|
||||
{
|
||||
// there's a case-insensitive match for the value
|
||||
previewType = previewTypeResult;
|
||||
}
|
||||
|
||||
return Array.IndexOf(
|
||||
MouseJumpPreviewTypeConverter.PreviewTypeOrder,
|
||||
previewType);
|
||||
}
|
||||
|
||||
// Receives an int as a parameter that represents the selected index in the Segmented
|
||||
// control on the Mouse Jump settings page, and returns the name of the PreviewType enum
|
||||
// for that index.
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
var previewType = MouseJumpPreviewTypeConverter.DefaultPreviewType;
|
||||
|
||||
if (value is not int segmentedIndex)
|
||||
{
|
||||
// the value isn't an int so just use the default preview type
|
||||
}
|
||||
else if ((segmentedIndex < 0) || (segmentedIndex > MouseJumpPreviewTypeConverter.PreviewTypeOrder.Length))
|
||||
{
|
||||
// not a valid selected index so just use the default preview type
|
||||
}
|
||||
else
|
||||
{
|
||||
previewType = MouseJumpPreviewTypeConverter.PreviewTypeOrder[segmentedIndex];
|
||||
}
|
||||
|
||||
return previewType.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
src/settings-ui/Settings.UI/Images/MouseJump-Desktop.png
Normal file
BIN
src/settings-ui/Settings.UI/Images/MouseJump-Desktop.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@@ -49,6 +49,11 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Images\MouseJump-Desktop.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Animations" />
|
||||
@@ -84,6 +89,7 @@
|
||||
<ProjectReference Include="..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||
<ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
|
||||
<ProjectReference Include="..\..\modules\MouseUtils\MouseJump.Common\MouseJump.Common.csproj" />
|
||||
<ProjectReference Include="..\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -122,6 +128,9 @@
|
||||
<Page Update="SettingsXAML\OOBE\Views\OobeWorkspaces.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
<Page Update="SettingsXAML\Panels\MouseJumpPanel.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
<Page Update="SettingsXAML\Views\WorkspacesPage.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="32,0,0,16"
|
||||
Margin="32,16,0,16"
|
||||
VerticalAlignment="Top"
|
||||
Orientation="Vertical">
|
||||
<TextBlock
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
<UserControl
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Panels.MouseJumpPanel"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
|
||||
xmlns:converters="using:Microsoft.PowerToys.Settings.UI.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
AutomationProperties.LandmarkType="Main"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<converters:MouseJumpPreviewTypeConverter x:Key="MouseJumpPreviewTypeConverter" />
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///CommunityToolkit.WinUI.Controls.Segmented/Segmented/Segmented.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
<controls:SettingsGroup x:Uid="MouseUtils_MouseJump">
|
||||
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="MouseUtils_Enable_MouseJump"
|
||||
HeaderIcon="{ui:BitmapIcon Source=/Assets/Settings/Icons/MouseJump.png}"
|
||||
IsEnabled="{x:Bind ViewModel.IsJumpEnabledGpoConfigured, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}">
|
||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.IsMouseJumpEnabled, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
|
||||
<InfoBar
|
||||
x:Uid="GPO_SettingIsManaged"
|
||||
IsClosable="False"
|
||||
IsOpen="{x:Bind ViewModel.IsJumpEnabledGpoConfigured, Mode=OneWay}"
|
||||
IsTabStop="{x:Bind ViewModel.IsJumpEnabledGpoConfigured, Mode=OneWay}"
|
||||
Severity="Informational" />
|
||||
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="MouseUtils_MouseJump_ActivationShortcut"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}"
|
||||
IsEnabled="{x:Bind ViewModel.IsMouseJumpEnabled, Mode=OneWay}">
|
||||
<controls:ShortcutControl MinWidth="{StaticResource SettingActionControlMinWidth}" HotkeySettings="{x:Bind Path=ViewModel.MouseJumpActivationShortcut, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
|
||||
<tkcontrols:SettingsCard
|
||||
x:Name="MouseUtils_MouseJump_ThumbnailSize"
|
||||
x:Uid="MouseUtils_MouseJump_ThumbnailSize"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}"
|
||||
IsEnabled="{x:Bind ViewModel.IsMouseJumpEnabled, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard.Description>
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="0,4,0,0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
x:Uid="MouseUtils_MouseJump_ThumbnailSize_Description_Prefix"
|
||||
Margin="0,0,4,0"
|
||||
Style="{ThemeResource SecondaryTextStyle}" />
|
||||
<TextBlock
|
||||
Margin="0,0,4,0"
|
||||
FontWeight="SemiBold"
|
||||
Style="{ThemeResource SecondaryTextStyle}"
|
||||
Text="{x:Bind ViewModel.MouseJumpThumbnailSize.Width, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Margin="0,5,4,0"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="10"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Style="{ThemeResource SecondaryTextStyle}"
|
||||
Text="" />
|
||||
<TextBlock
|
||||
Margin="0,0,4,0"
|
||||
FontWeight="SemiBold"
|
||||
Style="{ThemeResource SecondaryTextStyle}"
|
||||
Text="{x:Bind ViewModel.MouseJumpThumbnailSize.Height, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
x:Uid="MouseUtils_MouseJump_ThumbnailSize_Description_Suffix"
|
||||
Margin="0,0,4,0"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Style="{ThemeResource SecondaryTextStyle}" />
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard.Description>
|
||||
<StackPanel
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal"
|
||||
Spacing="8">
|
||||
<Button
|
||||
x:Uid="EditButton"
|
||||
Width="40"
|
||||
Height="36"
|
||||
Content=""
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
Style="{StaticResource SubtleButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="EditTooltip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Flyout>
|
||||
<Flyout x:Uid="MouseJumpThumbnailSize_Edit" ShouldConstrainToRootBounds="False">
|
||||
<StackPanel Spacing="16">
|
||||
<NumberBox
|
||||
x:Uid="MouseUtils_MouseJump_ThumbnailSize_Edit_Width"
|
||||
Width="140"
|
||||
Minimum="160"
|
||||
SpinButtonPlacementMode="Compact"
|
||||
Value="{x:Bind ViewModel.MouseJumpThumbnailSize.Width, Mode=TwoWay}" />
|
||||
<NumberBox
|
||||
x:Uid="MouseUtils_MouseJump_ThumbnailSize_Edit_Height"
|
||||
Width="140"
|
||||
Minimum="120"
|
||||
SpinButtonPlacementMode="Compact"
|
||||
Value="{x:Bind ViewModel.MouseJumpThumbnailSize.Height, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard>
|
||||
|
||||
<tkcontrols:SettingsExpander
|
||||
x:Uid="MouseUtils_MouseJump_Appearance"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}"
|
||||
IsEnabled="{x:Bind ViewModel.IsMouseJumpEnabled, Mode=OneWay}"
|
||||
IsExpanded="False">
|
||||
<tkcontrols:SettingsExpander.Items>
|
||||
<tkcontrols:SettingsCard
|
||||
x:Name="MouseUtils_MouseJump_PreviewImage"
|
||||
MinHeight="300"
|
||||
MaxHeight="300"
|
||||
Loaded="PreviewImage_Loaded">
|
||||
<Grid
|
||||
MinHeight="283"
|
||||
MaxHeight="283"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Image Source="{x:Bind Path=ViewModel.MouseJumpPreviewImage, Mode=OneWay}" Stretch="None" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Name="MouseUtils_MouseJump_PreviewType" x:Uid="MouseUtils_MouseJump_PreviewType">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<tkcontrols:Segmented
|
||||
x:Name="PreviewTypeSetting"
|
||||
SelectedIndex="{x:Bind ViewModel.MouseJumpPreviewType, Mode=TwoWay, Converter={StaticResource MouseJumpPreviewTypeConverter}}"
|
||||
SelectionChanged="PreviewTypeSetting_SelectionChanged"
|
||||
SelectionMode="Single"
|
||||
Style="{StaticResource ButtonSegmentedStyle}">
|
||||
<tkcontrols:SegmentedItem>
|
||||
<TextBlock x:Uid="MouseUtils_MouseJump_PreviewType_Compact" />
|
||||
</tkcontrols:SegmentedItem>
|
||||
<tkcontrols:SegmentedItem>
|
||||
<TextBlock x:Uid="MouseUtils_MouseJump_PreviewType_Bezelled" />
|
||||
</tkcontrols:SegmentedItem>
|
||||
<tkcontrols:SegmentedItem>
|
||||
<TextBlock x:Uid="MouseUtils_MouseJump_PreviewType_Custom" />
|
||||
</tkcontrols:SegmentedItem>
|
||||
</tkcontrols:Segmented>
|
||||
<Button
|
||||
x:Name="CopyStyleToCustom"
|
||||
x:Uid="MouseUtils_MouseJump_CopyStyle"
|
||||
Margin="20,0,0,0"
|
||||
Click="CopyStyleToCustom_Click"
|
||||
IsEnabled="{Binding SelectedIndex, ElementName=PreviewTypeSetting}" />
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Name="MouseUtils_MouseJump_BackgroundColor1" x:Uid="MouseUtils_MouseJump_BackgroundColor1">
|
||||
<controls:ColorPickerButton SelectedColor="{x:Bind Path=ViewModel.MouseJumpBackgroundColor1, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Name="MouseUtils_MouseJump_BackgroundColor2" x:Uid="MouseUtils_MouseJump_BackgroundColor2">
|
||||
<controls:ColorPickerButton SelectedColor="{x:Bind Path=ViewModel.MouseJumpBackgroundColor2, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Name="MouseUtils_MouseJump_BorderThickness" x:Uid="MouseUtils_MouseJump_BorderThickness">
|
||||
<NumberBox
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
LargeChange="1"
|
||||
Maximum="25"
|
||||
Minimum="0"
|
||||
SmallChange="1"
|
||||
SpinButtonPlacementMode="Compact"
|
||||
Value="{x:Bind ViewModel.MouseJumpBorderThickness, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Name="MouseUtils_MouseJump_BorderColor" x:Uid="MouseUtils_MouseJump_BorderColor">
|
||||
<controls:ColorPickerButton SelectedColor="{x:Bind Path=ViewModel.MouseJumpBorderColor, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Name="MouseUtils_MouseJump_Border3dDepth" x:Uid="MouseUtils_MouseJump_Border3dDepth">
|
||||
<NumberBox
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
LargeChange="1"
|
||||
Maximum="25"
|
||||
Minimum="0"
|
||||
SmallChange="1"
|
||||
SpinButtonPlacementMode="Compact"
|
||||
Value="{x:Bind ViewModel.MouseJumpBorder3dDepth, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Name="MouseUtils_MouseJump_BorderPadding" x:Uid="MouseUtils_MouseJump_BorderPadding">
|
||||
<NumberBox
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
LargeChange="1"
|
||||
Maximum="25"
|
||||
Minimum="0"
|
||||
SmallChange="1"
|
||||
SpinButtonPlacementMode="Compact"
|
||||
Value="{x:Bind ViewModel.MouseJumpBorderPadding, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Name="MouseUtils_MouseJump_BezelThickness" x:Uid="MouseUtils_MouseJump_BezelThickness">
|
||||
<NumberBox
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
LargeChange="1"
|
||||
Maximum="25"
|
||||
Minimum="0"
|
||||
SmallChange="1"
|
||||
SpinButtonPlacementMode="Compact"
|
||||
Value="{x:Bind ViewModel.MouseJumpBezelThickness, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Name="MouseUtils_MouseJump_BezelColor" x:Uid="MouseUtils_MouseJump_BezelColor">
|
||||
<controls:ColorPickerButton SelectedColor="{x:Bind Path=ViewModel.MouseJumpBezelColor, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Name="MouseUtils_MouseJump_Bezel3dDepth" x:Uid="MouseUtils_MouseJump_Bezel3dDepth">
|
||||
<NumberBox
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
LargeChange="1"
|
||||
Maximum="25"
|
||||
Minimum="0"
|
||||
SmallChange="1"
|
||||
SpinButtonPlacementMode="Compact"
|
||||
Value="{x:Bind ViewModel.MouseJumpBezel3dDepth, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Name="MouseUtils_MouseJump_ScreenMargin" x:Uid="MouseUtils_MouseJump_ScreenMargin">
|
||||
<NumberBox
|
||||
MinWidth="{StaticResource SettingActionControlMinWidth}"
|
||||
LargeChange="1"
|
||||
Maximum="25"
|
||||
Minimum="0"
|
||||
SmallChange="1"
|
||||
SpinButtonPlacementMode="Compact"
|
||||
Value="{x:Bind ViewModel.MouseJumpScreenMargin, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Name="MouseUtils_MouseJump_ScreenColor1" x:Uid="MouseUtils_MouseJump_ScreenColor1">
|
||||
<controls:ColorPickerButton SelectedColor="{x:Bind Path=ViewModel.MouseJumpScreenColor1, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard x:Name="MouseUtils_MouseJump_ScreenColor2" x:Uid="MouseUtils_MouseJump_ScreenColor2">
|
||||
<controls:ColorPickerButton SelectedColor="{x:Bind Path=ViewModel.MouseJumpScreenColor2, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</tkcontrols:SettingsExpander.Items>
|
||||
</tkcontrols:SettingsExpander>
|
||||
</controls:SettingsGroup>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,159 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.WinUI;
|
||||
using CommunityToolkit.WinUI.Controls;
|
||||
using Microsoft.PowerToys.Settings.UI.ViewModels;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using MouseJump.Common.Helpers;
|
||||
using MouseJump.Common.Models.Settings;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Panels
|
||||
{
|
||||
public sealed partial class MouseJumpPanel : UserControl
|
||||
{
|
||||
internal MouseUtilsViewModel ViewModel { get; set; }
|
||||
|
||||
public MouseJumpPanel()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void PreviewImage_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
bool TryFindFrameworkElement(SettingsCard settingsCard, string partName, out FrameworkElement result)
|
||||
{
|
||||
result = settingsCard.FindDescendants()
|
||||
.OfType<FrameworkElement>()
|
||||
.FirstOrDefault(
|
||||
x => x.Name == partName);
|
||||
return result is not null;
|
||||
}
|
||||
|
||||
/*
|
||||
apply a variation of the "Left" VisualState for SettingsCards
|
||||
to center the preview image in the true center of the card
|
||||
see https://github.com/CommunityToolkit/Windows/blob/9c7642ff35eaaa51a404f9bcd04b10c7cf851921/components/SettingsControls/src/SettingsCard/SettingsCard.xaml#L334-L347
|
||||
*/
|
||||
|
||||
var settingsCard = (SettingsCard)sender;
|
||||
|
||||
var partNames = new List<string>
|
||||
{
|
||||
"PART_HeaderIconPresenterHolder",
|
||||
"PART_DescriptionPresenter",
|
||||
"PART_HeaderPresenter",
|
||||
"PART_ActionIconPresenter",
|
||||
};
|
||||
foreach (var partName in partNames)
|
||||
{
|
||||
if (!TryFindFrameworkElement(settingsCard, partName, out var element))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
element.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
if (TryFindFrameworkElement(settingsCard, "PART_ContentPresenter", out var content))
|
||||
{
|
||||
Grid.SetRow(content, 1);
|
||||
Grid.SetColumn(content, 1);
|
||||
content.HorizontalAlignment = HorizontalAlignment.Center;
|
||||
}
|
||||
}
|
||||
|
||||
private void PreviewTypeSetting_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
// hide or display controls based on whether the "Custom" preview type is selected
|
||||
var selectedPreviewType = this.GetSelectedPreviewType();
|
||||
var customPreviewTypeSelected = selectedPreviewType == PreviewType.Custom;
|
||||
this.CopyStyleToCustom.IsEnabled = !customPreviewTypeSelected;
|
||||
var customControlVisibility = customPreviewTypeSelected
|
||||
? Visibility.Visible
|
||||
: Visibility.Collapsed;
|
||||
this.MouseUtils_MouseJump_BackgroundColor1.Visibility = customControlVisibility;
|
||||
this.MouseUtils_MouseJump_BackgroundColor2.Visibility = customControlVisibility;
|
||||
this.MouseUtils_MouseJump_BorderThickness.Visibility = customControlVisibility;
|
||||
this.MouseUtils_MouseJump_BorderColor.Visibility = customControlVisibility;
|
||||
this.MouseUtils_MouseJump_Border3dDepth.Visibility = customControlVisibility;
|
||||
this.MouseUtils_MouseJump_BorderPadding.Visibility = customControlVisibility;
|
||||
this.MouseUtils_MouseJump_BezelThickness.Visibility = customControlVisibility;
|
||||
this.MouseUtils_MouseJump_BezelColor.Visibility = customControlVisibility;
|
||||
this.MouseUtils_MouseJump_Bezel3dDepth.Visibility = customControlVisibility;
|
||||
this.MouseUtils_MouseJump_ScreenMargin.Visibility = customControlVisibility;
|
||||
this.MouseUtils_MouseJump_ScreenColor1.Visibility = customControlVisibility;
|
||||
this.MouseUtils_MouseJump_ScreenColor2.Visibility = customControlVisibility;
|
||||
}
|
||||
|
||||
private /* async */ void CopyStyleToCustom_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
/*
|
||||
var resourceLoader = ResourceLoaderInstance.ResourceLoader;
|
||||
var messageBox = this.MouseUtils_MouseJump_CopyToCustomStyle_MessageBox;
|
||||
messageBox.Title = resourceLoader.GetString("MouseUtils_MouseJump_CopyToCustomStyle_MessageBox_Title");
|
||||
messageBox.PrimaryButtonText = resourceLoader.GetString("MouseUtils_MouseJump_CopyToCustomStyle_MessageBox_PrimaryButtonText");
|
||||
messageBox.PrimaryButtonCommand = new RelayCommand(this.MouseUtils_MouseJump_CopyToCustomStyle_MessageBox_PrimaryButtonCommand);
|
||||
// await messageBox.ShowAsync();
|
||||
*/
|
||||
this.MouseUtils_MouseJump_CopyToCustomStyle_MessageBox_PrimaryButtonCommand();
|
||||
}
|
||||
|
||||
private void MouseUtils_MouseJump_CopyToCustomStyle_MessageBox_PrimaryButtonCommand()
|
||||
{
|
||||
var selectedPreviewType = this.GetSelectedPreviewType();
|
||||
var selectedPreviewStyle = selectedPreviewType switch
|
||||
{
|
||||
PreviewType.Compact => StyleHelper.CompactPreviewStyle,
|
||||
PreviewType.Bezelled => StyleHelper.BezelledPreviewStyle,
|
||||
PreviewType.Custom => StyleHelper.BezelledPreviewStyle,
|
||||
_ => throw new InvalidOperationException(),
|
||||
};
|
||||
|
||||
// convert the color into a string.
|
||||
// note that we have to replace Named and System colors with their ARGB equivalents
|
||||
// so that serialization returns an ARGB string rather than the Named or System color *name*.
|
||||
this.ViewModel.MouseJumpPreviewType = selectedPreviewType.ToString();
|
||||
this.ViewModel.MouseJumpBackgroundColor1 = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(selectedPreviewStyle.CanvasStyle.BackgroundStyle.Color1));
|
||||
this.ViewModel.MouseJumpBackgroundColor2 = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(selectedPreviewStyle.CanvasStyle.BackgroundStyle.Color2));
|
||||
this.ViewModel.MouseJumpBorderThickness = (int)selectedPreviewStyle.CanvasStyle.BorderStyle.Top;
|
||||
this.ViewModel.MouseJumpBorderColor = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(selectedPreviewStyle.CanvasStyle.BorderStyle.Color));
|
||||
this.ViewModel.MouseJumpBorder3dDepth = (int)selectedPreviewStyle.CanvasStyle.BorderStyle.Depth;
|
||||
this.ViewModel.MouseJumpBorderPadding = (int)selectedPreviewStyle.CanvasStyle.PaddingStyle.Top;
|
||||
this.ViewModel.MouseJumpBezelThickness = (int)selectedPreviewStyle.ScreenStyle.BorderStyle.Top;
|
||||
this.ViewModel.MouseJumpBezelColor = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(selectedPreviewStyle.ScreenStyle.BorderStyle.Color));
|
||||
this.ViewModel.MouseJumpBezel3dDepth = (int)selectedPreviewStyle.ScreenStyle.BorderStyle.Depth;
|
||||
this.ViewModel.MouseJumpScreenMargin = (int)selectedPreviewStyle.ScreenStyle.MarginStyle.Top;
|
||||
this.ViewModel.MouseJumpScreenColor1 = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(selectedPreviewStyle.ScreenStyle.BackgroundStyle.Color1));
|
||||
this.ViewModel.MouseJumpScreenColor2 = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(selectedPreviewStyle.ScreenStyle.BackgroundStyle.Color2));
|
||||
}
|
||||
|
||||
private PreviewType GetSelectedPreviewType()
|
||||
{
|
||||
// this needs to match the order of the SegmentedItems in the "Preview Type" Segmented control
|
||||
var previewTypeOrder = new PreviewType[]
|
||||
{
|
||||
PreviewType.Compact, PreviewType.Bezelled, PreviewType.Custom,
|
||||
};
|
||||
|
||||
var selectedIndex = this.PreviewTypeSetting.SelectedIndex;
|
||||
if ((selectedIndex < 0) || (selectedIndex >= previewTypeOrder.Length))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return previewTypeOrder[selectedIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
xmlns:converters="using:Microsoft.PowerToys.Settings.UI.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:panels="using:Microsoft.PowerToys.Settings.UI.Panels"
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
@@ -245,101 +246,7 @@
|
||||
</tkcontrols:SettingsExpander>
|
||||
</controls:SettingsGroup>
|
||||
|
||||
<controls:SettingsGroup x:Uid="MouseUtils_MouseJump">
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="MouseUtils_Enable_MouseJump"
|
||||
HeaderIcon="{ui:BitmapIcon Source=/Assets/Settings/Icons/MouseJump.png}"
|
||||
IsEnabled="{x:Bind ViewModel.IsJumpEnabledGpoConfigured, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}">
|
||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.IsMouseJumpEnabled, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<InfoBar
|
||||
x:Uid="GPO_SettingIsManaged"
|
||||
IsClosable="False"
|
||||
IsOpen="{x:Bind ViewModel.IsJumpEnabledGpoConfigured, Mode=OneWay}"
|
||||
IsTabStop="{x:Bind ViewModel.IsJumpEnabledGpoConfigured, Mode=OneWay}"
|
||||
Severity="Informational" />
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="MouseUtils_MouseJump_ActivationShortcut"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}"
|
||||
IsEnabled="{x:Bind ViewModel.IsMouseJumpEnabled, Mode=OneWay}">
|
||||
<controls:ShortcutControl MinWidth="{StaticResource SettingActionControlMinWidth}" HotkeySettings="{x:Bind Path=ViewModel.MouseJumpActivationShortcut, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="MouseUtils_MouseJump_ThumbnailSize"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}"
|
||||
IsEnabled="{x:Bind ViewModel.IsMouseJumpEnabled, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard.Description>
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="0,4,0,0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
x:Uid="MouseUtils_MouseJump_ThumbnailSize_Description_Prefix"
|
||||
Margin="0,0,4,0"
|
||||
Style="{ThemeResource SecondaryTextStyle}" />
|
||||
<TextBlock
|
||||
Margin="0,0,4,0"
|
||||
FontWeight="SemiBold"
|
||||
Style="{ThemeResource SecondaryTextStyle}"
|
||||
Text="{x:Bind ViewModel.MouseJumpThumbnailSize.Width, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Margin="0,5,4,0"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="10"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Style="{ThemeResource SecondaryTextStyle}"
|
||||
Text="" />
|
||||
<TextBlock
|
||||
Margin="0,0,4,0"
|
||||
FontWeight="SemiBold"
|
||||
Style="{ThemeResource SecondaryTextStyle}"
|
||||
Text="{x:Bind ViewModel.MouseJumpThumbnailSize.Height, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
x:Uid="MouseUtils_MouseJump_ThumbnailSize_Description_Suffix"
|
||||
Margin="0,0,4,0"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Style="{ThemeResource SecondaryTextStyle}" />
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard.Description>
|
||||
<StackPanel
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal"
|
||||
Spacing="8">
|
||||
<Button
|
||||
x:Uid="EditButton"
|
||||
Width="40"
|
||||
Height="36"
|
||||
Content=""
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
Style="{StaticResource SubtleButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="EditTooltip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Flyout>
|
||||
<Flyout x:Uid="MouseJumpThumbnailSize_Edit" ShouldConstrainToRootBounds="False">
|
||||
<StackPanel Spacing="16">
|
||||
<NumberBox
|
||||
x:Uid="MouseUtils_MouseJump_ThumbnailSize_Edit_Width"
|
||||
Width="140"
|
||||
Minimum="160"
|
||||
SpinButtonPlacementMode="Compact"
|
||||
Value="{x:Bind ViewModel.MouseJumpThumbnailSize.Width, Mode=TwoWay}" />
|
||||
<NumberBox
|
||||
x:Uid="MouseUtils_MouseJump_ThumbnailSize_Edit_Height"
|
||||
Width="140"
|
||||
Minimum="120"
|
||||
SpinButtonPlacementMode="Compact"
|
||||
Value="{x:Bind ViewModel.MouseJumpThumbnailSize.Height, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard>
|
||||
</controls:SettingsGroup>
|
||||
<panels:MouseJumpPanel x:Name="MouseUtils_MouseJump_Panel" x:Uid="MouseUtils_MouseJump_Panel" />
|
||||
|
||||
<controls:SettingsGroup x:Uid="MouseUtils_MousePointerCrosshairs">
|
||||
<tkcontrols:SettingsCard
|
||||
|
||||
@@ -46,6 +46,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
|
||||
DataContext = ViewModel;
|
||||
InitializeComponent();
|
||||
|
||||
this.MouseUtils_MouseJump_Panel.ViewModel = ViewModel;
|
||||
}
|
||||
|
||||
public void RefreshEnabledState()
|
||||
|
||||
@@ -23,13 +23,6 @@
|
||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.IsEnabled, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
|
||||
<InfoBar
|
||||
x:Uid="NewPlus_NoWindows10SupportWarning"
|
||||
IsClosable="False"
|
||||
IsOpen="{x:Bind ViewModel.IsWin10OrLower, Mode=OneWay}"
|
||||
IsTabStop="{x:Bind ViewModel.IsWin10OrLower, Mode=OneWay}"
|
||||
Severity="Warning" />
|
||||
|
||||
<InfoBar
|
||||
x:Uid="GPO_SettingIsManaged"
|
||||
IsClosable="False"
|
||||
|
||||
@@ -2174,7 +2174,7 @@ Take a moment to preview the various utilities listed or view our comprehensive
|
||||
<value>Preference updated.</value>
|
||||
</data>
|
||||
<data name="Oobe_WhatsNew_DataDiagnostics_Yes_Click_InfoBar_Desc.Text" xml:space="preserve">
|
||||
<value>You can change this at any time from </value>
|
||||
<value>You can change this at any time from</value>
|
||||
</data>
|
||||
<data name="Oobe_WhatsNew_DataDiagnostics_Yes_Click_OpenSettings_Text.Text" xml:space="preserve">
|
||||
<value>settings.</value>
|
||||
@@ -4124,13 +4124,17 @@ Activate by holding the key for the character you want to add an accent to, then
|
||||
<value>Launch Registry Preview</value>
|
||||
<comment>"Registry Preview" is the name of the utility</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump.Header" xml:space="preserve">
|
||||
<value>Mouse Jump</value>
|
||||
<comment>Refers to the utility name</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump.Description" xml:space="preserve">
|
||||
<value>Quickly move the mouse pointer long distances.</value>
|
||||
<comment>"Mouse Jump" is the name of the utility. Mouse is the hardware mouse.</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump.Header" xml:space="preserve">
|
||||
<value>Mouse Jump</value>
|
||||
<comment>Refers to the utility name</comment>
|
||||
<data name="MouseUtils_Enable_MouseJump.Header" xml:space="preserve">
|
||||
<value>Enable Mouse Jump</value>
|
||||
<comment>"Mouse Jump" is the name of the utility.</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ActivationShortcut.Description" xml:space="preserve">
|
||||
<value>Customize the shortcut to turn on or off this mode</value>
|
||||
@@ -4138,9 +4142,124 @@ Activate by holding the key for the character you want to add an accent to, then
|
||||
<data name="MouseUtils_MouseJump_ActivationShortcut.Header" xml:space="preserve">
|
||||
<value>Activation shortcut</value>
|
||||
</data>
|
||||
<data name="MouseUtils_Enable_MouseJump.Header" xml:space="preserve">
|
||||
<value>Enable Mouse Jump</value>
|
||||
<comment>"Mouse Jump" is the name of the utility.</comment>
|
||||
<data name="MouseUtils_MouseJump_ThumbnailSize.Header" xml:space="preserve">
|
||||
<value>Thumbnail Size</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ThumbnailSize_Description_Prefix.Text" xml:space="preserve">
|
||||
<value>Constrain thumbnail image size to a maximum of</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ThumbnailSize_Description_Suffix.Text" xml:space="preserve">
|
||||
<value>pixels</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ThumbnailSize_Edit_Height.Header" xml:space="preserve">
|
||||
<value>Maximum height (px)</value>
|
||||
<comment>px = pixels</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ThumbnailSize_Edit_Width.Header" xml:space="preserve">
|
||||
<value>Maximum width (px)</value>
|
||||
<comment>px = pixels</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_Appearance.Header" xml:space="preserve">
|
||||
<value>Appearance</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_PreviewType.Header" xml:space="preserve">
|
||||
<value>Preview style</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_PreviewType.Description" xml:space="preserve">
|
||||
<value>Select a predefined style, or apply a custom one</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_PreviewType_Compact.Text" xml:space="preserve">
|
||||
<value>Compact</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_PreviewType_Bezelled.Text" xml:space="preserve">
|
||||
<value>Bezelled</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_PreviewType_Custom.Text" xml:space="preserve">
|
||||
<value>Custom</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_CopyStyle.Content" xml:space="preserve">
|
||||
<value>Copy to Custom preview style</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_BackgroundColor1.Header" xml:space="preserve">
|
||||
<value>Background color 1</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_BackgroundColor1.Description" xml:space="preserve">
|
||||
<value>The start color for the background gradient fill on the preview image</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_BackgroundColor2.Header" xml:space="preserve">
|
||||
<value>Background color 2</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_BackgroundColor2.Description" xml:space="preserve">
|
||||
<value>The end color for the background gradient fill on the preview image</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_BorderThickness.Header" xml:space="preserve">
|
||||
<value>Border thickness</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_BorderThickness.Description" xml:space="preserve">
|
||||
<value>The thickness (in pixels) of the border that surrounds the preview image</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_BorderColor.Header" xml:space="preserve">
|
||||
<value>Border color</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_BorderColor.Description" xml:space="preserve">
|
||||
<value>The color of the border that surrounds the preview image</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_Border3dDepth.Header" xml:space="preserve">
|
||||
<value>Border 3D depth</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_Border3dDepth.Description" xml:space="preserve">
|
||||
<value>The width (in pixels) of the 3d effect on the border that surrounds the preview image</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_BorderPadding.Header" xml:space="preserve">
|
||||
<value>Border padding</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_BorderPadding.Description" xml:space="preserve">
|
||||
<value>The amount of padding to draw between the border that surrounds the main preview image and the screen images</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_BezelThickness.Header" xml:space="preserve">
|
||||
<value>Bezel thickness</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_BezelThickness.Description" xml:space="preserve">
|
||||
<value>The thickness (in pixels) of the border that surrounds the individual screen images</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_BezelColor.Header" xml:space="preserve">
|
||||
<value>Bezel color</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_BezelColor.Description" xml:space="preserve">
|
||||
<value>The color of the border that surrounds the individual screen images</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_Bezel3dDepth.Header" xml:space="preserve">
|
||||
<value>Bezel 3D depth</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_Bezel3dDepth.Description" xml:space="preserve">
|
||||
<value>The width (in pixels) of the 3d effect on the border that surrounds individual screen images</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ScreenMargin.Header" xml:space="preserve">
|
||||
<value>Screen spacing</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ScreenMargin.Description" xml:space="preserve">
|
||||
<value>The width (in pixels) of the margin drawn between individual screen images</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ScreenColor1.Header" xml:space="preserve">
|
||||
<value>Screen color 1</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ScreenColor1.Description" xml:space="preserve">
|
||||
<value>The start color for the background gradient fill on individual screen images</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ScreenColor2.Header" xml:space="preserve">
|
||||
<value>Screen color 2</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ScreenColor2.Description" xml:space="preserve">
|
||||
<value>The end color for the background gradient fill on individual screen images</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_CopyToCustomStyle_MessageBox_Title" xml:space="preserve">
|
||||
<value>Copy to Custom preview style</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_CopyToCustomStyle_MessageBox_Text" xml:space="preserve">
|
||||
<value>This will replace the current settings in the Custom preview style.</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_CopyToCustomStyle_MessageBox_PrimaryButtonText" xml:space="preserve">
|
||||
<value>Copy</value>
|
||||
</data>
|
||||
<data name="Hosts_Toggle_LoopbackDuplicates.Description" xml:space="preserve">
|
||||
<value>127.0.0.1, ::1, ...</value>
|
||||
@@ -4168,23 +4287,6 @@ Activate by holding the key for the character you want to add an accent to, then
|
||||
<data name="AdvancedPaste_ShortcutWarning.Title" xml:space="preserve">
|
||||
<value>Using this shortcut may prevent non-text paste actions (e.g. images, files) or built-in paste plain text actions in other applications from functioning.</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ThumbnailSize.Header" xml:space="preserve">
|
||||
<value>Thumbnail Size</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ThumbnailSize_Description_Prefix.Text" xml:space="preserve">
|
||||
<value>Constrain thumbnail image size to a maximum of</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ThumbnailSize_Description_Suffix.Text" xml:space="preserve">
|
||||
<value>pixels</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ThumbnailSize_Edit_Height.Header" xml:space="preserve">
|
||||
<value>Maximum height (px)</value>
|
||||
<comment>px = pixels</comment>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_ThumbnailSize_Edit_Width.Header" xml:space="preserve">
|
||||
<value>Maximum width (px)</value>
|
||||
<comment>px = pixels</comment>
|
||||
</data>
|
||||
<data name="Oobe_Peek.Description" xml:space="preserve">
|
||||
<value>A lightning fast file preview feature for Windows.</value>
|
||||
<comment>{Locked="Windows"}</comment>
|
||||
@@ -4396,9 +4498,6 @@ Activate by holding the key for the character you want to add an accent to, then
|
||||
<value>Enable New+</value>
|
||||
<comment>Localize product name in accordance with Windows New</comment>
|
||||
</data>
|
||||
<data name="NewPlus_NoWindows10SupportWarning.Title" xml:space="preserve">
|
||||
<value>New+ is not supported in Windows 10 and is not expected to work.</value>
|
||||
</data>
|
||||
<data name="NewPlus_TemplatesNotBackupAndRestoreWarning.Title" xml:space="preserve">
|
||||
<value>PowerToys "Backup and Restore" feature doesn't take templates into account at this moment. If you use that feature, templates will have to be copied manually.</value>
|
||||
</data>
|
||||
|
||||
@@ -14,7 +14,7 @@ using Microsoft.PowerToys.Settings.Utilities;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
public class MouseUtilsViewModel : Observable
|
||||
public partial class MouseUtilsViewModel : Observable
|
||||
{
|
||||
private ISettingsUtils SettingsUtils { get; set; }
|
||||
|
||||
@@ -24,8 +24,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
private MouseHighlighterSettings MouseHighlighterSettingsConfig { get; set; }
|
||||
|
||||
private MouseJumpSettings MouseJumpSettingsConfig { get; set; }
|
||||
|
||||
private MousePointerCrosshairsSettings MousePointerCrosshairsSettingsConfig { get; set; }
|
||||
|
||||
public MouseUtilsViewModel(ISettingsUtils settingsUtils, ISettingsRepository<GeneralSettings> settingsRepository, ISettingsRepository<FindMyMouseSettings> findMyMouseSettingsRepository, ISettingsRepository<MouseHighlighterSettings> mouseHighlighterSettingsRepository, ISettingsRepository<MouseJumpSettings> mouseJumpSettingsRepository, ISettingsRepository<MousePointerCrosshairsSettings> mousePointerCrosshairsSettingsRepository, Func<string, int> ipcMSGCallBackFunc)
|
||||
@@ -80,10 +78,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
_highlightFadeDurationMs = MouseHighlighterSettingsConfig.Properties.HighlightFadeDurationMs.Value;
|
||||
_highlighterAutoActivate = MouseHighlighterSettingsConfig.Properties.AutoActivate.Value;
|
||||
|
||||
ArgumentNullException.ThrowIfNull(mouseJumpSettingsRepository);
|
||||
|
||||
MouseJumpSettingsConfig = mouseJumpSettingsRepository.SettingsConfig;
|
||||
MouseJumpSettingsConfig.Properties.ThumbnailSize.PropertyChanged += MouseJumpThumbnailSizePropertyChanged;
|
||||
this.InitializeMouseJumpSettings(mouseJumpSettingsRepository);
|
||||
|
||||
ArgumentNullException.ThrowIfNull(mousePointerCrosshairsSettingsRepository);
|
||||
|
||||
@@ -138,17 +133,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
_isMouseHighlighterEnabled = GeneralSettingsConfig.Enabled.MouseHighlighter;
|
||||
}
|
||||
|
||||
_jumpEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredMouseJumpEnabledValue();
|
||||
if (_jumpEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _jumpEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
|
||||
{
|
||||
// Get the enabled state from GPO.
|
||||
_jumpEnabledStateIsGPOConfigured = true;
|
||||
_isMouseJumpEnabled = _jumpEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
_isMouseJumpEnabled = GeneralSettingsConfig.Enabled.MouseJump;
|
||||
}
|
||||
this.InitializeMouseJumpEnabledValues();
|
||||
|
||||
_mousePointerCrosshairsEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredMousePointerCrosshairsEnabledValue();
|
||||
if (_mousePointerCrosshairsEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _mousePointerCrosshairsEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
|
||||
@@ -657,87 +642,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
SettingsUtils.SaveSettings(MouseHighlighterSettingsConfig.ToJsonString(), MouseHighlighterSettings.ModuleName);
|
||||
}
|
||||
|
||||
public bool IsMouseJumpEnabled
|
||||
{
|
||||
get => _isMouseJumpEnabled;
|
||||
set
|
||||
{
|
||||
if (_jumpEnabledStateIsGPOConfigured)
|
||||
{
|
||||
// If it's GPO configured, shouldn't be able to change this state.
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isMouseJumpEnabled != value)
|
||||
{
|
||||
_isMouseJumpEnabled = value;
|
||||
|
||||
GeneralSettingsConfig.Enabled.MouseJump = value;
|
||||
OnPropertyChanged(nameof(_isMouseJumpEnabled));
|
||||
|
||||
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig);
|
||||
SendConfigMSG(outgoing.ToString());
|
||||
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsJumpEnabledGpoConfigured
|
||||
{
|
||||
get => _jumpEnabledStateIsGPOConfigured;
|
||||
}
|
||||
|
||||
public HotkeySettings MouseJumpActivationShortcut
|
||||
{
|
||||
get
|
||||
{
|
||||
return MouseJumpSettingsConfig.Properties.ActivationShortcut;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (MouseJumpSettingsConfig.Properties.ActivationShortcut != value)
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.ActivationShortcut = value ?? MouseJumpSettingsConfig.Properties.DefaultActivationShortcut;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MouseJumpThumbnailSize MouseJumpThumbnailSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return MouseJumpSettingsConfig.Properties.ThumbnailSize;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if ((MouseJumpSettingsConfig.Properties.ThumbnailSize.Width != value?.Width)
|
||||
&& (MouseJumpSettingsConfig.Properties.ThumbnailSize.Height != value?.Height))
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.ThumbnailSize = value;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MouseJumpThumbnailSizePropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
NotifyMouseJumpPropertyChanged(nameof(MouseJumpThumbnailSize));
|
||||
}
|
||||
|
||||
public void NotifyMouseJumpPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
OnPropertyChanged(propertyName);
|
||||
|
||||
SndMouseJumpSettings outsettings = new SndMouseJumpSettings(MouseJumpSettingsConfig);
|
||||
SndModuleSettings<SndMouseJumpSettings> ipcMessage = new SndModuleSettings<SndMouseJumpSettings>(outsettings);
|
||||
SendConfigMSG(ipcMessage.ToJsonString());
|
||||
SettingsUtils.SaveSettings(MouseJumpSettingsConfig.ToJsonString(), MouseJumpSettings.ModuleName);
|
||||
}
|
||||
|
||||
public bool IsMousePointerCrosshairsEnabled
|
||||
{
|
||||
get => _isMousePointerCrosshairsEnabled;
|
||||
@@ -1017,10 +921,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
private int _highlightFadeDurationMs;
|
||||
private bool _highlighterAutoActivate;
|
||||
|
||||
private GpoRuleConfigured _jumpEnabledGpoRuleConfiguration;
|
||||
private bool _jumpEnabledStateIsGPOConfigured;
|
||||
private bool _isMouseJumpEnabled;
|
||||
|
||||
private GpoRuleConfigured _mousePointerCrosshairsEnabledGpoRuleConfiguration;
|
||||
private bool _mousePointerCrosshairsEnabledStateIsGPOConfigured;
|
||||
private bool _isMousePointerCrosshairsEnabled;
|
||||
|
||||
@@ -0,0 +1,513 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using global::PowerToys.GPOWrapper;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using MouseJump.Common.Helpers;
|
||||
using MouseJump.Common.Imaging;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
using MouseJump.Common.Models.Settings;
|
||||
using MouseJump.Common.Models.Styles;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
public partial class MouseUtilsViewModel : Observable
|
||||
{
|
||||
private GpoRuleConfigured _jumpEnabledGpoRuleConfiguration;
|
||||
private bool _jumpEnabledStateIsGPOConfigured;
|
||||
private bool _isMouseJumpEnabled;
|
||||
|
||||
internal MouseJumpSettings MouseJumpSettingsConfig { get; set; }
|
||||
|
||||
private void InitializeMouseJumpSettings(ISettingsRepository<MouseJumpSettings> mouseJumpSettingsRepository)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(mouseJumpSettingsRepository);
|
||||
this.MouseJumpSettingsConfig = mouseJumpSettingsRepository.SettingsConfig;
|
||||
this.MouseJumpSettingsConfig.Properties.ThumbnailSize.PropertyChanged += this.MouseJumpThumbnailSizePropertyChanged;
|
||||
}
|
||||
|
||||
private void InitializeMouseJumpEnabledValues()
|
||||
{
|
||||
_jumpEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredMouseJumpEnabledValue();
|
||||
if (_jumpEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _jumpEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
|
||||
{
|
||||
// Get the enabled state from GPO.
|
||||
_jumpEnabledStateIsGPOConfigured = true;
|
||||
_isMouseJumpEnabled = _jumpEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
_isMouseJumpEnabled = GeneralSettingsConfig.Enabled.MouseJump;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsMouseJumpEnabled
|
||||
{
|
||||
get => _isMouseJumpEnabled;
|
||||
set
|
||||
{
|
||||
if (_jumpEnabledStateIsGPOConfigured)
|
||||
{
|
||||
// If it's GPO configured, shouldn't be able to change this state.
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isMouseJumpEnabled != value)
|
||||
{
|
||||
_isMouseJumpEnabled = value;
|
||||
|
||||
GeneralSettingsConfig.Enabled.MouseJump = value;
|
||||
OnPropertyChanged(nameof(_isMouseJumpEnabled));
|
||||
|
||||
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig);
|
||||
SendConfigMSG(outgoing.ToString());
|
||||
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsJumpEnabledGpoConfigured
|
||||
{
|
||||
get => _jumpEnabledStateIsGPOConfigured;
|
||||
}
|
||||
|
||||
public HotkeySettings MouseJumpActivationShortcut
|
||||
{
|
||||
get
|
||||
{
|
||||
return MouseJumpSettingsConfig.Properties.ActivationShortcut;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (MouseJumpSettingsConfig.Properties.ActivationShortcut != value)
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.ActivationShortcut = value ?? MouseJumpSettingsConfig.Properties.DefaultActivationShortcut;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MouseJumpThumbnailSize MouseJumpThumbnailSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return MouseJumpSettingsConfig.Properties.ThumbnailSize;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if ((MouseJumpSettingsConfig.Properties.ThumbnailSize.Width != value?.Width)
|
||||
&& (MouseJumpSettingsConfig.Properties.ThumbnailSize.Height != value?.Height))
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.ThumbnailSize = value;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Bitmap LoadImageResource(string filename)
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var assemblyName = new AssemblyName(assembly.FullName ?? throw new InvalidOperationException());
|
||||
var resourceName = $"Microsoft.{assemblyName.Name}.{filename.Replace("/", ".")}";
|
||||
var resourceNames = assembly.GetManifestResourceNames();
|
||||
if (!resourceNames.Contains(resourceName))
|
||||
{
|
||||
throw new InvalidOperationException($"Embedded resource '{resourceName}' does not exist.");
|
||||
}
|
||||
|
||||
var stream = assembly.GetManifestResourceStream(resourceName)
|
||||
?? throw new InvalidOperationException();
|
||||
var image = (Bitmap)Image.FromStream(stream);
|
||||
return image;
|
||||
}
|
||||
|
||||
private static Lazy<Bitmap> MouseJumpDesktopImage => new(
|
||||
() => MouseUtilsViewModel.LoadImageResource("UI/Images/MouseJump-Desktop.png")
|
||||
);
|
||||
|
||||
public ImageSource MouseJumpPreviewImage
|
||||
{
|
||||
get
|
||||
{
|
||||
// keep these in sync with the layout of "Images\MouseJump-Desktop.png"
|
||||
var screens = new List<RectangleInfo>()
|
||||
{
|
||||
/*
|
||||
these magic numbers are the pixel dimensions of the individual screens on the
|
||||
fake desktop image - "Images\MouseJump-Desktop.png" - used to generate the
|
||||
preview image in the Settings UI properties page for Mouse Jump. if you update
|
||||
the fake desktop image be sure to update these values as well.
|
||||
*/
|
||||
new(635, 172, 272, 168),
|
||||
new(0, 0, 635, 339),
|
||||
};
|
||||
var desktopSize = LayoutHelper.GetCombinedScreenBounds(screens).Size;
|
||||
/*
|
||||
magic number 283 is the content height left in the settings card after removing the top and bottom chrome:
|
||||
|
||||
300px settings card height - 1px top border - 7px top margin - 8px bottom margin - 1px bottom border = 283px image height
|
||||
|
||||
this ensures we get a preview image scaled at 100% so borders etc are shown at exact pixel sizes in the preview
|
||||
*/
|
||||
var canvasSize = new SizeInfo(desktopSize.Width, 283).Clamp(desktopSize);
|
||||
|
||||
var previewType = Enum.TryParse<PreviewType>(this.MouseJumpPreviewType, true, out var previewTypeResult)
|
||||
? previewTypeResult
|
||||
: PreviewType.Bezelled;
|
||||
var previewStyle = previewType switch
|
||||
{
|
||||
PreviewType.Compact => StyleHelper.CompactPreviewStyle.WithCanvasSize(desktopSize),
|
||||
PreviewType.Bezelled => StyleHelper.BezelledPreviewStyle.WithCanvasSize(desktopSize),
|
||||
PreviewType.Custom => new PreviewStyle(
|
||||
canvasSize: canvasSize,
|
||||
canvasStyle: new(
|
||||
marginStyle: new(0),
|
||||
borderStyle: new(
|
||||
color: ConfigHelper.DeserializeFromConfigColorString(
|
||||
this.MouseJumpBorderColor),
|
||||
all: this.MouseJumpBorderThickness,
|
||||
depth: this.MouseJumpBorder3dDepth
|
||||
),
|
||||
paddingStyle: new(
|
||||
all: this.MouseJumpBorderPadding
|
||||
),
|
||||
backgroundStyle: new(
|
||||
color1: ConfigHelper.DeserializeFromConfigColorString(
|
||||
this.MouseJumpBackgroundColor1),
|
||||
color2: ConfigHelper.DeserializeFromConfigColorString(
|
||||
this.MouseJumpBackgroundColor2)
|
||||
)
|
||||
),
|
||||
screenStyle: new(
|
||||
marginStyle: new(
|
||||
all: this.MouseJumpScreenMargin
|
||||
),
|
||||
borderStyle: new(
|
||||
color: ConfigHelper.DeserializeFromConfigColorString(
|
||||
this.MouseJumpBezelColor),
|
||||
all: this.MouseJumpBezelThickness,
|
||||
depth: this.MouseJumpBezel3dDepth
|
||||
),
|
||||
paddingStyle: new(0),
|
||||
backgroundStyle: new(
|
||||
color1: ConfigHelper.DeserializeFromConfigColorString(
|
||||
this.MouseJumpScreenColor1),
|
||||
color2: ConfigHelper.DeserializeFromConfigColorString(
|
||||
this.MouseJumpScreenColor2)
|
||||
)
|
||||
)),
|
||||
_ => throw new InvalidOperationException(
|
||||
$"Unhandled {nameof(MouseJumpPreviewType)} '{previewType}'"),
|
||||
};
|
||||
|
||||
var previewLayout = LayoutHelper.GetPreviewLayout(
|
||||
previewStyle: previewStyle,
|
||||
screens: screens,
|
||||
activatedLocation: new(0, 0));
|
||||
|
||||
var desktopImage = MouseUtilsViewModel.MouseJumpDesktopImage.Value;
|
||||
var imageCopyService = new StaticImageRegionCopyService(desktopImage);
|
||||
using var previewImage = DrawingHelper.RenderPreview(
|
||||
previewLayout,
|
||||
imageCopyService);
|
||||
|
||||
// save the image to a memory stream
|
||||
using var stream = new MemoryStream();
|
||||
previewImage.Save(stream, ImageFormat.Png);
|
||||
stream.Position = 0;
|
||||
|
||||
// load the memory stream into a bitmap image
|
||||
var bitmap = new BitmapImage();
|
||||
var rnd = stream.AsRandomAccessStream();
|
||||
bitmap.DecodePixelWidth = previewImage.Width;
|
||||
bitmap.DecodePixelHeight = previewImage.Height;
|
||||
bitmap.SetSource(rnd);
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
public string MouseJumpPreviewType
|
||||
{
|
||||
get
|
||||
{
|
||||
return MouseJumpSettingsConfig.Properties.PreviewType;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value != MouseJumpSettingsConfig.Properties.PreviewType)
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.PreviewType = value;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string MouseJumpBackgroundColor1
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = MouseJumpSettingsConfig.Properties.BackgroundColor1;
|
||||
value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000";
|
||||
return value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000";
|
||||
if (!value.Equals(MouseJumpSettingsConfig.Properties.BackgroundColor1, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.BackgroundColor1 = value;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string MouseJumpBackgroundColor2
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = MouseJumpSettingsConfig.Properties.BackgroundColor2;
|
||||
value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000";
|
||||
return value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000";
|
||||
if (!value.Equals(MouseJumpSettingsConfig.Properties.BackgroundColor2, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.BackgroundColor2 = value;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int MouseJumpBorderThickness
|
||||
{
|
||||
get
|
||||
{
|
||||
return MouseJumpSettingsConfig.Properties.BorderThickness;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value != MouseJumpSettingsConfig.Properties.BorderThickness)
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.BorderThickness = value;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string MouseJumpBorderColor
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = MouseJumpSettingsConfig.Properties.BorderColor;
|
||||
value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000";
|
||||
return value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000";
|
||||
if (!value.Equals(MouseJumpSettingsConfig.Properties.BorderColor, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.BorderColor = value;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int MouseJumpBorder3dDepth
|
||||
{
|
||||
get
|
||||
{
|
||||
return MouseJumpSettingsConfig.Properties.Border3dDepth;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value != MouseJumpSettingsConfig.Properties.Border3dDepth)
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.Border3dDepth = value;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int MouseJumpBorderPadding
|
||||
{
|
||||
get
|
||||
{
|
||||
return MouseJumpSettingsConfig.Properties.BorderPadding;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value != MouseJumpSettingsConfig.Properties.BorderPadding)
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.BorderPadding = value;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int MouseJumpBezelThickness
|
||||
{
|
||||
get
|
||||
{
|
||||
return MouseJumpSettingsConfig.Properties.BezelThickness;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value != MouseJumpSettingsConfig.Properties.BezelThickness)
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.BezelThickness = value;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string MouseJumpBezelColor
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = MouseJumpSettingsConfig.Properties.BezelColor;
|
||||
value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000";
|
||||
return value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000";
|
||||
if (!value.Equals(MouseJumpSettingsConfig.Properties.BezelColor, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.BezelColor = value;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int MouseJumpBezel3dDepth
|
||||
{
|
||||
get
|
||||
{
|
||||
return MouseJumpSettingsConfig.Properties.Bezel3dDepth;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value != MouseJumpSettingsConfig.Properties.Bezel3dDepth)
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.Bezel3dDepth = value;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int MouseJumpScreenMargin
|
||||
{
|
||||
get
|
||||
{
|
||||
return MouseJumpSettingsConfig.Properties.ScreenMargin;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value != MouseJumpSettingsConfig.Properties.ScreenMargin)
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.ScreenMargin = value;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string MouseJumpScreenColor1
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = MouseJumpSettingsConfig.Properties.ScreenColor1;
|
||||
value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000";
|
||||
return value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000";
|
||||
if (!value.Equals(MouseJumpSettingsConfig.Properties.ScreenColor1, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.ScreenColor1 = value;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string MouseJumpScreenColor2
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = MouseJumpSettingsConfig.Properties.ScreenColor2;
|
||||
value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000";
|
||||
return value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000";
|
||||
if (!value.Equals(MouseJumpSettingsConfig.Properties.ScreenColor2, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
MouseJumpSettingsConfig.Properties.ScreenColor2 = value;
|
||||
NotifyMouseJumpPropertyChanged();
|
||||
NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MouseJumpThumbnailSizePropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
NotifyMouseJumpPropertyChanged(nameof(MouseJumpThumbnailSize));
|
||||
}
|
||||
|
||||
public void NotifyMouseJumpPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
OnPropertyChanged(propertyName);
|
||||
|
||||
SndMouseJumpSettings outsettings = new SndMouseJumpSettings(MouseJumpSettingsConfig);
|
||||
SndModuleSettings<SndMouseJumpSettings> ipcMessage = new SndModuleSettings<SndMouseJumpSettings>(outsettings);
|
||||
SendConfigMSG(ipcMessage.ToJsonString());
|
||||
SettingsUtils.SaveSettings(MouseJumpSettingsConfig.ToJsonString(), MouseJumpSettings.ModuleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user