/ src / settings-ui / Settings.UI / SettingsXAML / Views / MouseWithoutBordersPage.xaml.cs
MouseWithoutBordersPage.xaml.cs
  1  // Copyright (c) Microsoft Corporation
  2  // The Microsoft Corporation licenses this file to you under the MIT license.
  3  // See the LICENSE file in the project root for more information.
  4  
  5  using System;
  6  using System.IO.Abstractions;
  7  using System.Threading.Tasks;
  8  using System.Windows.Input;
  9  using Microsoft.PowerToys.Settings.UI.Helpers;
 10  using Microsoft.PowerToys.Settings.UI.Library;
 11  using Microsoft.PowerToys.Settings.UI.Library.Utilities;
 12  using Microsoft.PowerToys.Settings.UI.ViewModels;
 13  using Microsoft.UI.Xaml;
 14  using Microsoft.UI.Xaml.Controls;
 15  using Microsoft.UI.Xaml.Media;
 16  using Microsoft.UI.Xaml.Navigation;
 17  using Windows.ApplicationModel.DataTransfer;
 18  using WinRT;
 19  
 20  using static Microsoft.PowerToys.Settings.UI.ViewModels.MouseWithoutBordersViewModel;
 21  
 22  namespace Microsoft.PowerToys.Settings.UI.Views
 23  {
 24      public sealed partial class MouseWithoutBordersPage : NavigablePage, IRefreshablePage
 25      {
 26          private const string MouseWithoutBordersDragDropCheckString = "MWB Device Drag Drop";
 27  
 28          private const string PowerToyName = "MouseWithoutBorders";
 29  
 30          private MouseWithoutBordersViewModel ViewModel { get; set; }
 31  
 32          private readonly IFileSystemWatcher watcher;
 33  
 34          public MouseWithoutBordersPage()
 35          {
 36              var settingsUtils = SettingsUtils.Default;
 37              ViewModel = new MouseWithoutBordersViewModel(
 38                  settingsUtils,
 39                  SettingsRepository<GeneralSettings>.GetInstance(settingsUtils),
 40                  ShellPage.SendDefaultIPCMessage,
 41                  DispatcherQueue);
 42  
 43              watcher = Helper.GetFileWatcher(
 44                  PowerToyName,
 45                  "settings.json",
 46                  OnConfigFileUpdate);
 47  
 48              DataContext = ViewModel;
 49              InitializeComponent();
 50  
 51              Loaded += (s, e) => ViewModel.OnPageLoaded();
 52          }
 53  
 54          private void OnConfigFileUpdate()
 55          {
 56              // Note: FileSystemWatcher raise notification multiple times for single update operation.
 57              // Todo: Handle duplicate events either by somehow suppress them or re-read the configuration every time since we will be updating the UI only if something is changed.
 58              this.DispatcherQueue.TryEnqueue(() =>
 59              {
 60                  if (ViewModel.LoadUpdatedSettings())
 61                  {
 62                      ViewModel.NotifyUpdatedSettings();
 63                  }
 64              });
 65          }
 66  
 67          private static T GetChildOfType<T>(DependencyObject depObj, string tag)
 68              where T : FrameworkElement
 69          {
 70              if (depObj == null)
 71              {
 72                  return null;
 73              }
 74  
 75              for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
 76              {
 77                  var child = VisualTreeHelper.GetChild(depObj, i);
 78  
 79                  var result = (child as T) ?? GetChildOfType<T>(child, tag);
 80                  if (result != null && (string)result.Tag == tag)
 81                  {
 82                      return result;
 83                  }
 84              }
 85  
 86              return null;
 87          }
 88  
 89          private int GetDeviceIndex(Border b)
 90          {
 91              return b.DataContext.As<IndexedItem<DeviceViewModel>>().Index;
 92          }
 93  
 94          private void Device_DragStarting(UIElement sender, DragStartingEventArgs args)
 95          {
 96              args.Data.RequestedOperation = DataPackageOperation.Move;
 97              args.Data.Properties.Add("check-usage", MouseWithoutBordersDragDropCheckString);
 98              args.Data.Properties.Add("index", GetDeviceIndex((Border)sender));
 99          }
100  
101          private void Device_Drop(object sender, DragEventArgs e)
102          {
103              if (e.DataView.Properties.TryGetValue("check-usage", out object checkUsage))
104              {
105                  // Guard against values dragged from somewhere else
106                  if (!((string)checkUsage).Equals(MouseWithoutBordersDragDropCheckString, StringComparison.Ordinal))
107                  {
108                      return;
109                  }
110              }
111              else
112              {
113                  return;
114              }
115  
116              if (!e.DataView.Properties.TryGetValue("index", out object boxIndex))
117              {
118                  return;
119              }
120  
121              var draggedDeviceIndex = (int)boxIndex;
122  
123              if (draggedDeviceIndex < 0 || draggedDeviceIndex >= ViewModel.MachineMatrixString.Count)
124              {
125                  return;
126              }
127  
128              var targetDeviceIndex = GetDeviceIndex((Border)e.OriginalSource);
129  
130              ViewModel.MachineMatrixString.Swap(draggedDeviceIndex, targetDeviceIndex);
131              var itemsControl = (ItemsControl)FindName("DevicesItemsControl");
132              var binding = itemsControl.GetBindingExpression(ItemsControl.ItemsSourceProperty);
133              binding.UpdateSource();
134          }
135  
136          private void Device_DragOver(object sender, DragEventArgs e)
137          {
138              e.AcceptedOperation = DataPackageOperation.Move;
139          }
140  
141          public ICommand ShowConnectFieldsCommand => new RelayCommand(ShowConnectFields);
142  
143          public ICommand ConnectCommand => new AsyncCommand(Connect);
144  
145          public ICommand GenerateNewKeyCommand => new AsyncCommand(ViewModel.SubmitNewKeyRequestAsync);
146  
147          public ICommand CopyPCNameCommand => new RelayCommand(ViewModel.CopyMachineNameToClipboard);
148  
149          public ICommand ReconnectCommand => new AsyncCommand(ViewModel.SubmitReconnectRequestAsync);
150  
151          private void ShowConnectFields()
152          {
153              ViewModel.ConnectFieldsVisible = true;
154          }
155  
156          private async Task Connect()
157          {
158              if (ConnectPCNameTextBox.Text.Length != 0 && ConnectSecurityKeyTextBox.Text.Length != 0)
159              {
160                  string pcName = ConnectPCNameTextBox.Text;
161                  string securityKey = ConnectSecurityKeyTextBox.Text.Trim();
162  
163                  await ViewModel.SubmitConnectionRequestAsync(pcName, securityKey);
164  
165                  ConnectPCNameTextBox.Text = string.Empty;
166                  ConnectSecurityKeyTextBox.Text = string.Empty;
167              }
168          }
169  
170          public void RefreshEnabledState()
171          {
172              ViewModel.RefreshEnabledState();
173          }
174      }
175  }