[Mouse Jump] Customisable appearance - borders, margins, colours, etc - final part (#35521)

* [MouseJump] move Mouse Jump settings into separate control (#27511)

* [MouseJump] added Mouse Jump style controls to Settings UI (#27511)

* [MouseJump] added Mouse Jump style controls to Settings UI (#27511)

* [MouseJump] removing unused MouseJumpUI code (#27511)

* [MouseJump] whitespace (#27511)

* [MouseJump] fix spellcheck (#27511)

* [MouseJump] enabled "Copy to custom style" (#27511)

* [MouseJump] fixing build (internal members -> public) (#27511)

* [MouseJump] remove unused "using"s (#27511)

* [MouseJump] use custom styles in preview image (#27511)

* [MouseJump] fixing failing test (#27511)

* [MouseJump] fixing failing test (#27511)

* [MouseJump] fixing failing test (#27511)

* [MouseJump] fixing failing test (#27511)

* [MouseJump] delinting to trigger a build (#27511)

* [MouseJump] updated settings preview image ("browser" header) (#27511)

* [MouseJump] upgrade default "custom" style settings in config (#27511)

* [MouseJump] fixed a glitch in settings upgrade (#27511)

* [MouseJump] fixed spell checker (#27511)

* [MouseJump] typo in resource strings (image -> images) (#27511)

* Remove unused include
This commit is contained in:
Michael Clayton 2024-11-26 15:37:59 +00:00 коммит произвёл GitHub
Родитель 7c9876582c
Коммит 53212188b7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
39 изменённых файлов: 1710 добавлений и 992 удалений

2
.github/actions/spell-check/expect.txt поставляемый
Просмотреть файл

@ -102,6 +102,7 @@ backtracer
bbwe
bck
BESTEFFORT
bezelled
bhid
BIF
bigbar
@ -725,6 +726,7 @@ ISSEPARATOR
ITask
ith
ITHUMBNAIL
ITwoWayPipeMessageIPCManaged
IUI
IUnknown
IUse

Просмотреть файл

@ -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]

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 215 KiB

После

Ширина:  |  Высота:  |  Размер: 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;
}
}

Просмотреть файл

@ -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();
}
}
}

Двоичные данные
src/settings-ui/Settings.UI/Images/MouseJump-Desktop.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 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>

Просмотреть файл

@ -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=&#xEDA7;}"
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=&#xE740;}"
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="&#xE947;" />
<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="&#xE70F;"
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=&#xEB3C;}"
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=&#xEDA7;}"
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=&#xE740;}"
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="&#xE947;" />
<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="&#xE70F;"
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()

Просмотреть файл

@ -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>

Просмотреть файл

@ -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);
}
}
}