From dae91d2874f27fad759e304874a56257bed57401 Mon Sep 17 00:00:00 2001 From: donlaci Date: Thu, 11 Jul 2024 12:45:19 +0200 Subject: [PATCH] Implementing major new features: remove button, position manipulation, arguments, admin, minimized, maximized --- .../ProjectsEditor/Controls/ResetIsEnabled.cs | 22 ++ .../ProjectsEditor/Data/ProjectData.cs | 2 + .../ProjectsEditor/Models/Application.cs | 149 ++++++++- .../Projects/ProjectsEditor/Models/Project.cs | 15 +- .../ProjectsEditor/ProjectEditorPage.xaml | 297 +++++++++++++----- .../ProjectsEditor/ProjectEditorPage.xaml.cs | 98 +++++- .../Properties/Resources.Designer.cs | 99 ++++++ .../ProjectsEditor/Properties/Resources.resx | 36 +++ .../ProjectsEditor/Utils/DrawHelper.cs | 43 ++- .../ProjectsEditor/Utils/ProjectsEditorIO.cs | 11 +- .../ViewModels/MainViewModel.cs | 7 +- 11 files changed, 675 insertions(+), 104 deletions(-) create mode 100644 src/modules/Projects/ProjectsEditor/Controls/ResetIsEnabled.cs diff --git a/src/modules/Projects/ProjectsEditor/Controls/ResetIsEnabled.cs b/src/modules/Projects/ProjectsEditor/Controls/ResetIsEnabled.cs new file mode 100644 index 0000000000..3991422506 --- /dev/null +++ b/src/modules/Projects/ProjectsEditor/Controls/ResetIsEnabled.cs @@ -0,0 +1,22 @@ +// 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.Windows; +using System.Windows.Controls; + +namespace ProjectsEditor.Controls +{ + public class ResetIsEnabled : ContentControl + { + static ResetIsEnabled() + { + IsEnabledProperty.OverrideMetadata( + typeof(ResetIsEnabled), + new UIPropertyMetadata( + defaultValue: true, + propertyChangedCallback: (_, __) => { }, + coerceValueCallback: (_, x) => x)); + } + } +} diff --git a/src/modules/Projects/ProjectsEditor/Data/ProjectData.cs b/src/modules/Projects/ProjectsEditor/Data/ProjectData.cs index e1c52ffdd0..41a3ab6a2c 100644 --- a/src/modules/Projects/ProjectsEditor/Data/ProjectData.cs +++ b/src/modules/Projects/ProjectsEditor/Data/ProjectData.cs @@ -33,6 +33,8 @@ namespace ProjectsEditor.Data public string CommandLineArguments { get; set; } + public bool LaunchesAsAdmin { get; set; } + public bool Minimized { get; set; } public bool Maximized { get; set; } diff --git a/src/modules/Projects/ProjectsEditor/Models/Application.cs b/src/modules/Projects/ProjectsEditor/Models/Application.cs index a49e9ce270..5733c6938b 100644 --- a/src/modules/Projects/ProjectsEditor/Models/Application.cs +++ b/src/modules/Projects/ProjectsEditor/Models/Application.cs @@ -22,6 +22,8 @@ namespace ProjectsEditor.Models { public class Application : INotifyPropertyChanged, IDisposable { + private bool _isInitialized; + public event PropertyChangedEventHandler PropertyChanged; public Project Parent { get; set; } @@ -47,9 +49,80 @@ namespace ProjectsEditor.Models public string CommandLineArguments { get; set; } - public bool Minimized { get; set; } + private bool _launchesAsAdmin; - public bool Maximized { get; set; } + public bool LaunchesAsAdmin + { + get => _launchesAsAdmin; + set + { + _launchesAsAdmin = value; + OnPropertyChanged(new PropertyChangedEventArgs(nameof(AppMainParams))); + } + } + + internal void SwitchDeletion() + { + IsIncluded = !IsIncluded; + RedrawPreviewImage(); + } + + private void RedrawPreviewImage() + { + if (_isInitialized) + { + Parent.Initialize(); + } + } + + private bool _minimized; + + public bool Minimized + { + get => _minimized; + set + { + _minimized = value; + OnPropertyChanged(new PropertyChangedEventArgs(nameof(Minimized))); + OnPropertyChanged(new PropertyChangedEventArgs(nameof(EditPositionEnabled))); + RedrawPreviewImage(); + } + } + + private bool _maximized; + + public bool Maximized + { + get => _maximized; + set + { + _maximized = value; + OnPropertyChanged(new PropertyChangedEventArgs(nameof(Maximized))); + OnPropertyChanged(new PropertyChangedEventArgs(nameof(EditPositionEnabled))); + RedrawPreviewImage(); + } + } + + public bool EditPositionEnabled { get => !Minimized && !Maximized; } + + private string _appMainParams; + + public string AppMainParams + { + get + { + _appMainParams = _launchesAsAdmin ? Properties.Resources.Admin : string.Empty; + if (!string.IsNullOrWhiteSpace(CommandLineArguments)) + { + _appMainParams += (_appMainParams == string.Empty ? string.Empty : " | ") + Properties.Resources.Args + ": " + CommandLineArguments; + } + + OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsAppMainParamVisible))); + return _appMainParams; + } + } + + public bool IsAppMainParamVisible { get => !string.IsNullOrWhiteSpace(_appMainParams); } private bool _isNotFound; @@ -82,7 +155,7 @@ namespace ProjectsEditor.Models { get { - return RepeatIndex == 0 ? string.Empty : RepeatIndex.ToString(CultureInfo.InvariantCulture); + return RepeatIndex <= 1 ? string.Empty : RepeatIndex.ToString(CultureInfo.InvariantCulture); } } @@ -201,7 +274,17 @@ namespace ProjectsEditor.Models } } - public WindowPosition Position { get; set; } + private WindowPosition _position; + + public WindowPosition Position + { + get => _position; + set + { + _position = value; + _scaledPosition = null; + } + } private WindowPosition? _scaledPosition; @@ -247,6 +330,11 @@ namespace ProjectsEditor.Models PropertyChanged?.Invoke(this, e); } + public void InitializationFinished() + { + _isInitialized = true; + } + private bool? _isPackagedApp; public string PackagedId { get; set; } @@ -287,9 +375,62 @@ namespace ProjectsEditor.Models } } + private bool _isExpanded; + + public bool IsExpanded + { + get => _isExpanded; + set + { + if (_isExpanded != value) + { + _isExpanded = value; + OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsExpanded))); + } + } + } + + public string DeleteButtonContent { get => _isIncluded ? Properties.Resources.Delete : Properties.Resources.AddBack; } + + private bool _isIncluded = true; + + public bool IsIncluded + { + get => _isIncluded; + set + { + if (_isIncluded != value) + { + _isIncluded = value; + OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsIncluded))); + OnPropertyChanged(new PropertyChangedEventArgs(nameof(DeleteButtonContent))); + if (!_isIncluded) + { + IsExpanded = false; + } + } + } + } + public void Dispose() { GC.SuppressFinalize(this); } + + internal void CommandLineTextChanged(string newCommandLineValue) + { + CommandLineArguments = newCommandLineValue; + OnPropertyChanged(new PropertyChangedEventArgs(nameof(AppMainParams))); + } + + internal void MaximizedChecked() + { + Minimized = false; + } + + internal void MinimizedChecked() + { + Maximized = false; + } } } diff --git a/src/modules/Projects/ProjectsEditor/Models/Project.cs b/src/modules/Projects/ProjectsEditor/Models/Project.cs index 21aaae81ba..784367c03f 100644 --- a/src/modules/Projects/ProjectsEditor/Models/Project.cs +++ b/src/modules/Projects/ProjectsEditor/Models/Project.cs @@ -209,20 +209,23 @@ namespace ProjectsEditor.Models Applications = new List(); foreach (var item in selectedProject.Applications) { - Applications.Add(new Application() + Application newApp = new Application() { AppName = item.AppName, AppPath = item.AppPath, AppTitle = item.AppTitle, CommandLineArguments = item.CommandLineArguments, PackageFullName = item.PackageFullName, + LaunchesAsAdmin = item.LaunchesAsAdmin, Minimized = item.Minimized, Maximized = item.Maximized, MonitorNumber = item.MonitorNumber, IsNotFound = item.IsNotFound, Position = new Application.WindowPosition() { X = item.Position.X, Y = item.Position.Y, Height = item.Position.Height, Width = item.Position.Width }, Parent = this, - }); + }; + newApp.InitializationFinished(); + Applications.Add(newApp); } } @@ -304,5 +307,13 @@ namespace ProjectsEditor.Models return new Rectangle((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY)); } + + internal void CloseExpanders() + { + foreach (Application app in Applications) + { + app.IsExpanded = false; + } + } } } diff --git a/src/modules/Projects/ProjectsEditor/ProjectEditorPage.xaml b/src/modules/Projects/ProjectsEditor/ProjectEditorPage.xaml index 42ff72faf9..6c7fd135cc 100644 --- a/src/modules/Projects/ProjectsEditor/ProjectEditorPage.xaml +++ b/src/modules/Projects/ProjectsEditor/ProjectEditorPage.xaml @@ -6,12 +6,22 @@ xmlns:props="clr-namespace:ProjectsEditor.Properties" xmlns:local="clr-namespace:ProjectsEditor" xmlns:models="clr-namespace:ProjectsEditor.Models" + xmlns:controls="clr-namespace:ProjectsEditor.Controls" mc:Ignorable="d" Title="Project Editor" Background="{DynamicResource PrimaryBackgroundBrush}"> + + @@ -29,81 +39,218 @@ - - - - - - - - - - - - - - - - - - - - + MouseLeave="AppBorder_MouseLeave" + Margin="1"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /// Looks up a localized string similar to Add Back. + /// + public static string AddBack { + get { + return ResourceManager.GetString("AddBack", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Admin. + /// + public static string Admin { + get { + return ResourceManager.GetString("Admin", resourceCulture); + } + } + /// /// Looks up a localized string similar to app. /// @@ -105,6 +123,15 @@ namespace ProjectsEditor.Properties { } } + /// + /// Looks up a localized string similar to Args. + /// + public static string Args { + get { + return ResourceManager.GetString("Args", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cancel. /// @@ -114,6 +141,15 @@ namespace ProjectsEditor.Properties { } } + /// + /// Looks up a localized string similar to CLI arguments. + /// + public static string CliArguments { + get { + return ResourceManager.GetString("CliArguments", resourceCulture); + } + } + /// /// Looks up a localized string similar to Created. /// @@ -222,6 +258,15 @@ namespace ProjectsEditor.Properties { } } + /// + /// Looks up a localized string similar to Height. + /// + public static string Height { + get { + return ResourceManager.GetString("Height", resourceCulture); + } + } + /// /// Looks up a localized string similar to hours ago. /// @@ -258,6 +303,24 @@ namespace ProjectsEditor.Properties { } } + /// + /// Looks up a localized string similar to Launch as Admin. + /// + public static string LaunchAsAdmin { + get { + return ResourceManager.GetString("LaunchAsAdmin", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Left. + /// + public static string Left { + get { + return ResourceManager.GetString("Left", resourceCulture); + } + } + /// /// Looks up a localized string similar to Projects demo app. /// @@ -267,6 +330,24 @@ namespace ProjectsEditor.Properties { } } + /// + /// Looks up a localized string similar to Maximized. + /// + public static string Maximized { + get { + return ResourceManager.GetString("Maximized", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Minimized. + /// + public static string Minimized { + get { + return ResourceManager.GetString("Minimized", resourceCulture); + } + } + /// /// Looks up a localized string similar to Minimized Apps. /// @@ -528,6 +609,24 @@ namespace ProjectsEditor.Properties { } } + /// + /// Looks up a localized string similar to Top. + /// + public static string Top { + get { + return ResourceManager.GetString("Top", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Width. + /// + public static string Width { + get { + return ResourceManager.GetString("Width", resourceCulture); + } + } + /// /// Looks up a localized string similar to Write arguments here. /// diff --git a/src/modules/Projects/ProjectsEditor/Properties/Resources.resx b/src/modules/Projects/ProjectsEditor/Properties/Resources.resx index 60d86314ff..1275c455ff 100644 --- a/src/modules/Projects/ProjectsEditor/Properties/Resources.resx +++ b/src/modules/Projects/ProjectsEditor/Properties/Resources.resx @@ -117,6 +117,12 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Add Back + + + Admin + app @@ -132,9 +138,16 @@ Are you sure you want to delete this project? + + Args + Arguments + Cancel + + CLI arguments + Created @@ -171,6 +184,9 @@ Error parsing projects data. + + Height + hours ago @@ -180,12 +196,25 @@ Launch + + Launch as Admin + Launch args + + Left + the left x coordinate + Projects demo app + + Maximized + + + Minimized + Minimized Apps @@ -273,6 +302,13 @@ Capture + + Top + the top y coordinate + + + Width + Write arguments here diff --git a/src/modules/Projects/ProjectsEditor/Utils/DrawHelper.cs b/src/modules/Projects/ProjectsEditor/Utils/DrawHelper.cs index e296101653..aed7c9d3cf 100644 --- a/src/modules/Projects/ProjectsEditor/Utils/DrawHelper.cs +++ b/src/modules/Projects/ProjectsEditor/Utils/DrawHelper.cs @@ -14,21 +14,24 @@ using System.Linq; using System.Windows.Media.Imaging; using ManagedCommon; using ProjectsEditor.Models; +using static System.Windows.Forms.VisualStyles.VisualStyleElement.TextBox; namespace ProjectsEditor.Utils { public class DrawHelper { private static Font font = new("Tahoma", 24); + private static double scale = 0.1; + private static double gapWidth; + private static double gapHeight; public static BitmapImage DrawPreview(Project project, Rectangle bounds) { List horizontalGaps = new List(); List verticalGaps = new List(); - double gapWidth = bounds.Width * 0.01; - double gapHeight = bounds.Height * 0.01; + gapWidth = bounds.Width * 0.01; + gapHeight = bounds.Height * 0.01; - double scale = 0.1; int Scaled(double value) { return (int)(value * scale); @@ -46,9 +49,25 @@ namespace ProjectsEditor.Utils return Scaled(posY - bounds.Top + gapTransform); } + Rectangle GetAppRect(Application app) + { + if (app.Maximized) + { + Project project = app.Parent; + var monitor = project.Monitors.Where(x => x.MonitorNumber == app.MonitorNumber).FirstOrDefault(); + return new Rectangle(TransformX(monitor.MonitorDpiAwareBounds.Left), TransformY(monitor.MonitorDpiAwareBounds.Top), Scaled(monitor.MonitorDpiAwareBounds.Width), Scaled(monitor.MonitorDpiAwareBounds.Height)); + } + else + { + return new Rectangle(TransformX(app.ScaledPosition.X), TransformY(app.ScaledPosition.Y), Scaled(app.ScaledPosition.Width), Scaled(app.ScaledPosition.Height)); + } + } + Dictionary repeatCounter = new Dictionary(); - foreach (Application app in project.Applications) + var appsIncluded = project.Applications.Where(x => x.IsIncluded); + + foreach (Application app in appsIncluded) { if (repeatCounter.TryGetValue(app.AppPath, out int value)) { @@ -62,13 +81,7 @@ namespace ProjectsEditor.Utils app.RepeatIndex = repeatCounter[app.AppPath]; } - // remove those repeatIndexes, which are single 1-es (no repetitions) by setting them to 0 - foreach (Application app in project.Applications.Where(x => repeatCounter[x.AppPath] == 1)) - { - app.RepeatIndex = 0; - } - - foreach (Application app in project.Applications) + foreach (Application app in project.Applications.Where(x => !x.IsIncluded)) { app.RepeatIndex = 0; } @@ -112,18 +125,18 @@ namespace ProjectsEditor.Utils g.FillRectangle(monitorBrush, new Rectangle(TransformX(monitor.MonitorDpiAwareBounds.Left), TransformY(monitor.MonitorDpiAwareBounds.Top), Scaled(monitor.MonitorDpiAwareBounds.Width), Scaled(monitor.MonitorDpiAwareBounds.Height))); } - var appsToDraw = project.Applications.Where(x => !x.Minimized); + var appsToDraw = appsIncluded.Where(x => !x.Minimized); // draw the highlighted app at the end to have its icon in the foreground for the case there are overlapping icons foreach (Application app in appsToDraw.Where(x => !x.IsHighlighted)) { - Rectangle rect = new Rectangle(TransformX(app.ScaledPosition.X), TransformY(app.ScaledPosition.Y), Scaled(app.ScaledPosition.Width), Scaled(app.ScaledPosition.Height)); + Rectangle rect = GetAppRect(app); DrawWindow(g, brush, rect, app, desiredIconSize); } foreach (Application app in appsToDraw.Where(x => x.IsHighlighted)) { - Rectangle rect = new Rectangle(TransformX(app.ScaledPosition.X), TransformY(app.ScaledPosition.Y), Scaled(app.ScaledPosition.Width), Scaled(app.ScaledPosition.Height)); + Rectangle rect = GetAppRect(app); DrawWindow(g, brushForHighlight, rect, app, desiredIconSize); } @@ -180,7 +193,7 @@ namespace ProjectsEditor.Utils try { graphics.DrawIcon(app.Icon, iconBounds); - if (app.RepeatIndex > 0) + if (app.RepeatIndex > 1) { string indexString = app.RepeatIndex.ToString(CultureInfo.InvariantCulture); int indexSize = (int)(iconBounds.Width * 0.5); diff --git a/src/modules/Projects/ProjectsEditor/Utils/ProjectsEditorIO.cs b/src/modules/Projects/ProjectsEditor/Utils/ProjectsEditorIO.cs index 68f6d41c1e..d03d929c25 100644 --- a/src/modules/Projects/ProjectsEditor/Utils/ProjectsEditorIO.cs +++ b/src/modules/Projects/ProjectsEditor/Utils/ProjectsEditorIO.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Windows; using ManagedCommon; using ProjectsEditor.Data; @@ -85,7 +86,7 @@ namespace ProjectsEditor.Utils foreach (var app in project.Applications) { - newProject.Applications.Add(new Models.Application() + Models.Application newApp = new Models.Application() { AppName = app.Application, AppPath = app.ApplicationPath, @@ -93,6 +94,7 @@ namespace ProjectsEditor.Utils PackageFullName = app.PackageFullName, Parent = newProject, CommandLineArguments = app.CommandLineArguments, + LaunchesAsAdmin = app.LaunchesAsAdmin, Maximized = app.Maximized, Minimized = app.Minimized, IsNotFound = false, @@ -104,7 +106,9 @@ namespace ProjectsEditor.Utils Y = app.Position.Y, }, MonitorNumber = app.Monitor, - }); + }; + newApp.InitializationFinished(); + newProject.Applications.Add(newApp); } foreach (var monitor in project.MonitorConfiguration) @@ -136,7 +140,7 @@ namespace ProjectsEditor.Utils MonitorConfiguration = new List { }, }; - foreach (var app in project.Applications) + foreach (var app in project.Applications.Where(x => x.IsIncluded)) { wrapper.Applications.Add(new ProjectData.ApplicationWrapper { @@ -145,6 +149,7 @@ namespace ProjectsEditor.Utils Title = app.AppTitle, PackageFullName = app.PackageFullName, CommandLineArguments = app.CommandLineArguments, + LaunchesAsAdmin = app.LaunchesAsAdmin, Maximized = app.Maximized, Minimized = app.Minimized, Position = new ProjectData.ApplicationWrapper.WindowPositionWrapper diff --git a/src/modules/Projects/ProjectsEditor/ViewModels/MainViewModel.cs b/src/modules/Projects/ProjectsEditor/ViewModels/MainViewModel.cs index ab50c911b3..64dfce6de5 100644 --- a/src/modules/Projects/ProjectsEditor/ViewModels/MainViewModel.cs +++ b/src/modules/Projects/ProjectsEditor/ViewModels/MainViewModel.cs @@ -161,7 +161,7 @@ namespace ProjectsEditor.ViewModels editedProject.IsShortcutNeeded = projectToSave.IsShortcutNeeded; editedProject.PreviewIcons = projectToSave.PreviewIcons; editedProject.PreviewImage = projectToSave.PreviewImage; - editedProject.Applications = projectToSave.Applications; + editedProject.Applications = projectToSave.Applications.Where(x => x.IsIncluded).ToList(); editedProject.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("AppsCountString")); editedProject.Initialize(); @@ -233,7 +233,12 @@ namespace ProjectsEditor.ViewModels public void EditProject(Project selectedProject, bool isNewlyCreated = false) { var editPage = new ProjectEditor(this); + SetEditedProject(selectedProject); + if (!isNewlyCreated) + { + selectedProject = new Project(selectedProject); + } if (isNewlyCreated) {