[New Utility]Mouse Without Borders

* Integrate Mouse Without Borders into PowerToys

---------

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
This commit is contained in:
Andrey Nekrasov
2023-05-15 23:32:26 +01:00
committed by Jaime Bernardo
parent a0b9af039d
commit 29eebe16a4
304 changed files with 37234 additions and 133 deletions

View File

@@ -359,6 +359,7 @@ namespace Microsoft.PowerToys.Settings.UI
case "ImageResizer": return typeof(ImageResizerPage);
case "KBM": return typeof(KeyboardManagerPage);
case "MouseUtils": return typeof(MouseUtilsPage);
case "MouseWithoutBorders": return typeof(MouseWithoutBordersPage);
case "PowerRename": return typeof(PowerRenamePage);
case "QuickAccent": return typeof(PowerAccentPage);
case "FileExplorer": return typeof(PowerPreviewPage);

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -0,0 +1,29 @@
// 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.Globalization;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
namespace Microsoft.PowerToys.Settings.UI.Converters
{
public class NegativeBoolToVisibilityConverter : IValueConverter
{
object IValueConverter.Convert(object value, Type targetType, object parameter, string language)
{
if ((bool)value)
{
return Visibility.Collapsed;
}
return Visibility.Visible;
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,42 @@
// 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 System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Microsoft.PowerToys.Settings.UI.Helpers
{
internal sealed class AsyncCommand : ICommand
{
private readonly Func<Task> _execute;
private readonly Func<bool> _canExecute;
public event EventHandler CanExecuteChanged;
public AsyncCommand(Func<Task> execute, Func<bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute();
}
public async void Execute(object parameter)
{
await _execute();
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}

View File

@@ -0,0 +1,58 @@
// 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 System.Text;
using System.Threading.Tasks;
namespace Microsoft.PowerToys.Settings.UI.Helpers
{
#pragma warning disable SA1649 // File name should match first type name
public class IndexedItem<T>
#pragma warning restore SA1649 // File name should match first type name
{
public T Item { get; set; }
public int Index { get; set; }
public IndexedItem(T item, int index)
{
Item = item;
Index = index;
}
}
#pragma warning disable SA1402 // File may only contain a single type
public class IndexedObservableCollection<T> : ObservableCollection<IndexedItem<T>>
#pragma warning restore SA1402 // File may only contain a single type
{
public IndexedObservableCollection(IEnumerable<T> items)
{
int index = 0;
foreach (var item in items)
{
Add(new IndexedItem<T>(item, index++));
}
}
public IEnumerable<T> ToEnumerable()
{
return this.Select(x => x.Item);
}
public void Swap(int index1, int index2)
{
var temp = this[index1];
this[index1] = this[index2];
this[index2] = temp;
// Update the original index of the items
this[index1].Index = index1;
this[index2].Index = index2;
}
}
}

View File

@@ -1,27 +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.ComponentModel;
using System.Runtime.CompilerServices;
namespace Microsoft.PowerToys.Settings.UI.Helpers
{
public class Observable : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
{
return;
}
storage = value;
OnPropertyChanged(propertyName);
}
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

View File

@@ -125,6 +125,9 @@ namespace Microsoft.PowerToys.Settings.UI
case "MousePointerCrosshairs":
needToUpdate = generalSettingsConfig.Enabled.MousePointerCrosshairs != isEnabled;
generalSettingsConfig.Enabled.MousePointerCrosshairs = isEnabled; break;
case "MouseWithoutBorders":
needToUpdate = generalSettingsConfig.Enabled.MouseWithoutBorders != isEnabled;
generalSettingsConfig.Enabled.MouseWithoutBorders = isEnabled; break;
case "PastePlain":
needToUpdate = generalSettingsConfig.Enabled.PastePlain != isEnabled;
generalSettingsConfig.Enabled.PastePlain = isEnabled; break;

View File

@@ -16,6 +16,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Enums
ImageResizer,
KBM,
MouseUtils,
MouseWithoutBorders,
Peek,
PowerRename,
Run,

View File

@@ -0,0 +1,52 @@
<Page
x:Class="Microsoft.PowerToys.Settings.UI.OOBE.Views.OobeMouseWithoutBorders"
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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Microsoft.PowerToys.Settings.UI.OOBE.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:toolkitcontrols="using:CommunityToolkit.WinUI.UI.Controls"
mc:Ignorable="d">
<controls:OOBEPageControl
x:Uid="Oobe_MouseWithoutBorders"
HeroImage="ms-appx:///Assets/Modules/OOBE/MouseWithoutBorders.png">
<controls:OOBEPageControl.PageContent>
<StackPanel Orientation="Vertical">
<TextBlock
x:Uid="Oobe_HowToUse"
Style="{ThemeResource OobeSubtitleStyle}" />
<toolkitcontrols:MarkdownTextBlock
x:Uid="Oobe_MouseWithoutBorders_HowToUse"
Background="Transparent" />
<TextBlock
x:Uid="Oobe_TipsAndTricks"
Style="{ThemeResource OobeSubtitleStyle}" />
<toolkitcontrols:MarkdownTextBlock
x:Uid="Oobe_MouseWithoutBorders_TipsAndTricks"
Background="Transparent" />
<StackPanel
Margin="0,24,0,0"
Orientation="Horizontal"
Spacing="12">
<Button
x:Uid="OOBE_Settings"
Click="SettingsLaunchButton_Click" />
<HyperlinkButton
NavigateUri="https://aka.ms/PowerToysOverview_MouseWithoutBorders"
Style="{StaticResource TextButtonStyle}">
<TextBlock
x:Uid="LearnMore_MouseWithoutBorders"
TextWrapping="Wrap" />
</HyperlinkButton>
</StackPanel>
</StackPanel>
</controls:OOBEPageControl.PageContent>
</controls:OOBEPageControl>
</Page>

View File

@@ -0,0 +1,47 @@
// 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 Microsoft.PowerToys.Settings.UI.OOBE.Enums;
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
using Microsoft.PowerToys.Settings.UI.Views;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class OobeMouseWithoutBorders : Page
{
public OobePowerToysModule ViewModel { get; set; }
public OobeMouseWithoutBorders()
{
this.InitializeComponent();
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.MouseWithoutBorders]);
DataContext = ViewModel;
}
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (OobeShellPage.OpenMainWindowCallback != null)
{
OobeShellPage.OpenMainWindowCallback(typeof(MouseWithoutBordersPage));
}
ViewModel.LogOpeningSettingsEvent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ViewModel.LogOpeningModuleEvent();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
ViewModel.LogClosingModuleEvent();
}
}
}

View File

@@ -68,6 +68,10 @@
x:Uid="Shell_MouseUtilities"
Icon="{ui:BitmapIcon Source=/Assets/FluentIcons/FluentIconsMouseUtils.png}"
Tag="MouseUtils" />
<NavigationViewItem
x:Uid="Shell_MouseWithoutBorders"
Icon="{ui:BitmapIcon Source=/Assets/FluentIcons/FluentIconsMouseWithoutBorders.png}"
Tag="MouseWithoutBorders" />
<NavigationViewItem
x:Uid="Shell_PastePlain"
Icon="{ui:BitmapIcon Source=/Assets/FluentIcons/FluentIconsPastePlain.png}"

View File

@@ -115,6 +115,11 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
ModuleName = "MouseUtils",
IsNew = false,
});
Modules.Insert((int)PowerToysModules.MouseWithoutBorders, new OobePowerToysModule()
{
ModuleName = "MouseWithoutBorders",
IsNew = true,
});
Modules.Insert((int)PowerToysModules.Peek, new OobePowerToysModule()
{
ModuleName = "Peek",
@@ -249,6 +254,7 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
case "TextExtractor": NavigationFrame.Navigate(typeof(OobePowerOCR)); break;
case "VideoConference": NavigationFrame.Navigate(typeof(OobeVideoConference)); break;
case "MouseUtils": NavigationFrame.Navigate(typeof(OobeMouseUtils)); break;
case "MouseWithoutBorders": NavigationFrame.Navigate(typeof(OobeMouseWithoutBorders)); break;
case "MeasureTool": NavigationFrame.Navigate(typeof(OobeMeasureTool)); break;
case "Hosts": NavigationFrame.Navigate(typeof(OobeHosts)); break;
case "RegistryPreview": NavigationFrame.Navigate(typeof(OobeRegistryPreview)); break;

View File

@@ -89,6 +89,7 @@
<PackageReference Include="Microsoft.WindowsAppSDK" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" />
<PackageReference Include="StreamJsonRpc" />
<Manifest Include="$(ApplicationManifest)" />
</ItemGroup>
<!-- Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
@@ -108,6 +109,13 @@
<ProjectReference Include="..\Settings.UI.Library\Settings.UI.Library.csproj" />
</ItemGroup>
<PropertyGroup>
<!-- TODO: fix issues and reenable -->
<!-- These are caused by streamjsonrpc dependency on Microsoft.VisualStudio.Threading.Analyzers -->
<!-- We might want to add that to the project and fix the issues as well -->
<NoWarn>VSTHRD002;VSTHRD110;VSTHRD100;VSTHRD200;VSTHRD101</NoWarn>
</PropertyGroup>
<ItemGroup>
<Page Update="FlyoutWindow.xaml">
<Generator>MSBuild:Compile</Generator>

View File

@@ -216,6 +216,275 @@
<value>Enable Screen Ruler</value>
<comment>"Screen Ruler" is the name of the utility</comment>
</data>
<data name="MouseWithoutBorders_ActivationSettings.Header" xml:space="preserve">
<value>Activation</value>
</data>
<data name="MouseWithoutBorders_DeviceLayoutSettings.Header" xml:space="preserve">
<value>Device layout</value>
</data>
<data name="MouseWithoutBorders_DeviceLayoutSettings.Description" xml:space="preserve">
<value>Drag and drop a machine to rearrange the order.</value>
</data>
<data name="MouseWithoutBorders_CannotDragDropAsAdmin.Title" xml:space="preserve">
<value>It is not possible to use drag and drop while running PowerToys elevated. As a workaround, please restart PowerToys without elevation to edit the device layout.</value>
</data>
<data name="MouseWithoutBorders_KeySettings.Header" xml:space="preserve">
<value>Encryption Key</value>
</data>
<data name="MouseWithoutBorders_SecurityKey.Header" xml:space="preserve">
<value>Security key</value>
</data>
<data name="MouseWithoutBorders_SecurityKey.Description" xml:space="preserve">
<value>The key must be auto generated in one machine by click on New Key, then typed in other machines</value>
</data>
<data name="MouseWithoutBorders_NewKey.Content" xml:space="preserve">
<value>New key</value>
</data>
<data name="MouseWithoutBorders_CopyMachineName.Text" xml:space="preserve">
<value>Copy this PC name to the clipboard</value>
</data>
<data name="MouseWithoutBorders_ReconnectButton.Text" xml:space="preserve">
<value>Refresh connections</value>
</data>
<data name="MouseWithoutBorders_ReconnectTooltip.Text" xml:space="preserve">
<value>Reestablishes connections with other devices if you are experiencing issues.</value>
</data>
<data name="MouseWithoutBorders_ThisMachineNameLabel.Header" xml:space="preserve">
<value>The local machine's host name is:</value>
</data>
<data name="MouseWithoutBorders_Connect.Content" xml:space="preserve">
<value>Connect</value>
</data>
<data name="MouseWithoutBorders_UninstallService.Header" xml:space="preserve">
<value>Uninstall service</value>
</data>
<data name="MouseWithoutBorders_UninstallService.Description" xml:space="preserve">
<value>Removes the service from the computer. Needs to run as administrator.</value>
</data>
<data name="MouseWithoutBorders_Settings.Header" xml:space="preserve">
<value>Behavior</value>
</data>
<data name="MouseWithoutBorders_TroubleShooting.Header" xml:space="preserve">
<value>Troubleshooting</value>
</data>
<data name="MouseWithoutBorders_AddFirewallRuleButtonControl.Header" xml:space="preserve">
<value>Add a firewall rule for Mouse Without Borders</value>
<comment>"Mouse Without Borders" is a product name</comment>
</data>
<data name="MouseWithoutBorders_AddFirewallRuleButtonControl.Description" xml:space="preserve">
<value>Adding a firewall rule might help solve connection issues.</value>
</data>
<data name="MouseWithoutBorders_RunAsAdminText.Title" xml:space="preserve">
<value>You need to run as administrator to modify this setting.</value>
</data>
<data name="MouseWithoutBorders_ServiceUserUninstallWarning.Title" xml:space="preserve">
<value>If PowerToys is installed as a user, uninstalling/upgrading may require the Mouse Without Borders service to be removed manually later.</value>
</data>
<data name="MouseWithoutBorders_ServiceSettings.Header" xml:space="preserve">
<value>Service</value>
</data>
<data name="MouseWithoutBorders_Toggle_Enable.Header" xml:space="preserve">
<value>Enable Mouse Without Borders</value>
</data>
<data name="MouseWithoutBorders.SecondaryLinksHeader" xml:space="preserve">
<value>Attribution</value>
<comment>giving credit to the projects this utility was based on</comment>
</data>
<data name="MouseWithoutBorders.ModuleDescription" xml:space="preserve">
<value>Mouse Without Borders is a quick and easy way to move your cursor across multiple devices.</value>
<comment>"Mouse Without Borders" is the name of the utility</comment>
</data>
<data name="MouseWithoutBorders.ModuleTitle" xml:space="preserve">
<value>Mouse Without Borders</value>
<comment>"Mouse Without Borders" is the name of the utility</comment>
</data>
<data name="MouseWithoutBorders_UseService.Header" xml:space="preserve">
<value>Use Service</value>
</data>
<data name="MouseWithoutBorders_UseService.Description"
xml:space="preserve">
<value>Runs in service mode, that allows MWB to control remote machines when they're locked. Also allows control of system and administrator applications.</value>
</data>
<data name="MouseWithoutBorders_MatrixOneRow.Header" xml:space="preserve">
<value>Devices in a single row</value>
</data>
<data name="MouseWithoutBorders_MatrixOneRow.Description" xml:space="preserve">
<value>Sets whether the devices should are aligned on a single row. A two by two matrix is considered otherwise.</value>
</data>
<data name="MouseWithoutBorders_WrapMouse.Header" xml:space="preserve">
<value>Wrap mouse</value>
</data>
<data name="MouseWithoutBorders_WrapMouse.Description"
xml:space="preserve">
<value>Move control back to the first machine when mouse moves past the last one.</value>
</data>
<data name="MouseWithoutBorders_ShareClipboard.Header" xml:space="preserve">
<value>Share clipboard</value>
</data>
<data name="MouseWithoutBorders_TransferFile.Header" xml:space="preserve">
<value>Transfer file</value>
</data>
<data name="MouseWithoutBorders_HideMouseAtScreenEdge.Header" xml:space="preserve">
<value>Hide mouse at the screen edge</value>
</data>
<data name="MouseWithoutBorders_DrawMouseCursor.Header" xml:space="preserve">
<value>Draw mouse cursor</value>
</data>
<data name="MouseWithoutBorders_ValidateRemoteMachineIP.Header" xml:space="preserve">
<value>Validate remote machine IP</value>
</data>
<data name="MouseWithoutBorders_SameSubnetOnly.Header" xml:space="preserve">
<value>Same subnet only</value>
</data>
<data name="MouseWithoutBorders_BlockScreenSaverOnOtherMachines.Header" xml:space="preserve">
<value>Block screen saver on other machines</value>
</data>
<data name="MouseWithoutBorders_MoveMouseRelatively.Header" xml:space="preserve">
<value>Move mouse relatively</value>
</data>
<data name="MouseWithoutBorders_BlockMouseAtScreenCorners.Header" xml:space="preserve">
<value>Block mouse at screen corners</value>
</data>
<data name="MouseWithoutBorders_ShowClipboardAndNetworkStatusMessages.Header" xml:space="preserve">
<value>Show clipboard and network status messages</value>
</data>
<data name="MouseWithoutBorders_ShowOriginalUI.Header" xml:space="preserve">
<value>Show the original Mouse Without Borders UI</value>
</data>
<data name="MouseWithoutBorders_ShowOriginalUI.Description" xml:space="preserve">
<value>This is accessible from the system tray and requires a restart.</value>
</data>
<data name="MouseWithoutBorders_ShareClipboard.Description"
xml:space="preserve">
<value>If share clipboard stops working, Ctrl+Alt+Del then Esc may solve the problem.</value>
</data>
<data name="MouseWithoutBorders_TransferFile.Description"
xml:space="preserve">
<value>If a file (&lt;100MB) is copied, it will be transferred to the remote machine clipboard.</value>
</data>
<data name="MouseWithoutBorders_HideMouseAtScreenEdge.Description"
xml:space="preserve">
<value>Hide the mouse cursor at the top edge of the screen when switching to other machine. This option also steals the focus from any full-screen app to ensure the keyboard input is redirected.</value>
</data>
<data name="MouseWithoutBorders_DrawMouseCursor.Description"
xml:space="preserve">
<value>Mouse cursor may not be visible in Windows 10 and later versions of Windows when there is no physical mouse attached.</value>
</data>
<data name="MouseWithoutBorders_ValidateRemoteMachineIP.Description"
xml:space="preserve">
<value>Reverse DNS lookup to validate machine IP Address.</value>
</data>
<data name="MouseWithoutBorders_SameSubnetOnly.Description"
xml:space="preserve">
<value>Only connect to machines in the same intranet NNN.NNN.*.* (only works when both machines have IPv4 enabled)</value>
</data>
<data name="MouseWithoutBorders_BlockScreenSaverOnOtherMachines.Description"
xml:space="preserve">
<value>Prevent screen saver from starting on other machines when user is actively working on this machine.</value>
</data>
<data name="MouseWithoutBorders_MoveMouseRelatively.Description"
xml:space="preserve">
<value>Use this option when remote machine's monitor settings are different, or remote machine has multiple monitors.</value>
</data>
<data name="MouseWithoutBorders_BlockMouseAtScreenCorners.Description"
xml:space="preserve">
<value>To avoid accident machine-switch at screen corners.</value>
</data>
<data name="MouseWithoutBorders_ShowClipboardAndNetworkStatusMessages.Description"
xml:space="preserve">
<value>Show clipboard activities and network status in system tray notifications</value>
</data>
<data name="MouseWithoutBorders_KeyboardShortcuts_Group.Header" xml:space="preserve">
<value>Keyboard Shortcuts</value>
<comment>keyboard is the hardware peripheral</comment>
</data>
<data name="MouseWithoutBorders_EasyMouseOption.Header" xml:space="preserve">
<value>Easy Mouse: Move between machines by moving the mouse pointer to the screen edges.</value>
</data>
<data name="MouseWithoutBorders_EasyMouseOption.Description" xml:space="preserve">
<value>Can also be set to move only when pressing Shift or Ctrl.</value>
<comment>Shift and Ctrl are the keyboard keys</comment>
</data>
<data name="MouseWithoutBorders_EasyMouseOption_Disabled.Content" xml:space="preserve">
<value>Disabled</value>
</data>
<data name="MouseWithoutBorders_EasyMouseOption_Enabled.Content" xml:space="preserve">
<value>Enabled</value>
</data>
<data name="MouseWithoutBorders_EasyMouseOption_Ctrl.Content" xml:space="preserve">
<value>Ctrl</value>
<comment>This is the Ctrl keyboard key</comment>
</data>
<data name="MouseWithoutBorders_EasyMouseOption_Shift.Content" xml:space="preserve">
<value>Shift</value>
<comment>This is the Shift keyboard key</comment>
</data>
<data name="MouseWithoutBorders_ToggleEasyMouseShortcut.Header" xml:space="preserve">
<value>Shortcut to toggle Easy Mouse. Ctrl+Alt+:</value>
<comment>Ctrl and Alt are the keyboard keys</comment>
</data>
<data name="MouseWithoutBorders_ToggleEasyMouseShortcut.Description" xml:space="preserve">
<value>Only works if EasyMouse is set to Enabled or Disabled.</value>
</data>
<data name="MouseWithoutBorders_ToggleEasyMouseShortcut_Disabled.Content" xml:space="preserve">
<value>Disabled</value>
</data>
<data name="MouseWithoutBorders_SwitchBetweenMachineShortcut.Header" xml:space="preserve">
<value>Shortcut to switch between machines. Ctrl+Alt+:</value>
<comment>Ctrl and Alt are the keyboard keys</comment>
</data>
<data name="MouseWithoutBorders_SwitchBetweenMachineShortcut.Description" xml:space="preserve">
<value>Click on Ctrl+Alt+ the chosen option to switch between machines.</value>
<comment>Ctrl and Alt are the keyboard keys</comment>
</data>
<data name="MouseWithoutBorders_SwitchBetweenMachineShortcut_F1.Content" xml:space="preserve">
<value>F1, F2, F3, F4</value>
<comment>Don't localize. These are keyboard keys</comment>
</data>
<data name="MouseWithoutBorders_SwitchBetweenMachineShortcut_1.Content" xml:space="preserve">
<value>1, 2, 3, 4</value>
<comment>Don't localize. These are keyboard keys</comment>
</data>
<data name="MouseWithoutBorders_SwitchBetweenMachineShortcut_Disabled.Content" xml:space="preserve">
<value>Disabled</value>
</data>
<data name="MouseWithoutBorders_LockMachinesShortcut.Header" xml:space="preserve">
<value>Shortcut to press twice quickly to lock all machines. Ctrl+Alt+:</value>
<comment>Ctrl and Alt are the keyboard keys</comment>
</data>
<data name="MouseWithoutBorders_LockMachinesShortcut.Description" xml:space="preserve">
<value>Click on Ctrl+Alt+ the chosen option twice quickly to lock all machines. Note: Only the machines which have the same shortcut configured will be locked.</value>
<comment>Ctrl and Alt are the keyboard keys</comment>
</data>
<data name="MouseWithoutBorders_LockMachinesShortcut_Disabled.Content" xml:space="preserve">
<value>Disabled</value>
</data>
<data name="MouseWithoutBorders_ReconnectShortcut.Header" xml:space="preserve">
<value>Shortcut to try reconnecting. Ctrl+Alt+:</value>
<comment>Ctrl and Alt are the keyboard keys</comment>
</data>
<data name="MouseWithoutBorders_ReconnectShortcut.Description" xml:space="preserve">
<value>Click on Ctrl+Alt+ the chosen option to reconnect.</value>
<comment>Ctrl and Alt are the keyboard keys</comment>
</data>
<data name="MouseWithoutBorders_ReconnectShortcut_Disabled.Content" xml:space="preserve">
<value>Disabled</value>
</data>
<data name="MouseWithoutBorders_Switch2AllPcShortcut.Header" xml:space="preserve">
<value>Shortcut to switch to multiple machine mode. Ctrl+Alt+:</value>
<comment>Ctrl and Alt are the keyboard keys</comment>
</data>
<data name="MouseWithoutBorders_Switch2AllPcShortcut.Description" xml:space="preserve">
<value>Allows controlling all computers at once. Pressing Ctrl three times is also an option.</value>
<comment>This is the Ctrl keyboard key</comment>
</data>
<data name="MouseWithoutBorders_Switch2AllPcShortcut_Disabled.Content" xml:space="preserve">
<value>Disabled</value>
</data>
<data name="MouseWithoutBorders_Switch2AllPcShortcut_Ctrl3.Content" xml:space="preserve">
<value>Ctrl three times</value>
<comment>This is the Ctrl keyboard key</comment>
</data>
<data name="VideoConference_Enable.Header" xml:space="preserve">
<value>Enable Video Conference Mute</value>
</data>
@@ -364,6 +633,10 @@
<value>Keyboard Manager</value>
<comment>Product name: Navigation view item name for Keyboard Manager</comment>
</data>
<data name="Shell_MouseWithoutBorders.Content" xml:space="preserve">
<value>Mouse Without Borders</value>
<comment>Product name: Navigation view item name for Mouse Without Borders</comment>
</data>
<data name="Shell_MouseUtilities.Content" xml:space="preserve">
<value>Mouse utilities</value>
<comment>Product name: Navigation view item name for Mouse utilities</comment>
@@ -1674,6 +1947,9 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
<data name="Oobe_KBM.Description" xml:space="preserve">
<value>Keyboard Manager allows you to customize the keyboard to be more productive by remapping keys and creating your own keyboard shortcuts.</value>
</data>
<data name="Oobe_MouseWithoutBorders.Description" xml:space="preserve">
<value>Mouse Without Borders enables using the mouse pointer, keyboard, clipboard and drag and drop between machines in the same local network.</value>
</data>
<data name="Oobe_PowerRename.Description" xml:space="preserve">
<value>PowerRename enables you to perform simple bulk renaming, searching and replacing file names.</value>
</data>
@@ -1753,6 +2029,12 @@ From there, simply click on one of the supported files in the File Explorer and
<data name="Oobe_KBM_TipsAndTricks.Text" xml:space="preserve">
<value>Want to only have a shortcut work for a single application? Use the Target App field when creating the shortcut remapping.</value>
</data>
<data name="Oobe_MouseWithoutBorders_HowToUse.Text" xml:space="preserve">
<value>Use the Settings screen on each machine to connect to the other machines using the same key. If a connection is not working, it may be necessary to add an exception to the Windows Firewall.</value>
</data>
<data name="Oobe_MouseWithoutBorders_TipsAndTricks.Text" xml:space="preserve">
<value>Use the service option in Settings to install a service enabling Mouse Without Borders to function even in the lock screen.</value>
</data>
<data name="Oobe_PowerRename_HowToUse.Text" xml:space="preserve">
<value>In File Explorer, right-click one or more selected files and select **PowerRename** from the context menu.</value>
</data>
@@ -1812,6 +2094,10 @@ From there, simply click on one of the supported files in the File Explorer and
<value>Keyboard Manager</value>
<comment>Do not localize this string</comment>
</data>
<data name="Oobe_MouseWithoutBorders.Title" xml:space="preserve">
<value>Mouse Without Borders</value>
<comment>Product name. Do not localize this string</comment>
</data>
<data name="Oobe_PowerRename.Title" xml:space="preserve">
<value>PowerRename</value>
<comment>Do not localize this string</comment>

View File

@@ -93,6 +93,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("MouseUtils_MousePointerCrosshairs/Header"), IsEnabled = generalSettingsConfig.Enabled.MousePointerCrosshairs, Tag = "MousePointerCrosshairs", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsMouseCrosshairs.png", EnabledChangedCallback = EnabledChangedOnUI });
}
if ((gpo = GPOWrapper.GetConfiguredMouseWithoutBordersEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled)
{
FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("MouseWithoutBorders/ModuleTitle"), IsEnabled = generalSettingsConfig.Enabled.MouseWithoutBorders, Tag = "MouseWithoutBorders", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsMouseWithoutBorders.png", EnabledChangedCallback = EnabledChangedOnUI });
}
if ((gpo = GPOWrapper.GetConfiguredPastePlainEnabledValue()) != GpoRuleConfigured.Disabled && gpo != GpoRuleConfigured.Enabled)
{
FlyoutMenuItems.Add(new FlyoutMenuItem() { Label = resourceLoader.GetString("PastePlain/ModuleTitle"), IsEnabled = generalSettingsConfig.Enabled.PastePlain, Tag = "PastePlain", Icon = "ms-appx:///Assets/FluentIcons/FluentIconsPastePlain.png", EnabledChangedCallback = EnabledChangedOnUI });
@@ -170,6 +175,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
case "MouseHighlighter": item.IsEnabled = generalSettingsConfig.Enabled.MouseHighlighter; break;
case "MouseJump": item.IsEnabled = generalSettingsConfig.Enabled.MouseJump; break;
case "MousePointerCrosshairs": item.IsEnabled = generalSettingsConfig.Enabled.MousePointerCrosshairs; break;
case "MouseWithoutBorders": item.IsEnabled = generalSettingsConfig.Enabled.MouseWithoutBorders; break;
case "PastePlain": item.IsEnabled = generalSettingsConfig.Enabled.PastePlain; break;
case "Peek": item.IsEnabled = generalSettingsConfig.Enabled.Peek; break;
case "PowerRename": item.IsEnabled = generalSettingsConfig.Enabled.PowerRename; break;

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@ using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Services;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;

View File

@@ -0,0 +1,443 @@
<Page x:Class="Microsoft.PowerToys.Settings.UI.Views.MouseWithoutBordersPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:labs="using:CommunityToolkit.Labs.WinUI" mc:Ignorable="d"
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
xmlns:converters="using:Microsoft.PowerToys.Settings.UI.Converters"
xmlns:toolkitConverters="using:CommunityToolkit.WinUI.UI.Converters"
xmlns:ui="using:CommunityToolkit.WinUI.UI"
AutomationProperties.LandmarkType="Main">
<Page.Resources>
<converters:NegativeBoolToVisibilityConverter
x:Key="negativeBoolToVisibilityConverter" />
<toolkitConverters:BoolToObjectConverter
x:Key="OneRowMatrixBoolToNumberOfRowsConverter" TrueValue="4"
FalseValue="2" />
</Page.Resources>
<controls:SettingsPageControl x:Uid="MouseWithoutBorders"
ModuleImageSource="ms-appx:///Assets/Modules/MouseWithoutBorders.png">
<controls:SettingsPageControl.ModuleContent>
<StackPanel Orientation="Vertical">
<controls:SettingsGroup
x:Uid="MouseWithoutBorders_ActivationSettings">
<labs:SettingsCard x:Uid="MouseWithoutBorders_Toggle_Enable"
HeaderIcon="{ui:BitmapIcon Source=/Assets/FluentIcons/FluentIconsMouseWithoutBorders.png}"
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabledGpoConfigured, Converter={StaticResource BoolNegationConverter}}">
<ToggleSwitch x:Uid="ToggleSwitch"
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.CanBeEnabled}"
IsOn="{x:Bind Mode=TwoWay, Path=ViewModel.IsEnabled}" />
</labs:SettingsCard>
<InfoBar x:Uid="GPO_IsSettingForced" IsClosable="False"
IsOpen="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabledGpoConfigured}"
IsTabStop="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabledGpoConfigured}"
Severity="Informational" />
</controls:SettingsGroup>
<controls:SettingsGroup x:Uid="MouseWithoutBorders_KeySettings"
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}">
<labs:SettingsExpander
x:Uid="MouseWithoutBorders_SecurityKey"
x:Name="MouseWithoutBorders_ConnectSettings"
IsExpanded="{x:Bind Mode=TwoWay, Path=ViewModel.ConnectFieldsVisible}">
<labs:SettingsExpander.Items>
<labs:SettingsCard ContentAlignment="Right">
<StackPanel Orientation="Horizontal" Spacing="4">
<TextBox x:Name="ConnectSecurityKeyTextBox"
Width="250"
PlaceholderText="Security Key" />
<TextBox x:Name="ConnectPCNameTextBox"
Width="250"
PlaceholderText="PC Name" />
<Button x:Uid="MouseWithoutBorders_Connect"
Command="{x:Bind Mode=OneTime, Path=ConnectCommand}"
Style="{StaticResource AccentButtonStyle}" />
</StackPanel>
</labs:SettingsCard>
</labs:SettingsExpander.Items>
<StackPanel Orientation="Horizontal" Spacing="4">
<TextBox IsReadOnly="True"
Text="{x:Bind Mode=TwoWay, Path=ViewModel.SecurityKey}" />
<Button x:Uid="MouseWithoutBorders_NewKey"
Command="{x:Bind Mode=OneTime, Path=GenerateNewKeyCommand}"
Style="{StaticResource AccentButtonStyle}" />
<Button x:Uid="MouseWithoutBorders_Connect"
Command="{x:Bind Mode=OneTime, Path=ShowConnectFieldsCommand}"
Visibility="{x:Bind Path=ViewModel.ConnectFieldsVisible, Mode=OneWay, Converter={StaticResource negativeBoolToVisibilityConverter}}"
Style="{StaticResource AccentButtonStyle}" />
</StackPanel>
</labs:SettingsExpander>
</controls:SettingsGroup>
<controls:SettingsGroup
x:Uid="MouseWithoutBorders_ThisMachineNameLabel"
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}">
<Button
Command="{x:Bind Mode=OneTime, Path=CopyPCNameCommand}"
Style="{StaticResource AccentButtonStyle}">
<ToolTipService.ToolTip>
<TextBlock
x:Uid="MouseWithoutBorders_CopyMachineName" />
</ToolTipService.ToolTip>
<TextBlock
Text="{x:Bind Mode=OneTime, Path=ViewModel.MachineHostName}" />
</Button>
</controls:SettingsGroup>
<controls:SettingsGroup
x:Uid="MouseWithoutBorders_DeviceLayoutSettings"
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}">
<InfoBar x:Uid="MouseWithoutBorders_CannotDragDropAsAdmin"
IsClosable="True"
IsOpen="{x:Bind Mode=OneWay, Path=ViewModel.IsElevated}"
IsTabStop="True" Severity="Informational" />
<StackPanel Orientation="Vertical"
HorizontalAlignment="Center">
<StackPanel HorizontalAlignment="Center"
Orientation="Horizontal">
<ItemsControl x:Name="DevicesItemsControl"
ItemsSource="{Binding MachineMatrixString, Mode=TwoWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid Orientation="Horizontal"
MaximumRowsOrColumns="{Binding MatrixOneRow, Mode=OneWay, Converter={StaticResource OneRowMatrixBoolToNumberOfRowsConverter}}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--TODO: colors?-->
<!--BorderBrush="#DFDFDF"
Background="#d4d4d4"-->
<!-- Dragging while elevated crashes on WinUI3: https://github.com/microsoft/microsoft-ui-xaml/issues/7690 -->
<Border
BorderBrush="{Binding Item.StatusBrush}"
Background="{ThemeResource SubtleButtonBackgroundDisabled}"
Width="90" Height="90"
BorderThickness="2" Margin="3"
CanDrag="{Binding Mode=OneWay, Path=Item.CanDragDrop}"
AllowDrop="{Binding Mode=OneWay, Path=Item.CanDragDrop}"
CornerRadius="4"
DragStarting="Device_DragStarting"
DragOver="Device_DragOver"
DataContext="{Binding}"
ToolTipService.ToolTip="{Binding Item.Name, Mode=OneWay}"
Drop="Device_Drop">
<StackPanel
VerticalAlignment="Center"
Orientation="Vertical"
Opacity="0.5">
<FontIcon FontSize="39"
Width="50" Height="50"
FontFamily="Segoe MDL2 Assets"
Glyph="&#xE7F4;" />
<TextBlock Tag="DeviceName"
HorizontalAlignment="Center"
Text="{Binding Item.Name}" />
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<Button
HorizontalAlignment="Center"
Command="{x:Bind Mode=OneTime, Path=ReconnectCommand}"
Style="{StaticResource AccentButtonStyle}">
<ToolTipService.ToolTip>
<TextBlock
x:Uid="MouseWithoutBorders_ReconnectTooltip" />
</ToolTipService.ToolTip>
<TextBlock
x:Uid="MouseWithoutBorders_ReconnectButton" />
</Button>
</StackPanel>
<labs:SettingsCard x:Uid="MouseWithoutBorders_MatrixOneRow">
<ToggleSwitch
x:Uid="MouseWithoutBorders_MatrixOneRow_ToggleSwitch"
IsOn="{x:Bind ViewModel.MatrixOneRow, Mode=TwoWay}" />
</labs:SettingsCard>
</controls:SettingsGroup>
<controls:SettingsGroup
x:Uid="MouseWithoutBorders_ServiceSettings"
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.CanToggleUseService}">
<labs:SettingsCard x:Uid="MouseWithoutBorders_UseService">
<ToggleSwitch
x:Uid="MouseWithoutBorders_UseService_ToggleSwitch"
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}"
IsOn="{x:Bind ViewModel.UseService, Mode=TwoWay}" />
</labs:SettingsCard>
<InfoBar x:Uid="MouseWithoutBorders_RunAsAdminText"
IsClosable="False"
IsOpen="{x:Bind Mode=OneWay, Path=ViewModel.CanToggleUseService, Converter={StaticResource BoolNegationConverter}}"
IsTabStop="True" Severity="Informational" />
<InfoBar
x:Uid="MouseWithoutBorders_ServiceUserUninstallWarning"
IsOpen="True" IsClosable="True" IsTabStop="True"
Severity="Warning" />
<labs:SettingsCard
x:Uid="MouseWithoutBorders_UninstallService" ActionIcon="{ui:FontIcon FontFamily={StaticResource SymbolThemeFontFamily},
FontSize=14,
Glyph=&#xE8A7;}"
Command="{x:Bind ViewModel.UninstallServiceEventHandler}"
IsClickEnabled="{x:Bind Mode=OneWay, Path=ViewModel.CanUninstallService}" />
</controls:SettingsGroup>
<controls:SettingsGroup x:Uid="MouseWithoutBorders_Settings"
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}">
<labs:SettingsCard x:Uid="MouseWithoutBorders_WrapMouse">
<ToggleSwitch
x:Uid="MouseWithoutBorders_WrapMouse_ToggleSwitch"
IsOn="{x:Bind ViewModel.WrapMouse, Mode=TwoWay}" />
</labs:SettingsCard>
<labs:SettingsCard
x:Uid="MouseWithoutBorders_ShareClipboard">
<ToggleSwitch
x:Uid="MouseWithoutBorders_ShareClipboard_ToggleSwitch"
IsOn="{x:Bind ViewModel.ShareClipboard, Mode=TwoWay}" />
</labs:SettingsCard>
<labs:SettingsCard x:Uid="MouseWithoutBorders_TransferFile">
<ToggleSwitch
x:Uid="MouseWithoutBorders_TransferFile_ToggleSwitch"
IsOn="{x:Bind ViewModel.TransferFile, Mode=TwoWay}" />
</labs:SettingsCard>
<labs:SettingsCard
x:Uid="MouseWithoutBorders_HideMouseAtScreenEdge">
<ToggleSwitch
x:Uid="MouseWithoutBorders_HideMouseAtScreenEdge_ToggleSwitch"
IsOn="{x:Bind ViewModel.HideMouseAtScreenEdge, Mode=TwoWay}" />
</labs:SettingsCard>
<labs:SettingsCard
x:Uid="MouseWithoutBorders_DrawMouseCursor">
<ToggleSwitch
x:Uid="MouseWithoutBorders_DrawMouseCursor_ToggleSwitch"
IsOn="{x:Bind ViewModel.DrawMouseCursor, Mode=TwoWay}" />
</labs:SettingsCard>
<labs:SettingsCard
x:Uid="MouseWithoutBorders_ValidateRemoteMachineIP">
<ToggleSwitch
x:Uid="MouseWithoutBorders_ValidateRemoteMachineIP_ToggleSwitch"
IsOn="{x:Bind ViewModel.ValidateRemoteMachineIP, Mode=TwoWay}" />
</labs:SettingsCard>
<labs:SettingsCard
x:Uid="MouseWithoutBorders_SameSubnetOnly">
<ToggleSwitch
x:Uid="MouseWithoutBorders_SameSubnetOnly_ToggleSwitch"
IsOn="{x:Bind ViewModel.SameSubnetOnly, Mode=TwoWay}" />
</labs:SettingsCard>
<labs:SettingsCard
x:Uid="MouseWithoutBorders_BlockScreenSaverOnOtherMachines">
<ToggleSwitch
x:Uid="MouseWithoutBorders_BlockScreenSaverOnOtherMachines_ToggleSwitch"
IsOn="{x:Bind ViewModel.BlockScreenSaverOnOtherMachines, Mode=TwoWay}" />
</labs:SettingsCard>
<labs:SettingsCard
x:Uid="MouseWithoutBorders_MoveMouseRelatively">
<ToggleSwitch
x:Uid="MouseWithoutBorders_MoveMouseRelatively_ToggleSwitch"
IsOn="{x:Bind ViewModel.MoveMouseRelatively, Mode=TwoWay}" />
</labs:SettingsCard>
<labs:SettingsCard
x:Uid="MouseWithoutBorders_BlockMouseAtScreenCorners">
<ToggleSwitch
x:Uid="MouseWithoutBorders_BlockMouseAtScreenCorners_ToggleSwitch"
IsOn="{x:Bind ViewModel.BlockMouseAtScreenCorners, Mode=TwoWay}" />
</labs:SettingsCard>
<labs:SettingsCard
x:Uid="MouseWithoutBorders_ShowClipboardAndNetworkStatusMessages">
<ToggleSwitch
x:Uid="MouseWithoutBorders_ShowClipboardAndNetworkStatusMessages_ToggleSwitch"
IsOn="{x:Bind ViewModel.ShowClipboardAndNetworkStatusMessages, Mode=TwoWay}" />
</labs:SettingsCard>
</controls:SettingsGroup>
<controls:SettingsGroup x:Uid="MouseWithoutBorders_KeyboardShortcuts_Group"
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}">
<labs:SettingsCard x:Uid="MouseWithoutBorders_EasyMouseOption">
<ComboBox
MinWidth="{StaticResource SettingActionControlMinWidth}"
SelectedIndex="{x:Bind Path=ViewModel.EasyMouseOptionIndex, Mode=TwoWay}">
<ComboBoxItem x:Uid="MouseWithoutBorders_EasyMouseOption_Disabled" />
<ComboBoxItem x:Uid="MouseWithoutBorders_EasyMouseOption_Enabled" />
<ComboBoxItem x:Uid="MouseWithoutBorders_EasyMouseOption_Ctrl" />
<ComboBoxItem x:Uid="MouseWithoutBorders_EasyMouseOption_Shift" />
</ComboBox>
</labs:SettingsCard>
<labs:SettingsCard x:Uid="MouseWithoutBorders_ToggleEasyMouseShortcut">
<ComboBox
MinWidth="{StaticResource SettingActionControlMinWidth}"
SelectedIndex="{x:Bind Path=ViewModel.ToggleEasyMouseShortcutIndex, Mode=TwoWay}"
>
<ComboBoxItem x:Uid="MouseWithoutBorders_ToggleEasyMouseShortcut_Disabled" />
<ComboBoxItem>A</ComboBoxItem>
<ComboBoxItem>B</ComboBoxItem>
<ComboBoxItem>C</ComboBoxItem>
<ComboBoxItem>D</ComboBoxItem>
<ComboBoxItem>E</ComboBoxItem>
<ComboBoxItem>F</ComboBoxItem>
<ComboBoxItem>G</ComboBoxItem>
<ComboBoxItem>H</ComboBoxItem>
<ComboBoxItem>I</ComboBoxItem>
<ComboBoxItem>J</ComboBoxItem>
<ComboBoxItem>K</ComboBoxItem>
<ComboBoxItem>L</ComboBoxItem>
<ComboBoxItem>M</ComboBoxItem>
<ComboBoxItem>N</ComboBoxItem>
<ComboBoxItem>O</ComboBoxItem>
<ComboBoxItem>P</ComboBoxItem>
<ComboBoxItem>Q</ComboBoxItem>
<ComboBoxItem>R</ComboBoxItem>
<ComboBoxItem>S</ComboBoxItem>
<ComboBoxItem>T</ComboBoxItem>
<ComboBoxItem>U</ComboBoxItem>
<ComboBoxItem>V</ComboBoxItem>
<ComboBoxItem>W</ComboBoxItem>
<ComboBoxItem>X</ComboBoxItem>
<ComboBoxItem>Y</ComboBoxItem>
<ComboBoxItem>Z</ComboBoxItem>
</ComboBox>
</labs:SettingsCard>
<labs:SettingsCard x:Uid="MouseWithoutBorders_LockMachinesShortcut">
<ComboBox
MinWidth="{StaticResource SettingActionControlMinWidth}"
SelectedIndex="{x:Bind Path=ViewModel.LockMachinesShortcutIndex, Mode=TwoWay}"
>
<ComboBoxItem x:Uid="MouseWithoutBorders_LockMachinesShortcut_Disabled" />
<ComboBoxItem>A</ComboBoxItem>
<ComboBoxItem>B</ComboBoxItem>
<ComboBoxItem>C</ComboBoxItem>
<ComboBoxItem>D</ComboBoxItem>
<ComboBoxItem>E</ComboBoxItem>
<ComboBoxItem>F</ComboBoxItem>
<ComboBoxItem>G</ComboBoxItem>
<ComboBoxItem>H</ComboBoxItem>
<ComboBoxItem>I</ComboBoxItem>
<ComboBoxItem>J</ComboBoxItem>
<ComboBoxItem>K</ComboBoxItem>
<ComboBoxItem>L</ComboBoxItem>
<ComboBoxItem>M</ComboBoxItem>
<ComboBoxItem>N</ComboBoxItem>
<ComboBoxItem>O</ComboBoxItem>
<ComboBoxItem>P</ComboBoxItem>
<ComboBoxItem>Q</ComboBoxItem>
<ComboBoxItem>R</ComboBoxItem>
<ComboBoxItem>S</ComboBoxItem>
<ComboBoxItem>T</ComboBoxItem>
<ComboBoxItem>U</ComboBoxItem>
<ComboBoxItem>V</ComboBoxItem>
<ComboBoxItem>W</ComboBoxItem>
<ComboBoxItem>X</ComboBoxItem>
<ComboBoxItem>Y</ComboBoxItem>
<ComboBoxItem>Z</ComboBoxItem>
</ComboBox>
</labs:SettingsCard>
<labs:SettingsCard x:Uid="MouseWithoutBorders_ReconnectShortcut">
<ComboBox
MinWidth="{StaticResource SettingActionControlMinWidth}"
SelectedIndex="{x:Bind Path=ViewModel.ReconnectShortcutIndex, Mode=TwoWay}"
>
<ComboBoxItem x:Uid="MouseWithoutBorders_ReconnectShortcut_Disabled" />
<ComboBoxItem>A</ComboBoxItem>
<ComboBoxItem>B</ComboBoxItem>
<ComboBoxItem>C</ComboBoxItem>
<ComboBoxItem>D</ComboBoxItem>
<ComboBoxItem>E</ComboBoxItem>
<ComboBoxItem>F</ComboBoxItem>
<ComboBoxItem>G</ComboBoxItem>
<ComboBoxItem>H</ComboBoxItem>
<ComboBoxItem>I</ComboBoxItem>
<ComboBoxItem>J</ComboBoxItem>
<ComboBoxItem>K</ComboBoxItem>
<ComboBoxItem>L</ComboBoxItem>
<ComboBoxItem>M</ComboBoxItem>
<ComboBoxItem>N</ComboBoxItem>
<ComboBoxItem>O</ComboBoxItem>
<ComboBoxItem>P</ComboBoxItem>
<ComboBoxItem>Q</ComboBoxItem>
<ComboBoxItem>R</ComboBoxItem>
<ComboBoxItem>S</ComboBoxItem>
<ComboBoxItem>T</ComboBoxItem>
<ComboBoxItem>U</ComboBoxItem>
<ComboBoxItem>V</ComboBoxItem>
<ComboBoxItem>W</ComboBoxItem>
<ComboBoxItem>X</ComboBoxItem>
<ComboBoxItem>Y</ComboBoxItem>
<ComboBoxItem>Z</ComboBoxItem>
</ComboBox>
</labs:SettingsCard>
<labs:SettingsCard x:Uid="MouseWithoutBorders_Switch2AllPcShortcut">
<ComboBox
MinWidth="{StaticResource SettingActionControlMinWidth}"
SelectedIndex="{x:Bind Path=ViewModel.Switch2AllPcShortcutIndex, Mode=TwoWay}"
>
<ComboBoxItem x:Uid="MouseWithoutBorders_Switch2AllPcShortcut_Disabled" />
<ComboBoxItem x:Uid="MouseWithoutBorders_Switch2AllPcShortcut_Ctrl3" />
<ComboBoxItem>A</ComboBoxItem>
<ComboBoxItem>B</ComboBoxItem>
<ComboBoxItem>C</ComboBoxItem>
<ComboBoxItem>D</ComboBoxItem>
<ComboBoxItem>E</ComboBoxItem>
<ComboBoxItem>F</ComboBoxItem>
<ComboBoxItem>G</ComboBoxItem>
<ComboBoxItem>H</ComboBoxItem>
<ComboBoxItem>I</ComboBoxItem>
<ComboBoxItem>J</ComboBoxItem>
<ComboBoxItem>K</ComboBoxItem>
<ComboBoxItem>L</ComboBoxItem>
<ComboBoxItem>M</ComboBoxItem>
<ComboBoxItem>N</ComboBoxItem>
<ComboBoxItem>O</ComboBoxItem>
<ComboBoxItem>P</ComboBoxItem>
<ComboBoxItem>Q</ComboBoxItem>
<ComboBoxItem>R</ComboBoxItem>
<ComboBoxItem>S</ComboBoxItem>
<ComboBoxItem>T</ComboBoxItem>
<ComboBoxItem>U</ComboBoxItem>
<ComboBoxItem>V</ComboBoxItem>
<ComboBoxItem>W</ComboBoxItem>
<ComboBoxItem>X</ComboBoxItem>
<ComboBoxItem>Y</ComboBoxItem>
<ComboBoxItem>Z</ComboBoxItem>
</ComboBox>
</labs:SettingsCard>
<labs:SettingsCard
x:Uid="MouseWithoutBorders_SwitchBetweenMachineShortcut"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource SymbolThemeFontFamily},
Glyph=&#xE92E;}">
<ComboBox
MinWidth="{StaticResource SettingActionControlMinWidth}"
SelectedIndex="{x:Bind Path=ViewModel.SelectedSwitchBetweenMachineShortcutOptionsIndex, Mode=TwoWay}">
<!-- These should be in the same order as the array items in MouseWithoutBordersViewModel.cs -->
<ComboBoxItem x:Uid="MouseWithoutBorders_SwitchBetweenMachineShortcut_F1" />
<ComboBoxItem x:Uid="MouseWithoutBorders_SwitchBetweenMachineShortcut_1"/>
<ComboBoxItem x:Uid="MouseWithoutBorders_SwitchBetweenMachineShortcut_Disabled" />
</ComboBox>
</labs:SettingsCard>
</controls:SettingsGroup>
<controls:SettingsGroup
x:Uid="MouseWithoutBorders_TroubleShooting"
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}">
<labs:SettingsCard
x:Uid="MouseWithoutBorders_AddFirewallRuleButtonControl" ActionIcon="{ui:FontIcon FontFamily={StaticResource SymbolThemeFontFamily},
FontSize=14,
Glyph=&#xE8A7;}"
Command="{x:Bind ViewModel.AddFirewallRuleEventHandler}"
IsClickEnabled="True" />
<labs:SettingsCard
x:Uid="MouseWithoutBorders_ShowOriginalUI">
<ToggleSwitch
x:Uid="MouseWithoutBorders_ShowOriginalUI_ToggleSwitch"
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}"
IsOn="{x:Bind ViewModel.ShowOriginalUI, Mode=TwoWay}" />
</labs:SettingsCard>
</controls:SettingsGroup>
</StackPanel>
</controls:SettingsPageControl.ModuleContent>
<controls:SettingsPageControl.PrimaryLinks>
<controls:PageLink x:Uid="LearnMore_MouseWithoutBorders"
Link="https://aka.ms/PowerToysOverview_MouseWithoutBorders" />
</controls:SettingsPageControl.PrimaryLinks>
<controls:SettingsPageControl.SecondaryLinks>
<controls:PageLink Text="Mouse without Borders"
Link="http://aka.ms/mm" />
<controls:PageLink
Link="https://github.com/microsoft/PowerToys/blob/main/COMMUNITY.md#mouse-without-borders-original-contributors"
Text="Truong Do (Đỗ Đức Trường) and other original contributors" />
</controls:SettingsPageControl.SecondaryLinks>
</controls:SettingsPageControl>
</Page>

