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 }