[New Module] Workspaces (#34324)

* spell checker

* Adding OOBE Projects page

* changed the default hotkey

* module interface

* rename projects editor

* bug report tool

* installer

* gpo

* exit event constant

* extend search for projects by search over the containing apps' names

* [Projects] fix grammatical issue #43 (1 app - many apps)

* [Projects] Editor: Main page: fix layout if there are many apps, launch button not disappearing on the right side

* dsc

* github

* pipeline

* guid prefix

* [Projects] fixing general settings gpo handling in runner + minor changes

* arm build fix

* Do not allow saving project if name or applist is empty. Also minor UI changes

* version

* editor version

* spellcheck

* editor dll signing

* update projects names to filter them out

* shortcut saving fix

* [Projects] Editor: brining the highlighted app's icon into the foreground. + minor UI fixes

* spell checker

* spellcheck

* [Projects] re-implementing icon size calculation to have similar sized icons for every app.

* [projects] Adding info message for cases: there are no projects or no results for the search

* [Projects] Adding Edit button to the popup. + minor changes

* [Projects] Making popup having rounded corners

* changed "no projects" text color and position

* remove opening the first proj

* fix placing windows of the same app in the project

* [Projects] bringing back the breadcrumb on the editor page. Make it clickable.

* [Projects] optimizing click handlers

* [Projects] Removing not selected apps on save

* moved on thread executor to common

* moved display utils

* added convert rect

* unsigned monitor number

* set awareness

* app placement

* [Projects] Re-implementing preview drawing - one common image

* [Projects] fix boundary calculation, use DPI aware values

* fix launching with command line args

* Fix ARM64 CI build

* launch packaged apps using names when possible

* spell-check

* update packaged apps path

* projects editor single instance

* [Projects] Add Select all checkbox, Delete selected button

* Add Checkbox for per monitor selection

* modifying highlight in preview

* spell checker

* logs

* exclude help windows

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/49

* Add intermediate step to project creation

* minor bugfix

* mutex fix

* modifying highlight for minimized apps

* Fixing bug: re-draw the preview on app deletion in the editor

* Adding helper class for getting the right bounds for screens

* spell checker

* spell checker

* Minor fixes in the capture dialog

* get dpi unaware screen bounds

* refactoring: added utils

* changed window filter

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/2

* clean up

* refactoring

* projects common lib

* localizable default project prefix

* launcher resources

* clean up

* change snapshot project saving

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/14

* changed project data

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/14

* changed project creation save-cancel handles

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/14

* spell-check

* Remove checkboxes, delete feature

* remove unused from the project

* get command line args in the snapshot

* minimized settings snap fix

* set window property after launching

* FZ: ignore projects launched windows

* Implementing major new features: remove button, position manipulation, arguments, admin, minimized, maximized

* modifying colors

* launcher project filters

* clean up

* Hide Admin checkbox

* hide WIP

* spell-check

* Revert "Hide Admin checkbox"

This reverts commit 3036df9d7f.

* get app elevated property

* Implementing Launch and Edit feature

* fixing: update of listed projects on the main page after hitting save in editor

* Fix for packaged app's icons

* fixing scroll speed issue

* change scroll speed to 15

* launch elevated apps

* minor fixes

* minor fix

* enhancing shortcut handling

* can-launch-elevated check

* projects module interface telemetry

* Implementing store of setting "order by".

* minor string correction

* moved projects data parsing

* telemetry

* add move apps checkbox

* notification about elevated apps

* restart unelevated

* move existing windows

* keep opened windows at the same positions

* handle powertoys settings

* use common theme

* fix corrupted data: project id and monitor id

* project launch on "launch and edit"

* clean up

* show screen numbers instead of monitor names

* launcher error messages

* fix default shortcut

* Adding launch button to projects settings, dashboard and flyout

* Adding new app which is launched when launching a project. It shows the status of the launch process

* spell checker

* Renaming Projects to App Layouts. Replacing only string values, not the variable names

* Re-ordering modules after Renaming Projects + spell checker

* setting window size according to the screen (making it bigger)

* commenting out feature "move apps if exist"

* spell checker

* Add ProjectsLauncherUI to signing

* opening apps in minimized state which are placed on a monitor, which is not found at the moment of launching

* consistent file name

* removed unused sln

* telemetry: create event

* WindowPosition comparison

* telemetry: edit event

* fix muted Launch as admin checkbox

* telemetry: delete event

* updated Edit telemetry event

* added invoke point to launcher args

* added utils

* parse invoke point

* replaced tuple with struct

* telemetry: launch event

* MonitorRect comparison

* resources

* rename: folders

* remove outdated

* rename: window property

* rename: files and folders

* rename: common data structures

* rename: telemetry namespace

* rename: workspaces data

* rename ProjectsLib -> WorkspacesLib

* rename: gpo

* rename: settings

* rename: launcher UI

* rename: other

* rename: pt run

* rename: fz

* rename: module interface

* rename: icon

* rename: snapshot tool

* rename: editor

* rename: common files

* rename: launcher

* rename: editor resources

* fix empty file crash

* rename: json

* rename: module interface

* fix custom actions build

* added launch editor event constant

* xaml formatting

* Add missing method defition to interop::Constants idl
Remove Any CPU config

* more .sln cleanup

* [Run][PowerToys] Fix Workspaces utility (#34336)

polished workspaces utility

* build fix - align CppWinRT version

* address PR comment: fix isdigit

* indentation

* address PR comment: rename function

* address PR comment: changed version for workspaces and revision

* added supported version definition

* addressPR comment: use BringToForeground

* address PR comments: updated projects

* address PR comment: uncomment gpo in settings

* address PR comment: rename oobe view

* update OOBE image with current module name

* moved AppUtils

* launching with AppUserModel.ID

* fixed module order in settings

* fix xaml formatting

* [Workspaces] Close launcher if there are failed launches. Plus adding new spinner gif

* fix topmost LauncherUI

* clean up

* UI closing

* BugReportTool - omit cmd arg data

* Delete icon on workspace removal

* Adding cancellation to launcher UI.

* reordered launching

* fix terminating UI

* Removing old shortcut on workspace renaming

* Sentence case labels

* get process path without waiting

* comment out unused

* remove unused argument

* logs

* New icon

* fix launch and edit for the new project

* fix launch and edit: save new project

* Update exe icons

---------

Co-authored-by: donlaci <laszlo@janeasystems.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: Stefan Markovic <stefan@janeasystems.com>
Co-authored-by: Davide Giacometti <davide.giacometti@outlook.it>
Co-authored-by: Niels Laute <niels.laute@live.nl>
This commit is contained in:
Seraphima Zykova
2024-08-23 09:28:13 +02:00
committed by GitHub
parent 2a8e211cfd
commit 579619952d
221 changed files with 12805 additions and 12 deletions

View File

@@ -0,0 +1,598 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;
using System.Windows;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Telemetry;
using WorkspacesEditor.Data;
using WorkspacesEditor.Models;
using WorkspacesEditor.Telemetry;
using WorkspacesEditor.Utils;
using static WorkspacesEditor.Data.WorkspacesData;
namespace WorkspacesEditor.ViewModels
{
public class MainViewModel : INotifyPropertyChanged, IDisposable
{
private WorkspacesEditorIO _workspacesEditorIO;
private ProjectEditor editPage;
private SnapshotWindow _snapshotWindow;
private List<OverlayWindow> _overlayWindows = new List<OverlayWindow>();
private Project editedProject;
private Project projectBeforeLaunch;
private string projectNameBeingEdited;
private MainWindow _mainWindow;
private Timer lastUpdatedTimer;
private WorkspacesSettings settings;
public ObservableCollection<Project> Workspaces { get; set; } = new ObservableCollection<Project>();
public IEnumerable<Project> WorkspacesView
{
get
{
IEnumerable<Project> workspaces = GetFilteredWorkspaces();
IsWorkspacesViewEmpty = !(workspaces != null && workspaces.Any());
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsWorkspacesViewEmpty)));
if (IsWorkspacesViewEmpty)
{
if (Workspaces != null && Workspaces.Any())
{
EmptyWorkspacesViewMessage = Properties.Resources.NoWorkspacesMatch;
}
else
{
EmptyWorkspacesViewMessage = Properties.Resources.No_Workspaces_Message;
}
OnPropertyChanged(new PropertyChangedEventArgs(nameof(EmptyWorkspacesViewMessage)));
return Enumerable.Empty<Project>();
}
OrderBy orderBy = (OrderBy)_orderByIndex;
if (orderBy == OrderBy.LastViewed)
{
return workspaces.OrderByDescending(x => x.LastLaunchedTime);
}
else if (orderBy == OrderBy.Created)
{
return workspaces.OrderByDescending(x => x.CreationTime);
}
else
{
return workspaces.OrderBy(x => x.Name);
}
}
}
public bool IsWorkspacesViewEmpty { get; set; }
public string EmptyWorkspacesViewMessage { get; set; }
// return those workspaces where the project name or any of the selected apps' name contains the search term
private IEnumerable<Project> GetFilteredWorkspaces()
{
if (string.IsNullOrEmpty(_searchTerm))
{
return Workspaces;
}
return Workspaces.Where(x =>
{
if (x.Name.Contains(_searchTerm, StringComparison.InvariantCultureIgnoreCase))
{
return true;
}
if (x.Applications == null)
{
return false;
}
return x.Applications.Any(app => app.AppName.Contains(_searchTerm, StringComparison.InvariantCultureIgnoreCase));
});
}
private string _searchTerm;
public string SearchTerm
{
get => _searchTerm;
set
{
_searchTerm = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(WorkspacesView)));
}
}
private int _orderByIndex;
public int OrderByIndex
{
get => _orderByIndex;
set
{
_orderByIndex = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(WorkspacesView)));
settings.Properties.SortBy = (WorkspacesProperties.SortByProperty)value;
settings.Save(new SettingsUtils());
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
public MainViewModel(WorkspacesEditorIO workspacesEditorIO)
{
settings = Utils.Settings.ReadSettings();
_orderByIndex = (int)settings.Properties.SortBy;
_workspacesEditorIO = workspacesEditorIO;
lastUpdatedTimer = new System.Timers.Timer();
lastUpdatedTimer.Interval = 1000;
lastUpdatedTimer.Elapsed += LastUpdatedTimerElapsed;
lastUpdatedTimer.Start();
}
public void Initialize()
{
foreach (Project project in Workspaces)
{
project.Initialize(App.ThemeManager.GetCurrentTheme());
}
OnPropertyChanged(new PropertyChangedEventArgs(nameof(WorkspacesView)));
}
public void SetEditedProject(Project editedProject)
{
this.editedProject = editedProject;
}
public void SaveProject(Project projectToSave)
{
SendEditTelemetryEvent(projectToSave, editedProject);
if (editedProject.Name != projectToSave.Name)
{
RemoveShortcut(editedProject);
}
editedProject.Name = projectToSave.Name;
editedProject.IsShortcutNeeded = projectToSave.IsShortcutNeeded;
editedProject.MoveExistingWindows = projectToSave.MoveExistingWindows;
editedProject.PreviewIcons = projectToSave.PreviewIcons;
editedProject.PreviewImage = projectToSave.PreviewImage;
editedProject.Applications = projectToSave.Applications.Where(x => x.IsIncluded).ToList();
editedProject.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("AppsCountString"));
editedProject.Initialize(App.ThemeManager.GetCurrentTheme());
_workspacesEditorIO.SerializeWorkspaces(Workspaces.ToList());
ApplyShortcut(editedProject);
}
private void ApplyShortcut(Project project)
{
string basePath = AppDomain.CurrentDomain.BaseDirectory;
string shortcutAddress = Path.Combine(FolderUtils.Desktop(), project.Name + ".lnk");
string shortcutIconFilename = Path.Combine(FolderUtils.Temp(), project.Id + ".ico");
if (!project.IsShortcutNeeded)
{
if (File.Exists(shortcutIconFilename))
{
File.Delete(shortcutIconFilename);
}
if (File.Exists(shortcutAddress))
{
File.Delete(shortcutAddress);
}
return;
}
Bitmap icon = WorkspacesIcon.DrawIcon(WorkspacesIcon.IconTextFromProjectName(project.Name), App.ThemeManager.GetCurrentTheme());
WorkspacesIcon.SaveIcon(icon, shortcutIconFilename);
try
{
// Workaround to be able to create a shortcut with unicode filename
File.WriteAllBytes(shortcutAddress, Array.Empty<byte>());
// Create a ShellLinkObject that references the .lnk file
Shell32.Shell shell = new Shell32.Shell();
Shell32.Folder dir = shell.NameSpace(FolderUtils.Desktop());
Shell32.FolderItem folderItem = dir.Items().Item($"{project.Name}.lnk");
Shell32.ShellLinkObject link = (Shell32.ShellLinkObject)folderItem.GetLink;
// Set the .lnk file properties
link.Description = $"Project Launcher {project.Id}";
link.Path = Path.Combine(basePath, "PowerToys.WorkspacesLauncher.exe");
link.Arguments = $"{project.Id.ToString()} {(int)InvokePoint.Shortcut}";
link.WorkingDirectory = basePath;
link.SetIconLocation(shortcutIconFilename, 0);
link.Save(shortcutAddress);
}
catch (Exception ex)
{
Logger.LogError($"Shortcut creation error: {ex.Message}");
}
}
public void SaveProjectName(Project project)
{
projectNameBeingEdited = project.Name;
}
public void CancelProjectName(Project project)
{
project.Name = projectNameBeingEdited;
}
public async void SnapWorkspace()
{
CancelSnapshot();
await Task.Run(() => RunSnapshotTool());
Project project = _workspacesEditorIO.ParseTempProject();
if (project != null)
{
if (editedProject != null)
{
project.UpdateAfterLaunchAndEdit(projectBeforeLaunch);
project.EditorWindowTitle = Properties.Resources.EditWorkspace;
editPage.DataContext = project;
CheckShortcutPresence(project);
project.Initialize(App.ThemeManager.GetCurrentTheme());
}
else
{
EditProject(project, true);
}
}
}
internal void RevertLaunch()
{
CheckShortcutPresence(projectBeforeLaunch);
editPage.DataContext = projectBeforeLaunch;
projectBeforeLaunch.Initialize(App.ThemeManager.GetCurrentTheme());
}
public void EditProject(Project selectedProject, bool isNewlyCreated = false)
{
editPage = new ProjectEditor(this);
SetEditedProject(selectedProject);
if (!isNewlyCreated)
{
selectedProject = new Project(selectedProject);
}
if (isNewlyCreated)
{
// generate a default name for the new project
string defaultNamePrefix = Properties.Resources.DefaultWorkspaceNamePrefix;
int nextProjectIndex = 0;
foreach (var proj in Workspaces)
{
if (proj.Name.StartsWith(defaultNamePrefix, StringComparison.CurrentCulture))
{
try
{
int index = int.Parse(proj.Name[(defaultNamePrefix.Length + 1)..], CultureInfo.CurrentCulture);
if (nextProjectIndex < index)
{
nextProjectIndex = index;
}
}
catch (Exception)
{
}
}
}
selectedProject.Name = defaultNamePrefix + " " + (nextProjectIndex + 1).ToString(CultureInfo.CurrentCulture);
}
selectedProject.EditorWindowTitle = isNewlyCreated ? Properties.Resources.CreateWorkspace : Properties.Resources.EditWorkspace;
selectedProject.Initialize(App.ThemeManager.GetCurrentTheme());
CheckShortcutPresence(selectedProject);
editPage.DataContext = selectedProject;
_mainWindow.ShowPage(editPage);
lastUpdatedTimer.Stop();
}
private void CheckShortcutPresence(Project project)
{
string basePath = AppDomain.CurrentDomain.BaseDirectory;
string shortcutAddress = Path.Combine(FolderUtils.Desktop(), project.Name + ".lnk");
project.IsShortcutNeeded = File.Exists(shortcutAddress);
}
public void AddNewProject(Project project)
{
project.Applications.RemoveAll(app => !app.IsIncluded);
project.Initialize(App.ThemeManager.GetCurrentTheme());
Workspaces.Add(project);
_workspacesEditorIO.SerializeWorkspaces(Workspaces.ToList());
TempProjectData.DeleteTempFile();
OnPropertyChanged(new PropertyChangedEventArgs(nameof(WorkspacesView)));
ApplyShortcut(project);
SendCreateTelemetryEvent(project);
}
public void DeleteProject(Project selectedProject)
{
Workspaces.Remove(selectedProject);
_workspacesEditorIO.SerializeWorkspaces(Workspaces.ToList());
RemoveShortcut(selectedProject);
OnPropertyChanged(new PropertyChangedEventArgs(nameof(WorkspacesView)));
SendDeleteTelemetryEvent();
}
private void RemoveShortcut(Project selectedProject)
{
string shortcutAddress = Path.Combine(FolderUtils.Desktop(), selectedProject.Name + ".lnk");
string shortcutIconFilename = Path.Combine(FolderUtils.Temp(), selectedProject.Id + ".ico");
if (File.Exists(shortcutIconFilename))
{
File.Delete(shortcutIconFilename);
}
if (File.Exists(shortcutAddress))
{
File.Delete(shortcutAddress);
}
}
public void SetMainWindow(MainWindow mainWindow)
{
_mainWindow = mainWindow;
}
public void SwitchToMainView()
{
_mainWindow.SwitchToMainView();
SearchTerm = string.Empty;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(SearchTerm)));
lastUpdatedTimer.Start();
editedProject = null;
}
public void LaunchProject(string projectId)
{
if (!Workspaces.Where(x => x.Id == projectId).Any())
{
Logger.LogWarning($"Workspace to launch not found. Id: {projectId}");
return;
}
LaunchProject(Workspaces.Where(x => x.Id == projectId).First(), true);
}
public async void LaunchProject(Project project, bool exitAfterLaunch = false)
{
await Task.Run(() => RunLauncher(project.Id, InvokePoint.EditorButton));
if (_workspacesEditorIO.ParseWorkspaces(this).Result == true)
{
OnPropertyChanged(new PropertyChangedEventArgs(nameof(WorkspacesView)));
}
if (exitAfterLaunch)
{
Logger.LogInfo($"Launched the Workspace {project.Name}. Exiting.");
Environment.Exit(0);
}
}
private void LastUpdatedTimerElapsed(object sender, ElapsedEventArgs e)
{
if (Workspaces == null)
{
return;
}
foreach (Project project in Workspaces)
{
project.OnPropertyChanged(new PropertyChangedEventArgs("LastLaunched"));
}
}
private void RunSnapshotTool(string filename = null)
{
Process process = new Process();
process.StartInfo = new ProcessStartInfo(@".\PowerToys.WorkspacesSnapshotTool.exe");
process.StartInfo.CreateNoWindow = true;
if (!string.IsNullOrEmpty(filename))
{
process.StartInfo.Arguments = filename;
}
try
{
process.Start();
process.WaitForExit();
}
catch (Exception ex)
{
MessageBox.Show($"An error occurred: {ex.Message}");
}
}
private void RunLauncher(string projectId, InvokePoint invokePoint)
{
Process process = new Process();
process.StartInfo = new ProcessStartInfo(@".\PowerToys.WorkspacesLauncher.exe", $"{projectId} {(int)invokePoint}");
process.StartInfo.CreateNoWindow = true;
try
{
process.Start();
process.WaitForExit();
}
catch (Exception ex)
{
MessageBox.Show($"An error occurred: {ex.Message}");
}
}
public void Dispose()
{
GC.SuppressFinalize(this);
}
internal void CloseAllPopups()
{
foreach (Project project in Workspaces)
{
project.IsPopupVisible = false;
}
}
internal void EnterSnapshotMode(bool isExistingProjectLaunched)
{
_mainWindow.WindowState = System.Windows.WindowState.Minimized;
_overlayWindows.Clear();
foreach (var screen in MonitorHelper.GetDpiUnawareScreens())
{
var bounds = screen.Bounds;
OverlayWindow overlayWindow = new OverlayWindow();
overlayWindow.Top = bounds.Top;
overlayWindow.Left = bounds.Left;
overlayWindow.Width = bounds.Width;
overlayWindow.Height = bounds.Height;
overlayWindow.ShowActivated = true;
overlayWindow.Topmost = true;
overlayWindow.Show();
_overlayWindows.Add(overlayWindow);
}
_snapshotWindow = new SnapshotWindow(this);
_snapshotWindow.ShowActivated = true;
_snapshotWindow.Topmost = true;
_snapshotWindow.Show();
}
internal void CancelSnapshot()
{
foreach (OverlayWindow overlayWindow in _overlayWindows)
{
overlayWindow.Close();
}
_mainWindow.WindowState = System.Windows.WindowState.Normal;
}
internal async void LaunchAndEdit(Project project)
{
await Task.Run(() => RunLauncher(project.Id, InvokePoint.LaunchAndEdit));
projectBeforeLaunch = new Project(project);
EnterSnapshotMode(true);
}
private void SendCreateTelemetryEvent(Project project)
{
var telemetryEvent = new CreateEvent();
telemetryEvent.Successful = true;
telemetryEvent.NumScreens = project.Monitors.Count;
telemetryEvent.AppCount = project.Applications.Count;
telemetryEvent.CliCount = project.Applications.FindAll(app => app.CommandLineArguments.Length > 0).Count;
telemetryEvent.ShortcutCreated = project.IsShortcutNeeded;
telemetryEvent.AdminCount = project.Applications.FindAll(app => app.IsElevated).Count;
PowerToysTelemetry.Log.WriteEvent(telemetryEvent);
}
private void SendEditTelemetryEvent(Project updatedProject, Project prevProject)
{
int appsRemovedCount = updatedProject.Applications.FindAll(val => !val.IsIncluded).Count;
foreach (var app in prevProject.Applications)
{
var updatedApp = updatedProject.Applications.Find(val => app.AppName == val.AppName && app.Position == val.Position);
if (updatedApp == null)
{
appsRemovedCount++;
}
}
int appsAddedCount = 0;
int cliAdded = 0, cliRemoved = 0;
int adminAdded = 0, adminRemoved = 0;
foreach (var app in updatedProject.Applications)
{
var prevApp = prevProject.Applications.Find(val => app.AppName == val.AppName && app.Position == val.Position);
if (prevApp == null)
{
if (app.IsIncluded)
{
appsAddedCount++;
}
continue;
}
if (app.CommandLineArguments.Length > 0 && prevApp.CommandLineArguments.Length == 0)
{
cliAdded++;
}
if (prevApp.CommandLineArguments.Length > 0 && app.CommandLineArguments.Length == 0)
{
cliRemoved++;
}
if (app.IsElevated && !prevApp.IsElevated)
{
adminAdded++;
}
if (!app.IsElevated && prevApp.IsElevated)
{
adminRemoved++;
}
}
var telemetryEvent = new EditEvent();
telemetryEvent.Successful = true;
telemetryEvent.ScreenCountDelta = updatedProject.Monitors.Count - prevProject.Monitors.Count;
telemetryEvent.AppsAdded = appsAddedCount;
telemetryEvent.AppsRemoved = appsRemovedCount;
telemetryEvent.CliAdded = cliAdded;
telemetryEvent.CliRemoved = cliRemoved;
telemetryEvent.AdminAdded = adminAdded;
telemetryEvent.AdminRemoved = adminRemoved;
telemetryEvent.PixelAdjustmentsUsed = updatedProject.IsPositionChangedManually;
PowerToysTelemetry.Log.WriteEvent(telemetryEvent);
}
private void SendDeleteTelemetryEvent()
{
var telemetryEvent = new EditEvent();
telemetryEvent.Successful = true;
PowerToysTelemetry.Log.WriteEvent(telemetryEvent);
}
}
}