View File

@@ -0,0 +1,176 @@
// 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.IO.Abstractions;
using System.Net;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Xml.Linq;
using CommunityToolkit.Labs.WinUI;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Microsoft.PowerToys.Settings.UI.ViewModels;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Windows.ApplicationModel.DataTransfer;
using WinRT;
using static Microsoft.PowerToys.Settings.UI.ViewModels.MouseWithoutBordersViewModel;
namespace Microsoft.PowerToys.Settings.UI.Views
{
public sealed partial class MouseWithoutBordersPage : Page, IRefreshablePage
{
private const string MouseWithoutBordersDragDropCheckString = "MWB Device Drag Drop";
private const string PowerToyName = "MouseWithoutBorders";
private MouseWithoutBordersViewModel ViewModel { get; set; }
private readonly IFileSystemWatcher watcher;
public MouseWithoutBordersPage()
{
var settingsUtils = new SettingsUtils();
ViewModel = new MouseWithoutBordersViewModel(
settingsUtils,
SettingsRepository<GeneralSettings>.GetInstance(settingsUtils),
ShellPage.SendDefaultIPCMessage,
DispatcherQueue);
watcher = Helper.GetFileWatcher(
PowerToyName,
"settings.json",
OnConfigFileUpdate);
DataContext = ViewModel;
InitializeComponent();
}
private void OnConfigFileUpdate()
{
// Note: FileSystemWatcher raise notification multiple times for single update operation.
// Todo: Handle duplicate events either by somehow suppress them or re-read the configuration everytime since we will be updating the UI only if something is changed.
this.DispatcherQueue.TryEnqueue(() =>
{
if (ViewModel.LoadUpdatedSettings())
{
ViewModel.NotifyUpdatedSettings();
}
});
}
private static T GetChildOfType<T>(DependencyObject depObj, string tag)
where T : FrameworkElement
{
if (depObj == null)
{
return null;
}
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child, tag);
if (result != null && (string)result.Tag == tag)
{
return result;
}
}
return null;
}
private int GetDeviceIndex(Border b)
{
return b.DataContext.As<IndexedItem<DeviceViewModel>>().Index;
}
private void Device_DragStarting(UIElement sender, DragStartingEventArgs args)
{
args.Data.RequestedOperation = DataPackageOperation.Move;
args.Data.Properties.Add("check-usage", MouseWithoutBordersDragDropCheckString);
args.Data.Properties.Add("index", GetDeviceIndex((Border)sender));
}
private void Device_Drop(object sender, DragEventArgs e)
{
if (e.DataView.Properties.TryGetValue("check-usage", out object checkUsage))
{
// Guard against values dragged from somewhere else
if (!((string)checkUsage).Equals(MouseWithoutBordersDragDropCheckString, StringComparison.Ordinal))
{
return;
}
}
else
{
return;
}
if (!e.DataView.Properties.TryGetValue("index", out object boxIndex))
{
return;
}
var draggedDeviceIndex = (int)boxIndex;
if (draggedDeviceIndex < 0 || draggedDeviceIndex >= ViewModel.MachineMatrixString.Count)
{
return;
}
var targetDeviceIndex = GetDeviceIndex((Border)e.OriginalSource);
ViewModel.MachineMatrixString.Swap(draggedDeviceIndex, targetDeviceIndex);
var itemsControl = (ItemsControl)FindName("DevicesItemsControl");
var binding = itemsControl.GetBindingExpression(ItemsControl.ItemsSourceProperty);
binding.UpdateSource();
}
private void Device_DragOver(object sender, DragEventArgs e)
{
e.AcceptedOperation = DataPackageOperation.Move;
}
public ICommand ShowConnectFieldsCommand => new RelayCommand(ShowConnectFields);
public ICommand ConnectCommand => new AsyncCommand(Connect);
public ICommand GenerateNewKeyCommand => new AsyncCommand(ViewModel.SubmitNewKeyRequestAsync);
public ICommand CopyPCNameCommand => new RelayCommand(ViewModel.CopyMachineNameToClipboard);
public ICommand ReconnectCommand => new AsyncCommand(ViewModel.SubmitReconnectRequestAsync);
private void ShowConnectFields()
{
ViewModel.ConnectFieldsVisible = true;
}
private async Task Connect()
{
if (ConnectPCNameTextBox.Text.Length != 0 && ConnectSecurityKeyTextBox.Text.Length != 0)
{
string pcName = ConnectPCNameTextBox.Text;
string securityKey = ConnectSecurityKeyTextBox.Text.Trim();
await ViewModel.SubmitConnectionRequestAsync(pcName, securityKey);
ConnectPCNameTextBox.Text = string.Empty;
ConnectSecurityKeyTextBox.Text = string.Empty;
}
}
public void RefreshEnabledState()
{
ViewModel.RefreshEnabledState();
}
}
}

View File

@@ -94,6 +94,11 @@
helpers:NavHelper.NavigateTo="views:MouseUtilsPage"
Icon="{ui:BitmapIcon Source=/Assets/FluentIcons/FluentIconsMouseUtils.png}" />
<NavigationViewItem
x:Uid="Shell_MouseWithoutBorders"
helpers:NavHelper.NavigateTo="views:MouseWithoutBordersPage"
Icon="{ui:BitmapIcon Source=/Assets/FluentIcons/FluentIconsMouseWithoutBorders.png}" />
<NavigationViewItem
x:Uid="Shell_PastePlain"
helpers:NavHelper.NavigateTo="views:PastePlainPage"