/ src / modules / MeasureTool / MeasureToolUI / MeasureToolXAML / MainWindow.xaml.cs
MainWindow.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  
  7  using Microsoft.UI;
  8  using Microsoft.UI.Windowing;
  9  using Microsoft.UI.Xaml;
 10  using Microsoft.UI.Xaml.Automation.Peers;
 11  using Microsoft.UI.Xaml.Automation.Provider;
 12  using Microsoft.UI.Xaml.Controls;
 13  using Microsoft.UI.Xaml.Controls.Primitives;
 14  using Microsoft.UI.Xaml.Input;
 15  using Settings.UI.Library.Enumerations;
 16  using Windows.Graphics;
 17  using WinUIEx;
 18  
 19  using static NativeMethods;
 20  
 21  namespace MeasureToolUI
 22  {
 23      /// <summary>
 24      /// An empty window that can be used on its own or navigated to within a Frame.
 25      /// </summary>
 26      public sealed partial class MainWindow : WindowEx, IDisposable
 27      {
 28          private const int WindowWidth = 216;
 29          private const int WindowHeight = 50;
 30  
 31          private readonly Settings settings = new();
 32  
 33          private PowerToys.MeasureToolCore.Core _coreLogic;
 34  
 35          private AppWindow _appWindow;
 36          private PointInt32 _initialPosition;
 37  
 38          protected override void OnPositionChanged(PointInt32 position)
 39          {
 40              _appWindow.Move(_initialPosition);
 41              this.SetWindowSize(WindowWidth, WindowHeight);
 42          }
 43  
 44          public MainWindow(PowerToys.MeasureToolCore.Core core)
 45          {
 46              InitializeComponent();
 47  
 48              var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
 49              WindowId windowId = Win32Interop.GetWindowIdFromWindow(hwnd);
 50              _appWindow = AppWindow.GetFromWindowId(windowId);
 51              SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
 52              var presenter = _appWindow.Presenter as OverlappedPresenter;
 53              presenter.IsAlwaysOnTop = true;
 54              this.SetIsAlwaysOnTop(true);
 55              this.SetIsResizable(false);
 56              this.SetIsMinimizable(false);
 57              this.SetIsMaximizable(false);
 58              IsTitleBarVisible = false;
 59  
 60              try
 61              {
 62                  this.SetIsShownInSwitchers(false);
 63              }
 64              catch (NotImplementedException)
 65              {
 66                  // WinUI will throw if explorer is not running, safely ignore
 67              }
 68              catch (Exception)
 69              {
 70              }
 71  
 72              // Remove the caption style from the window style. Windows App SDK 1.6 added it, which made the title bar and borders appear for Measure Tool. This code removes it.
 73              var windowStyle = GetWindowLong(hwnd, GWL_STYLE);
 74              windowStyle = windowStyle & (~WS_CAPTION);
 75              _ = SetWindowLong(hwnd, GWL_STYLE, windowStyle);
 76  
 77              _coreLogic = core;
 78              _coreLogic.InitResources();
 79              Closed += MainWindow_Closed;
 80              DisplayArea displayArea = DisplayArea.GetFromWindowId(windowId, DisplayAreaFallback.Nearest);
 81              float dpiScale = _coreLogic.GetDPIScaleForWindow((int)hwnd);
 82  
 83              _initialPosition = new PointInt32(displayArea.WorkArea.X + (displayArea.WorkArea.Width / 2) - (int)(dpiScale * WindowWidth / 2), displayArea.WorkArea.Y + (int)(dpiScale * 12));
 84  
 85              _coreLogic.SetToolbarBoundingBox(
 86                  _initialPosition.X,
 87                  _initialPosition.Y,
 88                  _initialPosition.X + (int)(dpiScale * WindowWidth),
 89                  _initialPosition.Y + (int)(dpiScale * WindowHeight));
 90              OnPositionChanged(_initialPosition);
 91              SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
 92          }
 93  
 94          private void StackPanel_Loaded(object sender, RoutedEventArgs e)
 95          {
 96              SelectDefaultMeasureStyle();
 97          }
 98  
 99          private void MainWindow_Closed(object sender, WindowEventArgs args)
100          {
101              _coreLogic?.Dispose();
102              _coreLogic = null;
103          }
104  
105          private void UpdateToolUsageCompletionEvent(object sender)
106          {
107              _coreLogic.SetToolCompletionEvent(new PowerToys.MeasureToolCore.ToolSessionCompleted(() =>
108              {
109                  DispatcherQueue.TryEnqueue(() =>
110                  {
111                      ((ToggleButton)sender).IsChecked = false;
112                  });
113              }));
114          }
115  
116          private void UncheckOtherButtons(ToggleButton button)
117          {
118              var panel = button.Parent as Panel;
119              foreach (var elem in panel.Children)
120              {
121                  if (elem is ToggleButton otherButton)
122                  {
123                      if (!button.Equals(otherButton))
124                      {
125                          otherButton.IsChecked = false;
126                      }
127                  }
128              }
129          }
130  
131          private void HandleToolClick(object toolButton, Action startToolAction)
132          {
133              ToggleButton button = toolButton as ToggleButton;
134              if (button == null)
135              {
136                  return;
137              }
138  
139              if (button.IsChecked.GetValueOrDefault())
140              {
141                  UncheckOtherButtons(button);
142                  _coreLogic.ResetState();
143                  UpdateToolUsageCompletionEvent(toolButton);
144                  startToolAction();
145              }
146              else
147              {
148                  _coreLogic.ResetState();
149              }
150          }
151  
152          private void BoundsTool_Click(object sender, RoutedEventArgs e)
153          {
154              HandleToolClick(sender, () => _coreLogic.StartBoundsTool());
155          }
156  
157          private void MeasureTool_Click(object sender, RoutedEventArgs e)
158          {
159              HandleToolClick(sender, () => _coreLogic.StartMeasureTool(true, true));
160          }
161  
162          private void HorizontalMeasureTool_Click(object sender, RoutedEventArgs e)
163          {
164              HandleToolClick(sender, () => _coreLogic.StartMeasureTool(true, false));
165          }
166  
167          private void VerticalMeasureTool_Click(object sender, RoutedEventArgs e)
168          {
169              HandleToolClick(sender, () => _coreLogic.StartMeasureTool(false, true));
170          }
171  
172          private void ClosePanelTool_Click(object sender, RoutedEventArgs e)
173          {
174              _coreLogic.ResetState();
175              this.Close();
176          }
177  
178          private void SelectDefaultMeasureStyle()
179          {
180              ToggleButton responsibleBtn = settings.DefaultMeasureStyle switch
181              {
182                  MeasureToolMeasureStyle.None => null,
183                  MeasureToolMeasureStyle.Bounds => btnBounds,
184                  MeasureToolMeasureStyle.Spacing => btnSpacing,
185                  MeasureToolMeasureStyle.HorizontalSpacing => btnHorizontalSpacing,
186                  MeasureToolMeasureStyle.VerticalSpacing => btnVerticalSpacing,
187                  _ => null,
188              };
189  
190              if (responsibleBtn is not null)
191              {
192                  var peer = FrameworkElementAutomationPeer.FromElement(responsibleBtn) as ToggleButtonAutomationPeer;
193                  peer.Toggle();
194              }
195          }
196  
197          public void Dispose()
198          {
199              _coreLogic?.Dispose();
200          }
201  
202          private void KeyboardAccelerator_Invoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
203          {
204              if (args.Element is ToggleButton toggle)
205              {
206                  var peer = new ToggleButtonAutomationPeer(toggle);
207                  peer.Toggle();
208                  args.Handled = true;
209              }
210              else if (args.Element is Button button)
211              {
212                  var peer = new ButtonAutomationPeer(button);
213                  if (peer.GetPattern(PatternInterface.Invoke) is IInvokeProvider provider)
214                  {
215                      provider.Invoke();
216                      args.Handled = true;
217                  }
218              }
219          }
220      }
221  }