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 }