/ src / settings-ui / Settings.UI / ViewModels / ZoomItViewModel.cs
ZoomItViewModel.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.Collections.ObjectModel;
  7  using System.Globalization;
  8  using System.Runtime.CompilerServices;
  9  using System.Runtime.InteropServices;
 10  using System.Text.Json;
 11  using AllExperiments;
 12  using global::PowerToys.GPOWrapper;
 13  using Microsoft.PowerToys.Settings.UI.Helpers;
 14  using Microsoft.PowerToys.Settings.UI.Library;
 15  using Microsoft.PowerToys.Settings.UI.Library.Helpers;
 16  using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
 17  using Microsoft.PowerToys.Settings.UI.Library.Utilities;
 18  using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
 19  using Microsoft.UI.Xaml.Media;
 20  using Microsoft.Windows.ApplicationModel.Resources;
 21  using Windows.Devices.Enumeration;
 22  
 23  namespace Microsoft.PowerToys.Settings.UI.ViewModels
 24  {
 25      public class ZoomItViewModel : Observable
 26      {
 27          private const string FormatGif = "GIF";
 28          private const string FormatMp4 = "MP4";
 29  
 30          private SettingsUtils SettingsUtils { get; set; }
 31  
 32          private GeneralSettings GeneralSettingsConfig { get; set; }
 33  
 34          private readonly ZoomItSettings _zoomItSettings;
 35  
 36          private Func<string, int> SendConfigMSG { get; }
 37  
 38          private Func<string, string, string, int, string> PickFileDialog { get; }
 39  
 40          private Func<LOGFONT, LOGFONT> PickFontDialog { get; }
 41  
 42          public ButtonClickCommand SelectDemoTypeFileCommand { get; set; }
 43  
 44          public ButtonClickCommand SelectBreakSoundFileCommand { get; set; }
 45  
 46          public ButtonClickCommand SelectBreakBackgroundFileCommand { get; set; }
 47  
 48          public ButtonClickCommand SelectTypeFontCommand { get; set; }
 49  
 50          // These values should track what's in DemoType.h
 51          public int DemoTypeMaxTypingSpeed { get; } = 10;
 52  
 53          public int DemoTypeMinTypingSpeed { get; } = 100;
 54  
 55          public ObservableCollection<Tuple<string, string>> MicrophoneList { get; set; } = new ObservableCollection<Tuple<string, string>>();
 56  
 57          private async void LoadMicrophoneList()
 58          {
 59              ResourceLoader resourceLoader = ResourceLoaderInstance.ResourceLoader;
 60              string recordDefaultMicrophone = resourceLoader.GetString("ZoomIt_Record_Microphones_Default_Name");
 61              MicrophoneList.Add(new Tuple<string, string>(string.Empty, recordDefaultMicrophone));
 62              var microphones = await DeviceInformation.FindAllAsync(DeviceClass.AudioCapture);
 63              foreach (var microphone in microphones)
 64              {
 65                  MicrophoneList.Add(new Tuple<string, string>(microphone.Id, microphone.Name));
 66              }
 67          }
 68  
 69          private static readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions
 70          {
 71              MaxDepth = 0,
 72              IncludeFields = true,
 73          };
 74  
 75          public ZoomItViewModel(SettingsUtils settingsUtils, ISettingsRepository<GeneralSettings> settingsRepository, Func<string, int> ipcMSGCallBackFunc, Func<string, string, string, int, string> pickFileDialog, Func<LOGFONT, LOGFONT> pickFontDialog)
 76          {
 77              ArgumentNullException.ThrowIfNull(settingsUtils);
 78  
 79              SettingsUtils = settingsUtils;
 80  
 81              // To obtain the general settings configurations of PowerToys Settings.
 82              ArgumentNullException.ThrowIfNull(settingsRepository);
 83  
 84              GeneralSettingsConfig = settingsRepository.SettingsConfig;
 85  
 86              var zoomItSettings = global::PowerToys.ZoomItSettingsInterop.ZoomItSettings.LoadSettingsJson();
 87              _zoomItSettings = JsonSerializer.Deserialize<ZoomItSettings>(zoomItSettings, _serializerOptions);
 88  
 89              InitializeEnabledValue();
 90  
 91              // set the callback functions value to handle outgoing IPC message for the enabled value.
 92              SendConfigMSG = ipcMSGCallBackFunc;
 93  
 94              // set the callback for when we need the user to pick a file.
 95              PickFileDialog = pickFileDialog;
 96  
 97              // set the callback for when we need the user to pick a font.
 98              PickFontDialog = pickFontDialog;
 99  
100              _typeFont = TypeFont;
101  
102              SelectDemoTypeFileCommand = new ButtonClickCommand(SelectDemoTypeFileAction);
103              SelectBreakSoundFileCommand = new ButtonClickCommand(SelectBreakSoundFileAction);
104              SelectBreakBackgroundFileCommand = new ButtonClickCommand(SelectBreakBackgroundFileAction);
105              SelectTypeFontCommand = new ButtonClickCommand(SelectTypeFontAction);
106  
107              LoadMicrophoneList();
108          }
109  
110          private void InitializeEnabledValue()
111          {
112              _enabledGpoRuleConfiguration = GPOWrapper.GetConfiguredZoomItEnabledValue();
113              if (_enabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled)
114              {
115                  // Get the enabled state from GPO.
116                  _enabledStateIsGPOConfigured = true;
117                  _isEnabled = _enabledGpoRuleConfiguration == GpoRuleConfigured.Enabled;
118              }
119              else
120              {
121                  _isEnabled = GeneralSettingsConfig.Enabled.ZoomIt;
122              }
123          }
124  
125          private void SendCustomAction(string actionName)
126          {
127              SendConfigMSG("{\"action\":{\"ZoomIt\":{\"action_name\":\"" + actionName + "\", \"value\":\"\"}}}");
128          }
129  
130          public bool IsEnabled
131          {
132              get => _isEnabled;
133  
134              set
135              {
136                  if (_enabledStateIsGPOConfigured)
137                  {
138                      // If it's GPO configured, shouldn't be able to change this state.
139                      return;
140                  }
141  
142                  if (value != _isEnabled)
143                  {
144                      _isEnabled = value;
145  
146                      // Set the status in the general settings configuration
147                      GeneralSettingsConfig.Enabled.ZoomIt = value;
148                      OutGoingGeneralSettings snd = new OutGoingGeneralSettings(GeneralSettingsConfig);
149  
150                      SendConfigMSG(snd.ToString());
151                      OnPropertyChanged(nameof(IsEnabled));
152                  }
153              }
154          }
155  
156          public bool IsEnabledGpoConfigured
157          {
158              get => _enabledStateIsGPOConfigured;
159          }
160  
161          public bool ShowTrayIcon
162          {
163              get => _zoomItSettings.Properties.ShowTrayIcon.Value;
164              set
165              {
166                  if (_zoomItSettings.Properties.ShowTrayIcon.Value != value)
167                  {
168                      _zoomItSettings.Properties.ShowTrayIcon.Value = value;
169                      OnPropertyChanged(nameof(ShowTrayIcon));
170                      NotifySettingsChanged();
171                  }
172              }
173          }
174  
175          public HotkeySettings ZoomToggleKey
176          {
177              get => _zoomItSettings.Properties.ToggleKey.Value;
178              set
179              {
180                  if (_zoomItSettings.Properties.ToggleKey.Value != value)
181                  {
182                      _zoomItSettings.Properties.ToggleKey.Value = value ?? ZoomItProperties.DefaultToggleKey;
183                      OnPropertyChanged(nameof(ZoomToggleKey));
184                      NotifySettingsChanged();
185                  }
186              }
187          }
188  
189          public bool AnimateZoom
190          {
191              get => _zoomItSettings.Properties.AnimateZoom.Value;
192              set
193              {
194                  if (_zoomItSettings.Properties.AnimateZoom.Value != value)
195                  {
196                      _zoomItSettings.Properties.AnimateZoom.Value = value;
197                      OnPropertyChanged(nameof(AnimateZoom));
198                      NotifySettingsChanged();
199                  }
200              }
201          }
202  
203          public bool SmoothImage
204          {
205              get => _zoomItSettings.Properties.SmoothImage.Value;
206              set
207              {
208                  if (_zoomItSettings.Properties.SmoothImage.Value != value)
209                  {
210                      _zoomItSettings.Properties.SmoothImage.Value = value;
211                      OnPropertyChanged(nameof(SmoothImage));
212                      NotifySettingsChanged();
213                  }
214              }
215          }
216  
217          public int ZoominSliderLevel
218          {
219              get => _zoomItSettings.Properties.ZoominSliderLevel.Value;
220              set
221              {
222                  if (_zoomItSettings.Properties.ZoominSliderLevel.Value != value)
223                  {
224                      _zoomItSettings.Properties.ZoominSliderLevel.Value = value;
225                      OnPropertyChanged(nameof(ZoominSliderLevel));
226                      NotifySettingsChanged();
227                  }
228              }
229          }
230  
231          public HotkeySettings LiveZoomToggleKey
232          {
233              get => _zoomItSettings.Properties.LiveZoomToggleKey.Value;
234              set
235              {
236                  if (_zoomItSettings.Properties.LiveZoomToggleKey.Value != value)
237                  {
238                      _zoomItSettings.Properties.LiveZoomToggleKey.Value = value ?? ZoomItProperties.DefaultLiveZoomToggleKey;
239                      OnPropertyChanged(nameof(LiveZoomToggleKey));
240                      OnPropertyChanged(nameof(LiveZoomToggleKeyDraw));
241                      NotifySettingsChanged();
242                  }
243              }
244          }
245  
246          public HotkeySettings LiveZoomToggleKeyDraw
247          {
248              get
249              {
250                  var baseKey = _zoomItSettings.Properties.LiveZoomToggleKey.Value;
251                  if (baseKey == null)
252                  {
253                      return null;
254                  }
255  
256                  // XOR with Shift: if Shift is present, remove it; if absent, add it
257                  return new HotkeySettings(
258                      baseKey.Win,
259                      baseKey.Ctrl,
260                      baseKey.Alt,
261                      !baseKey.Shift,  // XOR with Shift
262                      baseKey.Code);
263              }
264          }
265  
266          public HotkeySettings DrawToggleKey
267          {
268              get => _zoomItSettings.Properties.DrawToggleKey.Value;
269              set
270              {
271                  if (_zoomItSettings.Properties.DrawToggleKey.Value != value)
272                  {
273                      _zoomItSettings.Properties.DrawToggleKey.Value = value ?? ZoomItProperties.DefaultDrawToggleKey;
274                      OnPropertyChanged(nameof(DrawToggleKey));
275                      NotifySettingsChanged();
276                  }
277              }
278          }
279  
280          public HotkeySettings RecordToggleKey
281          {
282              get => _zoomItSettings.Properties.RecordToggleKey.Value;
283              set
284              {
285                  if (_zoomItSettings.Properties.RecordToggleKey.Value != value)
286                  {
287                      _zoomItSettings.Properties.RecordToggleKey.Value = value ?? ZoomItProperties.DefaultRecordToggleKey;
288                      OnPropertyChanged(nameof(RecordToggleKey));
289                      OnPropertyChanged(nameof(RecordToggleKeyCrop));
290                      OnPropertyChanged(nameof(RecordToggleKeyWindow));
291                      NotifySettingsChanged();
292                  }
293              }
294          }
295  
296          public HotkeySettings RecordToggleKeyCrop
297          {
298              get
299              {
300                  var baseKey = _zoomItSettings.Properties.RecordToggleKey.Value;
301                  if (baseKey == null)
302                  {
303                      return null;
304                  }
305  
306                  // XOR with Shift: if Shift is present, remove it; if absent, add it
307                  return new HotkeySettings(
308                      baseKey.Win,
309                      baseKey.Ctrl,
310                      baseKey.Alt,
311                      !baseKey.Shift,  // XOR with Shift
312                      baseKey.Code);
313              }
314          }
315  
316          public HotkeySettings RecordToggleKeyWindow
317          {
318              get
319              {
320                  var baseKey = _zoomItSettings.Properties.RecordToggleKey.Value;
321                  if (baseKey == null)
322                  {
323                      return null;
324                  }
325  
326                  // XOR with Alt: if Alt is present, remove it; if absent, add it
327                  return new HotkeySettings(
328                      baseKey.Win,
329                      baseKey.Ctrl,
330                      !baseKey.Alt,    // XOR with Alt
331                      baseKey.Shift,
332                      baseKey.Code);
333              }
334          }
335  
336          public HotkeySettings SnipToggleKey
337          {
338              get => _zoomItSettings.Properties.SnipToggleKey.Value;
339              set
340              {
341                  if (_zoomItSettings.Properties.SnipToggleKey.Value != value)
342                  {
343                      _zoomItSettings.Properties.SnipToggleKey.Value = value ?? ZoomItProperties.DefaultSnipToggleKey;
344                      OnPropertyChanged(nameof(SnipToggleKey));
345                      OnPropertyChanged(nameof(SnipToggleKeySave));
346                      NotifySettingsChanged();
347                  }
348              }
349          }
350  
351          public HotkeySettings SnipToggleKeySave
352          {
353              get
354              {
355                  var baseKey = _zoomItSettings.Properties.SnipToggleKey.Value;
356                  if (baseKey == null)
357                  {
358                      return null;
359                  }
360  
361                  return new HotkeySettings(
362                      baseKey.Win,
363                      baseKey.Ctrl,
364                      baseKey.Alt,
365                      !baseKey.Shift, // Toggle Shift: if Shift is present, remove it; if absent, add it
366                      baseKey.Code);
367              }
368          }
369  
370          public HotkeySettings BreakTimerKey
371          {
372              get => _zoomItSettings.Properties.BreakTimerKey.Value;
373              set
374              {
375                  if (_zoomItSettings.Properties.BreakTimerKey.Value != value)
376                  {
377                      _zoomItSettings.Properties.BreakTimerKey.Value = value ?? ZoomItProperties.DefaultBreakTimerKey;
378                      OnPropertyChanged(nameof(BreakTimerKey));
379                      NotifySettingsChanged();
380                  }
381              }
382          }
383  
384          public HotkeySettings DemoTypeToggleKey
385          {
386              get => _zoomItSettings.Properties.DemoTypeToggleKey.Value;
387              set
388              {
389                  if (_zoomItSettings.Properties.DemoTypeToggleKey.Value != value)
390                  {
391                      _zoomItSettings.Properties.DemoTypeToggleKey.Value = value ?? ZoomItProperties.DefaultDemoTypeToggleKey;
392                      OnPropertyChanged(nameof(DemoTypeToggleKey));
393                      OnPropertyChanged(nameof(DemoTypeToggleKeyReset));
394                      NotifySettingsChanged();
395                  }
396              }
397          }
398  
399          public HotkeySettings DemoTypeToggleKeyReset
400          {
401              get
402              {
403                  var baseKey = _zoomItSettings.Properties.DemoTypeToggleKey.Value;
404                  if (baseKey == null)
405                  {
406                      return null;
407                  }
408  
409                  // XOR with Shift: if Shift is present, remove it; if absent, add it
410                  return new HotkeySettings(
411                      baseKey.Win,
412                      baseKey.Ctrl,
413                      baseKey.Alt,
414                      !baseKey.Shift,  // XOR with Shift
415                      baseKey.Code);
416              }
417          }
418  
419          private LOGFONT _typeFont;
420  
421          public LOGFONT TypeFont
422          {
423              get
424              {
425                  var encodedFont = _zoomItSettings.Properties.Font.Value;
426                  byte[] decodedFont = Convert.FromBase64String(encodedFont);
427                  int size = Marshal.SizeOf(typeof(LOGFONT));
428                  if (size != decodedFont.Length)
429                  {
430                      throw new InvalidOperationException("Expected byte array from saved Settings doesn't match the LOGFONT structure size");
431                  }
432  
433                  // Allocate unmanaged memory to hold the byte array
434                  IntPtr ptr = Marshal.AllocHGlobal(size);
435                  try
436                  {
437                      // Copy the byte array into unmanaged memory
438                      Marshal.Copy(decodedFont, 0, ptr, size);
439  
440                      // Marshal the unmanaged memory back to a LOGFONT structure
441                      return (LOGFONT)Marshal.PtrToStructure(ptr, typeof(LOGFONT));
442                  }
443                  finally
444                  {
445                      // Free the unmanaged memory
446                      Marshal.FreeHGlobal(ptr);
447                  }
448              }
449  
450              set
451              {
452                  _typeFont = value;
453                  int size = Marshal.SizeOf(typeof(LOGFONT));
454                  byte[] bytes = new byte[size];
455  
456                  // Allocate unmanaged memory for the LOGFONT structure
457                  IntPtr ptr = Marshal.AllocHGlobal(size);
458                  try
459                  {
460                      // Marshal the LOGFONT structure to the unmanaged memory
461                      Marshal.StructureToPtr(value, ptr, false);
462  
463                      // Copy the unmanaged memory into the managed byte array
464                      Marshal.Copy(ptr, bytes, 0, size);
465                  }
466                  finally
467                  {
468                      // Free the unmanaged memory
469                      Marshal.FreeHGlobal(ptr);
470                  }
471  
472                  _zoomItSettings.Properties.Font.Value = Convert.ToBase64String(bytes);
473                  OnPropertyChanged(nameof(DemoSampleFontFamily));
474                  OnPropertyChanged(nameof(DemoSampleFontSize));
475                  OnPropertyChanged(nameof(DemoSampleFontWeight));
476                  OnPropertyChanged(nameof(DemoSampleFontStyle));
477                  OnPropertyChanged(nameof(DemoSampleTextDecoration));
478                  NotifySettingsChanged();
479              }
480          }
481  
482          public FontFamily DemoSampleFontFamily
483          {
484              get
485              {
486                  return new FontFamily(_typeFont.lfFaceName);
487              }
488          }
489  
490          public double DemoSampleFontSize
491          {
492              get
493              {
494                  return _typeFont.lfHeight <= 0 ? 16 : _typeFont.lfHeight; // 16 is always the height we expect?
495              }
496          }
497  
498          public global::Windows.UI.Text.FontWeight DemoSampleFontWeight
499          {
500              get
501              {
502                  if (_typeFont.lfWeight <= (int)FontWeight.FW_DONT_CARE)
503                  {
504                      return Microsoft.UI.Text.FontWeights.Normal;
505                  }
506                  else if (_typeFont.lfWeight <= (int)FontWeight.FW_THIN)
507                  {
508                      return Microsoft.UI.Text.FontWeights.Thin;
509                  }
510                  else if (_typeFont.lfWeight <= (int)FontWeight.FW_EXTRALIGHT)
511                  {
512                      return Microsoft.UI.Text.FontWeights.ExtraLight;
513                  }
514                  else if (_typeFont.lfWeight <= (int)FontWeight.FW_LIGHT)
515                  {
516                      return Microsoft.UI.Text.FontWeights.Light;
517                  }
518                  else if (_typeFont.lfWeight <= (int)FontWeight.FW_NORMAL)
519                  {
520                      return Microsoft.UI.Text.FontWeights.Normal;
521                  }
522                  else if (_typeFont.lfWeight <= (int)FontWeight.FW_MEDIUM)
523                  {
524                      return Microsoft.UI.Text.FontWeights.Medium;
525                  }
526                  else if (_typeFont.lfWeight <= (int)FontWeight.FW_SEMIBOLD)
527                  {
528                      return Microsoft.UI.Text.FontWeights.SemiBold;
529                  }
530                  else if (_typeFont.lfWeight <= (int)FontWeight.FW_BOLD)
531                  {
532                      return Microsoft.UI.Text.FontWeights.Bold;
533                  }
534                  else if (_typeFont.lfWeight <= (int)FontWeight.FW_EXTRABOLD)
535                  {
536                      return Microsoft.UI.Text.FontWeights.ExtraBold;
537                  }
538                  else
539                  {
540                      return Microsoft.UI.Text.FontWeights.Black; // FW_HEAVY
541                  }
542              }
543          }
544  
545          public global::Windows.UI.Text.FontStyle DemoSampleFontStyle
546          {
547              get => _typeFont.lfItalic != 0 ? global::Windows.UI.Text.FontStyle.Italic : global::Windows.UI.Text.FontStyle.Normal;
548          }
549  
550          public global::Windows.UI.Text.TextDecorations DemoSampleTextDecoration
551          {
552              get => _typeFont.lfUnderline != 0 ? global::Windows.UI.Text.TextDecorations.Underline : global::Windows.UI.Text.TextDecorations.None;
553          }
554  
555          public string DemoTypeFile
556          {
557              get => _zoomItSettings.Properties.DemoTypeFile.Value;
558              set
559              {
560                  if (_zoomItSettings.Properties.DemoTypeFile.Value != value)
561                  {
562                      _zoomItSettings.Properties.DemoTypeFile.Value = value;
563                      OnPropertyChanged(nameof(DemoTypeFile));
564                      NotifySettingsChanged();
565                  }
566              }
567          }
568  
569          public bool DemoTypeUserDrivenMode
570          {
571              get => _zoomItSettings.Properties.DemoTypeUserDrivenMode.Value;
572              set
573              {
574                  if (_zoomItSettings.Properties.DemoTypeUserDrivenMode.Value != value)
575                  {
576                      _zoomItSettings.Properties.DemoTypeUserDrivenMode.Value = value;
577                      OnPropertyChanged(nameof(DemoTypeUserDrivenMode));
578                      NotifySettingsChanged();
579                  }
580              }
581          }
582  
583          public int DemoTypeSpeedSlider
584          {
585              get => _zoomItSettings.Properties.DemoTypeSpeedSlider.Value;
586              set
587              {
588                  if (_zoomItSettings.Properties.DemoTypeSpeedSlider.Value != value)
589                  {
590                      _zoomItSettings.Properties.DemoTypeSpeedSlider.Value = value;
591                      OnPropertyChanged(nameof(DemoTypeSpeedSlider));
592                      NotifySettingsChanged();
593                  }
594              }
595          }
596  
597          public int BreakTimeout
598          {
599              get => _zoomItSettings.Properties.BreakTimeout.Value;
600              set
601              {
602                  if (_zoomItSettings.Properties.BreakTimeout.Value != value)
603                  {
604                      _zoomItSettings.Properties.BreakTimeout.Value = value;
605                      OnPropertyChanged(nameof(BreakTimeout));
606                      NotifySettingsChanged();
607                  }
608              }
609          }
610  
611          public bool BreakShowExpiredTime
612          {
613              get => _zoomItSettings.Properties.ShowExpiredTime.Value;
614              set
615              {
616                  if (_zoomItSettings.Properties.ShowExpiredTime.Value != value)
617                  {
618                      _zoomItSettings.Properties.ShowExpiredTime.Value = value;
619                      OnPropertyChanged(nameof(BreakShowExpiredTime));
620                      NotifySettingsChanged();
621                  }
622              }
623          }
624  
625          public bool BreakPlaySoundFile
626          {
627              get => _zoomItSettings.Properties.BreakPlaySoundFile.Value;
628              set
629              {
630                  if (_zoomItSettings.Properties.BreakPlaySoundFile.Value != value)
631                  {
632                      _zoomItSettings.Properties.BreakPlaySoundFile.Value = value;
633                      OnPropertyChanged(nameof(BreakPlaySoundFile));
634                      NotifySettingsChanged();
635                  }
636              }
637          }
638  
639          public string BreakSoundFile
640          {
641              get => _zoomItSettings.Properties.BreakSoundFile.Value;
642              set
643              {
644                  if (_zoomItSettings.Properties.BreakSoundFile.Value != value)
645                  {
646                      _zoomItSettings.Properties.BreakSoundFile.Value = value;
647                      OnPropertyChanged(nameof(BreakSoundFile));
648                      NotifySettingsChanged();
649                  }
650              }
651          }
652  
653          public double BreakTimerOpacity
654          {
655              get
656              {
657                  return Math.Clamp(_zoomItSettings.Properties.BreakOpacity.Value, 1, 100);
658              }
659  
660              set
661              {
662                  int intValue = (int)value;
663                  if (_zoomItSettings.Properties.BreakOpacity.Value != intValue)
664                  {
665                      _zoomItSettings.Properties.BreakOpacity.Value = intValue;
666                      OnPropertyChanged(nameof(BreakTimerOpacity));
667                      NotifySettingsChanged();
668                  }
669              }
670          }
671  
672          public int BreakTimerPosition
673          {
674              get => _zoomItSettings.Properties.BreakTimerPosition.Value;
675              set
676              {
677                  if (_zoomItSettings.Properties.BreakTimerPosition.Value != value)
678                  {
679                      _zoomItSettings.Properties.BreakTimerPosition.Value = value;
680                      OnPropertyChanged(nameof(BreakTimerPosition));
681                      NotifySettingsChanged();
682                  }
683              }
684          }
685  
686          public bool BreakShowBackgroundFile
687          {
688              get => _zoomItSettings.Properties.BreakShowBackgroundFile.Value;
689              set
690              {
691                  if (_zoomItSettings.Properties.BreakShowBackgroundFile.Value != value)
692                  {
693                      _zoomItSettings.Properties.BreakShowBackgroundFile.Value = value;
694                      OnPropertyChanged(nameof(BreakShowBackgroundFile));
695                      OnPropertyChanged(nameof(BreakBackgroundSelectionIndex));
696                      NotifySettingsChanged();
697                  }
698              }
699          }
700  
701          public bool BreakShowDesktop
702          {
703              get => _zoomItSettings.Properties.BreakShowDesktop.Value;
704              set
705              {
706                  if (_zoomItSettings.Properties.BreakShowDesktop.Value != value)
707                  {
708                      _zoomItSettings.Properties.BreakShowDesktop.Value = value;
709                      OnPropertyChanged(nameof(BreakShowDesktop));
710                      OnPropertyChanged(nameof(BreakBackgroundSelectionIndex));
711                      NotifySettingsChanged();
712                  }
713              }
714          }
715  
716          public int BreakBackgroundSelectionIndex
717          {
718              get
719              {
720                  if (!BreakShowBackgroundFile)
721                  {
722                      return 0;
723                  }
724  
725                  return BreakShowDesktop ? 1 : 2;
726              }
727  
728              set
729              {
730                  int clampedValue = Math.Clamp(value, 0, 2);
731                  switch (clampedValue)
732                  {
733                      case 0:
734                          BreakShowBackgroundFile = false;
735                          break;
736                      case 1:
737                          if (!BreakShowBackgroundFile)
738                          {
739                              BreakShowBackgroundFile = true;
740                          }
741  
742                          BreakShowDesktop = true;
743                          break;
744                      case 2:
745                          if (!BreakShowBackgroundFile)
746                          {
747                              BreakShowBackgroundFile = true;
748                          }
749  
750                          BreakShowDesktop = false;
751                          break;
752                      default:
753                          break;
754                  }
755              }
756          }
757  
758          public string BreakBackgroundFile
759          {
760              get => _zoomItSettings.Properties.BreakBackgroundFile.Value;
761              set
762              {
763                  if (_zoomItSettings.Properties.BreakBackgroundFile.Value != value)
764                  {
765                      _zoomItSettings.Properties.BreakBackgroundFile.Value = value;
766                      OnPropertyChanged(nameof(BreakBackgroundFile));
767                      NotifySettingsChanged();
768                  }
769              }
770          }
771  
772          public bool BreakBackgroundStretch
773          {
774              get => _zoomItSettings.Properties.BreakBackgroundStretch.Value;
775              set
776              {
777                  if (_zoomItSettings.Properties.BreakBackgroundStretch.Value != value)
778                  {
779                      _zoomItSettings.Properties.BreakBackgroundStretch.Value = value;
780                      OnPropertyChanged(nameof(BreakBackgroundStretch));
781                      NotifySettingsChanged();
782                  }
783              }
784          }
785  
786          public double RecordScaling
787          {
788              get
789              {
790                  return Math.Clamp(_zoomItSettings.Properties.RecordScaling.Value / 100.0, 0.1, 1.0);
791              }
792  
793              set
794              {
795                  int newValue = (int)(value * 100);
796                  if (_zoomItSettings.Properties.RecordScaling.Value != newValue)
797                  {
798                      _zoomItSettings.Properties.RecordScaling.Value = newValue;
799                      OnPropertyChanged(nameof(RecordScaling));
800                      NotifySettingsChanged();
801                  }
802              }
803          }
804  
805          public int RecordFormatIndex
806          {
807              get
808              {
809                  if (_zoomItSettings.Properties.RecordFormat.Value == FormatGif)
810                  {
811                      return 0;
812                  }
813  
814                  if (_zoomItSettings.Properties.RecordFormat.Value == FormatMp4)
815                  {
816                      return 1;
817                  }
818  
819                  return 0;
820              }
821  
822              set
823              {
824                  int format = 0;
825                  if (_zoomItSettings.Properties.RecordFormat.Value == FormatGif)
826                  {
827                      format = 0;
828                  }
829  
830                  if (_zoomItSettings.Properties.RecordFormat.Value == FormatMp4)
831                  {
832                      format = 1;
833                  }
834  
835                  if (format != value)
836                  {
837                      _zoomItSettings.Properties.RecordFormat.Value = value == 0 ? FormatGif : FormatMp4;
838                      OnPropertyChanged(nameof(RecordFormatIndex));
839                      NotifySettingsChanged();
840  
841                      // Reload settings to get the new format's scaling value
842                      var reloadedSettings = global::PowerToys.ZoomItSettingsInterop.ZoomItSettings.LoadSettingsJson();
843                      var reloaded = JsonSerializer.Deserialize<ZoomItSettings>(reloadedSettings, _serializerOptions);
844                      if (reloaded != null && reloaded.Properties != null)
845                      {
846                          _zoomItSettings.Properties.RecordScaling.Value = reloaded.Properties.RecordScaling.Value;
847                          OnPropertyChanged(nameof(RecordScaling));
848                      }
849                  }
850              }
851          }
852  
853          public bool RecordCaptureSystemAudio
854          {
855              get => _zoomItSettings.Properties.CaptureSystemAudio.Value;
856              set
857              {
858                  if (_zoomItSettings.Properties.CaptureSystemAudio.Value != value)
859                  {
860                      _zoomItSettings.Properties.CaptureSystemAudio.Value = value;
861                      OnPropertyChanged(nameof(RecordCaptureSystemAudio));
862                      NotifySettingsChanged();
863                  }
864              }
865          }
866  
867          public bool RecordCaptureAudio
868          {
869              get => _zoomItSettings.Properties.CaptureAudio.Value;
870              set
871              {
872                  if (_zoomItSettings.Properties.CaptureAudio.Value != value)
873                  {
874                      _zoomItSettings.Properties.CaptureAudio.Value = value;
875                      OnPropertyChanged(nameof(RecordCaptureAudio));
876                      NotifySettingsChanged();
877                  }
878              }
879          }
880  
881          public string RecordMicrophoneDeviceId
882          {
883              get => _zoomItSettings.Properties.MicrophoneDeviceId.Value;
884              set
885              {
886                  if (_zoomItSettings.Properties.MicrophoneDeviceId.Value != value)
887                  {
888                      _zoomItSettings.Properties.MicrophoneDeviceId.Value = value ?? string.Empty; // If we're trying to save a null, just default to empty string, which means default microphone.
889                      OnPropertyChanged(nameof(RecordMicrophoneDeviceId));
890                      NotifySettingsChanged();
891                  }
892              }
893          }
894  
895          private void NotifySettingsChanged()
896          {
897              global::PowerToys.ZoomItSettingsInterop.ZoomItSettings.SaveSettingsJson(
898                  JsonSerializer.Serialize(_zoomItSettings));
899              SendCustomAction("refresh_settings");
900          }
901  
902          private void SelectDemoTypeFileAction()
903          {
904              try
905              {
906                  ResourceLoader resourceLoader = ResourceLoaderInstance.ResourceLoader;
907                  string title = resourceLoader.GetString("ZoomIt_DemoType_File_Picker_Dialog_Title");
908                  string allFilesFilter = resourceLoader.GetString("FilePicker_AllFilesFilter");
909                  string pickedFile = PickFileDialog($"{allFilesFilter}\0*.*\0\0", title, null, 0);
910                  if (pickedFile != null)
911                  {
912                      DemoTypeFile = pickedFile;
913                  }
914              }
915              catch (Exception ex)
916              {
917                  Logger.LogError("Error picking Demo Type file.", ex);
918              }
919          }
920  
921          private void SelectBreakSoundFileAction()
922          {
923              try
924              {
925                  ResourceLoader resourceLoader = ResourceLoaderInstance.ResourceLoader;
926                  string title = resourceLoader.GetString("ZoomIt_Break_SoundFile_Picker_Dialog_Title");
927                  string soundFilesFilter = resourceLoader.GetString("FilePicker_ZoomIt_SoundsFilter");
928                  string allFilesFilter = resourceLoader.GetString("FilePicker_AllFilesFilter");
929                  string initialDirectory = Environment.ExpandEnvironmentVariables("%WINDIR%\\Media");
930                  string pickedFile = PickFileDialog($"{soundFilesFilter}\0*.wav\0{allFilesFilter}\0*.*\0\0", title, initialDirectory, 0);
931                  if (pickedFile != null)
932                  {
933                      BreakSoundFile = pickedFile;
934                  }
935              }
936              catch (Exception ex)
937              {
938                  Logger.LogError("Error picking Break Sound file.", ex);
939              }
940          }
941  
942          private void SelectBreakBackgroundFileAction()
943          {
944              try
945              {
946                  ResourceLoader resourceLoader = ResourceLoaderInstance.ResourceLoader;
947                  string title = resourceLoader.GetString("ZoomIt_Break_BackgroundFile_Picker_Dialog_Title");
948                  string bitmapFilesFilter = resourceLoader.GetString("FilePicker_ZoomIt_BitmapFilesFilter");
949                  string allPictureFilesFilter = resourceLoader.GetString("FilePicker_ZoomIt_AllPicturesFilter");
950                  string allFilesFilter = resourceLoader.GetString("FilePicker_AllFilesFilter");
951                  string initialDirectory = Environment.ExpandEnvironmentVariables("%USERPROFILE%\\Pictures");
952                  string pickedFile = PickFileDialog($"{bitmapFilesFilter} (*.bmp;*.dib)\0*.bmp;*.dib\0PNG (*.png)\0*.png\0JPEG (*.jpg;*.jpeg;*.jpe;*.jfif)\0*.jpg;*.jpeg;*.jpe;*.jfif\0GIF (*.gif)\0*.gif\0{allPictureFilesFilter}\0*.bmp;*.dib;*.png;*.jpg;*.jpeg;*.jpe;*.jfif;*.gif\0{allFilesFilter}\0*.*\0\0", title, initialDirectory, 5);
953                  if (pickedFile != null)
954                  {
955                      BreakBackgroundFile = pickedFile;
956                  }
957              }
958              catch (Exception ex)
959              {
960                  Logger.LogError("Error picking Break Background file.", ex);
961              }
962          }
963  
964          private void SelectTypeFontAction()
965          {
966              try
967              {
968                  LOGFONT result = PickFontDialog(_typeFont);
969                  if (result != null)
970                  {
971                      TypeFont = result;
972                  }
973              }
974              catch (Exception ex)
975              {
976                  Logger.LogError("Error picking Type font.", ex);
977              }
978          }
979  
980          public void RefreshEnabledState()
981          {
982              InitializeEnabledValue();
983              OnPropertyChanged(nameof(IsEnabled));
984          }
985  
986          private GpoRuleConfigured _enabledGpoRuleConfiguration;
987          private bool _enabledStateIsGPOConfigured;
988          private bool _isEnabled;
989      }
990  }