Add profile wip

This commit is contained in:
Stefan Markovic
2023-09-11 11:46:25 +02:00
parent 509f919d46
commit 01e0c4807d
12 changed files with 368 additions and 44 deletions

View File

@@ -5,6 +5,7 @@
<ItemGroup> <ItemGroup>
<PackageVersion Include="Appium.WebDriver" Version="4.2.1" /> <PackageVersion Include="Appium.WebDriver" Version="4.2.1" />
<PackageVersion Include="CommunityToolkit.Labs.WinUI.SettingsControls" Version="0.0.18" /> <PackageVersion Include="CommunityToolkit.Labs.WinUI.SettingsControls" Version="0.0.18" />
<PackageVersion Include="CommunityToolkit.Labs.WinUI.SegmentedControl" Version="0.0.3" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.2.0" /> <PackageVersion Include="CommunityToolkit.Mvvm" Version="8.2.0" />
<PackageVersion Include="CommunityToolkit.WinUI.UI" Version="7.1.2" /> <PackageVersion Include="CommunityToolkit.WinUI.UI" Version="7.1.2" />
<PackageVersion Include="CommunityToolkit.WinUI.UI.Animations" Version="7.1.2" /> <PackageVersion Include="CommunityToolkit.WinUI.UI.Animations" Version="7.1.2" />

View File

@@ -58,6 +58,11 @@
<ApplicationDefinition Include="EnvironmentVariablesXAML\App.xaml" /> <ApplicationDefinition Include="EnvironmentVariablesXAML\App.xaml" />
</ItemGroup> </ItemGroup>
<!-- Needed for CommunityToolkit.Labs.WinUI.SettingsControls and Needed for CommunityToolkit.Labs.WinUI.SegmentedControl. -->
<PropertyGroup>
<RestoreAdditionalProjectSources>https://pkgs.dev.azure.com/dotnet/CommunityToolkit/_packaging/CommunityToolkit-Labs/nuget/v3/index.json</RestoreAdditionalProjectSources>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="Assets\EnvironmentVariables\SplashScreen.scale-200.png" /> <Content Include="Assets\EnvironmentVariables\SplashScreen.scale-200.png" />
<Content Include="Assets\EnvironmentVariables\LockScreenLogo.scale-200.png" /> <Content Include="Assets\EnvironmentVariables\LockScreenLogo.scale-200.png" />
@@ -70,12 +75,13 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWinRT" /> <PackageReference Include="Microsoft.Windows.CsWinRT" />
<PackageReference Include="CommunityToolkit.Mvvm" />
<PackageReference Include="Microsoft.Extensions.Hosting" /> <PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="Microsoft.WindowsAppSDK" /> <PackageReference Include="Microsoft.WindowsAppSDK" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" /> <PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" /> <PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" />
<PackageReference Include="CommunityToolkit.Labs.WinUI.SettingsControls" /> <PackageReference Include="CommunityToolkit.Labs.WinUI.SettingsControls" />
<PackageReference Include="CommunityToolkit.Mvvm" />
<PackageReference Include="CommunityToolkit.Labs.WinUI.SegmentedControl" />
<PackageReference Include="WinUIEx" /> <PackageReference Include="WinUIEx" />
<Manifest Include="$(ApplicationManifest)" /> <Manifest Include="$(ApplicationManifest)" />
</ItemGroup> </ItemGroup>

View File

@@ -23,20 +23,6 @@ namespace EnvironmentVariables
AppWindow.SetIcon("Assets/EnvironmentVariables/EnvironmentVariables.ico"); AppWindow.SetIcon("Assets/EnvironmentVariables/EnvironmentVariables.ico");
Title = ResourceLoaderInstance.ResourceLoader.GetString("WindowTitle"); Title = ResourceLoaderInstance.ResourceLoader.GetString("WindowTitle");
Activated += MainWindow_Activated;
}
private void MainWindow_Activated(object sender, WindowActivatedEventArgs args)
{
if (args.WindowActivationState == WindowActivationState.Deactivated)
{
AppTitleTextBlock.Foreground = (SolidColorBrush)App.Current.Resources["WindowCaptionForegroundDisabled"];
}
else
{
AppTitleTextBlock.Foreground = (SolidColorBrush)App.Current.Resources["WindowCaptionForeground"];
}
} }
} }
} }

View File

@@ -43,10 +43,10 @@
<Grid> <Grid>
<!-- buttons --> <!-- buttons -->
<Button> <Button Command="{x:Bind NewProfileCommand}">
<StackPanel Orientation="Horizontal" Spacing="8"> <StackPanel Orientation="Horizontal" Spacing="8">
<FontIcon <FontIcon
x:Name="Icon" x:Name="NewProfileIcon"
FontSize="16" FontSize="16"
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
Glyph="&#xe710;" /> Glyph="&#xe710;" />
@@ -117,8 +117,8 @@
</Grid> </Grid>
<ContentDialog <ContentDialog
x:Name="EditDialog" x:Name="EditVariableDialog"
x:Uid="EditDialog" x:Uid="EditVariableDialog"
IsPrimaryButtonEnabled="True" IsPrimaryButtonEnabled="True"
PrimaryButtonStyle="{StaticResource AccentButtonStyle}"> PrimaryButtonStyle="{StaticResource AccentButtonStyle}">
<ContentDialog.DataContext> <ContentDialog.DataContext>
@@ -146,5 +146,67 @@
</ScrollViewer> </ScrollViewer>
</ContentDialog> </ContentDialog>
<ContentDialog
x:Name="AddProfileDialog"
x:Uid="AddProfileDialog"
IsPrimaryButtonEnabled="True"
PrimaryButtonStyle="{StaticResource AccentButtonStyle}">
<ContentDialog.DataContext>
<models:ProfileVariablesSet />
</ContentDialog.DataContext>
<ScrollViewer>
<StackPanel
MinWidth="320"
HorizontalAlignment="Stretch"
Spacing="12">
<TextBox
x:Uid="NameTxtBox"
IsSpellCheckEnabled="False"
Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<ToggleSwitch
x:Uid="Enabled"
IsOn="{Binding IsEnabled, Mode=TwoWay}"
OffContent=""
OnContent="" />
<Button>
<StackPanel Orientation="Horizontal" Spacing="8">
<FontIcon
x:Name="AddVariableIcon"
FontSize="16"
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
Glyph="&#xe710;" />
<TextBlock x:Uid="AddVariableBtn" />
</StackPanel>
<Button.Flyout>
<Flyout>
<StackPanel>
<labs:Segmented
x:Name="SwitchViewsSegmentedView"
MaxWidth="500"
HorizontalAlignment="Stretch"
SelectionChanged="Segmented_SelectionChanged"
SelectionMode="Single">
<labs:SegmentedItem x:Name="AddNewVariableSegmentedItem" x:Uid="NewVariableSegmentedButton" />
<labs:SegmentedItem x:Name="AddExistingVariableSegmentedItem" x:Uid="ExistingVariableSegmentedButton" />
</labs:Segmented>
<!-- Adding new variable -->
<TextBox
x:Name="AddNewVariableName"
x:Uid="AddNewVariableName"
Margin="0,20,0,0"
Visibility="{x:Bind ViewModel.ShowAddNewVariablePage, Mode=TwoWay}" />
<TextBox
x:Name="AddNewVariableValue"
x:Uid="AddNewVariableValue"
Margin="0,20,0,0"
Visibility="{x:Bind ViewModel.ShowAddNewVariablePage, Mode=TwoWay}" />
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
</StackPanel>
</ScrollViewer>
</ContentDialog>
</Grid> </Grid>
</Page> </Page>

View File

@@ -9,7 +9,9 @@ using CommunityToolkit.Labs.WinUI;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using EnvironmentVariables.Models; using EnvironmentVariables.Models;
using EnvironmentVariables.ViewModels; using EnvironmentVariables.ViewModels;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using static EnvironmentVariables.Models.Common;
namespace EnvironmentVariables.Views namespace EnvironmentVariables.Views
{ {
@@ -19,6 +21,10 @@ namespace EnvironmentVariables.Views
public ICommand EditCommand => new RelayCommand<Variable>(EditVariable); public ICommand EditCommand => new RelayCommand<Variable>(EditVariable);
public ICommand NewProfileCommand => new AsyncRelayCommand(AddProfileAsync);
public ICommand AddProfileCommand => new RelayCommand(AddProfile);
public MainPage() public MainPage()
{ {
this.InitializeComponent(); this.InitializeComponent();
@@ -30,15 +36,15 @@ namespace EnvironmentVariables.Views
{ {
var resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader; var resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
EditDialog.Title = resourceLoader.GetString("EditVariableDialog_Title"); EditVariableDialog.Title = resourceLoader.GetString("EditVariableDialog_Title");
EditDialog.PrimaryButtonText = resourceLoader.GetString("SaveBtn"); EditVariableDialog.PrimaryButtonText = resourceLoader.GetString("SaveBtn");
EditDialog.PrimaryButtonCommand = EditCommand; EditVariableDialog.PrimaryButtonCommand = EditCommand;
EditDialog.PrimaryButtonCommandParameter = variable; EditVariableDialog.PrimaryButtonCommandParameter = variable;
var clone = variable.Clone(); var clone = variable.Clone();
EditDialog.DataContext = clone; EditVariableDialog.DataContext = clone;
await EditDialog.ShowAsync(); await EditVariableDialog.ShowAsync();
} }
private async void EditVariable_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) private async void EditVariable_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
@@ -52,8 +58,50 @@ namespace EnvironmentVariables.Views
private void EditVariable(Variable original) private void EditVariable(Variable original)
{ {
var edited = EditDialog.DataContext as Variable; var edited = EditVariableDialog.DataContext as Variable;
ViewModel.EditVariable(original, edited); ViewModel.EditVariable(original, edited);
} }
private async Task AddProfileAsync()
{
SwitchViewsSegmentedView.SelectedIndex = 0;
ViewModel.CurrentAddVariablePage = AddVariablePageKind.AddNewVariable;
ViewModel.ShowAddNewVariablePage = Visibility.Visible;
var resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
AddProfileDialog.Title = resourceLoader.GetString("AddNewProfileDialog_Title");
AddProfileDialog.PrimaryButtonText = resourceLoader.GetString("AddBtn");
AddProfileDialog.PrimaryButtonCommand = AddProfileCommand;
AddProfileDialog.DataContext = new ProfileVariablesSet(Guid.NewGuid(), string.Empty);
await AddProfileDialog.ShowAsync();
}
private void AddProfile()
{
var profile = AddProfileDialog.DataContext as ProfileVariablesSet;
ViewModel.AddProfile(profile);
}
private void Segmented_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ViewModel.CurrentAddVariablePage == AddVariablePageKind.AddNewVariable)
{
ChangeToExistingVariablePage();
}
else
{
ChangeToNewVariablePage();
}
}
private void ChangeToNewVariablePage()
{
ViewModel.ChangeToNewVariablePage();
}
private void ChangeToExistingVariablePage()
{
ViewModel.ChangeToExistingVariablePage();
}
} }
} }

View File

@@ -11,6 +11,30 @@ namespace EnvironmentVariables.Helpers
{ {
internal sealed class EnvironmentVariablesHelper internal sealed class EnvironmentVariablesHelper
{ {
internal static string GetBackupVariableName(Variable variable, string profileName)
{
return variable.Name + "_PowerToys_" + profileName;
}
internal static Variable GetExisting(string variableName)
{
var userVariables = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
if (userVariables.Contains(variableName))
{
return new Variable(variableName, userVariables[variableName] as string, VariablesSetType.User);
}
var systemVariables = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine);
if (systemVariables.Contains(variableName))
{
return new Variable(variableName, userVariables[variableName] as string, VariablesSetType.System);
}
return null;
}
internal static void GetVariables(EnvironmentVariableTarget target, VariablesSet set) internal static void GetVariables(EnvironmentVariableTarget target, VariablesSet set)
{ {
var variables = Environment.GetEnvironmentVariables(target); var variables = Environment.GetEnvironmentVariables(target);

View File

@@ -0,0 +1,16 @@
// 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.
namespace EnvironmentVariables.Models
{
internal sealed class Common
{
internal enum AddVariablePageKind
{
AddNewVariable,
AddExistingVariable,
}
}
}

View File

@@ -3,16 +3,91 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using EnvironmentVariables.Helpers;
using ManagedCommon;
namespace EnvironmentVariables.Models namespace EnvironmentVariables.Models
{ {
public class ProfileVariablesSet : VariablesSet public partial class ProfileVariablesSet : VariablesSet
{ {
public bool IsEnabled { get; set; } [ObservableProperty]
private bool _isEnabled;
public ProfileVariablesSet()
: base()
{
}
public ProfileVariablesSet(Guid id, string name) public ProfileVariablesSet(Guid id, string name)
: base(id, name, VariablesSetType.Profile) : base(id, name, VariablesSetType.Profile)
{ {
} }
public void Apply()
{
Task.Run(() =>
{
foreach (var variable in Variables)
{
// Get existing variable with the same name if it exist
var variableToOverride = EnvironmentVariablesHelper.GetExisting(variable.Name);
// It exists. Rename it to preserve it.
if (variableToOverride != null)
{
variableToOverride.Name = EnvironmentVariablesHelper.GetBackupVariableName(variableToOverride, this.Name);
// Backup the variable
if (!EnvironmentVariablesHelper.SetVariable(variableToOverride))
{
Logger.LogError("Failed to set backup variable.");
}
}
if (!EnvironmentVariablesHelper.SetVariable(variable))
{
Logger.LogError("Failed to set profile variable.");
}
}
});
}
public void UnApply()
{
Task.Run(() =>
{
foreach (var variable in Variables)
{
// Unset the variable
if (!EnvironmentVariablesHelper.UnsetVariable(variable))
{
Logger.LogError("Failed to unset variable.");
}
var originalName = variable.Name;
var backupName = EnvironmentVariablesHelper.GetBackupVariableName(variable, this.Name);
// Get backup variable if it exist
var backupVariable = EnvironmentVariablesHelper.GetExisting(backupName);
if (backupVariable != null)
{
var variableToRestore = new Variable(originalName, backupVariable.Values, backupVariable.ParentType);
if (!EnvironmentVariablesHelper.UnsetVariable(backupVariable))
{
Logger.LogError("Failed to unset backup variable.");
}
if (!EnvironmentVariablesHelper.SetVariable(variableToRestore))
{
Logger.LogError("Failed to restore backup variable.");
}
}
}
});
}
} }
} }

View File

@@ -31,15 +31,10 @@ namespace EnvironmentVariables.Models
Values = values; Values = values;
ParentType = parentType; ParentType = parentType;
ValuesList = new List<string>();
var splitValues = Values.Split(';'); var splitValues = Values.Split(';');
if (splitValues.Length > 0) if (splitValues.Length > 0)
{ {
foreach (var splitValue in splitValues) ValuesList = new List<string>(splitValues);
{
ValuesList.Add(splitValue);
}
} }
} }

View File

@@ -4,11 +4,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel;
namespace EnvironmentVariables.Models namespace EnvironmentVariables.Models
{ {
public class VariablesSet public partial class VariablesSet : ObservableObject
{ {
public static readonly Guid UserGuid = new Guid("92F7AA9A-AE31-49CD-83C8-80A71E432AA5"); public static readonly Guid UserGuid = new Guid("92F7AA9A-AE31-49CD-83C8-80A71E432AA5");
public static readonly Guid SystemGuid = new Guid("F679C74D-DB00-4795-92E1-B1F6A4833279"); public static readonly Guid SystemGuid = new Guid("F679C74D-DB00-4795-92E1-B1F6A4833279");
@@ -19,7 +19,8 @@ namespace EnvironmentVariables.Models
public Guid Id { get; } public Guid Id { get; }
public string Name { get; } [ObservableProperty]
private string _name;
public VariablesSetType Type { get; } public VariablesSetType Type { get; }
@@ -27,6 +28,10 @@ namespace EnvironmentVariables.Models
public List<Variable> Variables { get; } public List<Variable> Variables { get; }
public VariablesSet()
{
}
public VariablesSet(Guid id, string name, VariablesSetType type) public VariablesSet(Guid id, string name, VariablesSetType type)
{ {
Id = id; Id = id;

View File

@@ -156,4 +156,28 @@
<data name="ValueTxtBox.Header" xml:space="preserve"> <data name="ValueTxtBox.Header" xml:space="preserve">
<value>Value</value> <value>Value</value>
</data> </data>
<data name="AddBtn" xml:space="preserve">
<value>Add</value>
</data>
<data name="AddNewProfileDialog_Title" xml:space="preserve">
<value>Add new profile</value>
</data>
<data name="Enabled.Header" xml:space="preserve">
<value>Enabled</value>
</data>
<data name="AddVariableBtn.Text" xml:space="preserve">
<value>Add variable</value>
</data>
<data name="AddNewVariableName.Header" xml:space="preserve">
<value>Variable name</value>
</data>
<data name="AddNewVariableValue.Header" xml:space="preserve">
<value>Variable value</value>
</data>
<data name="ExistingVariableSegmentedButton.Content" xml:space="preserve">
<value>Existing</value>
</data>
<data name="NewVariableSegmentedButton.Content" xml:space="preserve">
<value>New</value>
</data>
</root> </root>

View File

@@ -3,15 +3,18 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using EnvironmentVariables.Helpers; using EnvironmentVariables.Helpers;
using EnvironmentVariables.Models; using EnvironmentVariables.Models;
using Microsoft.UI.Xaml;
using static EnvironmentVariables.Models.Common;
namespace EnvironmentVariables.ViewModels namespace EnvironmentVariables.ViewModels
{ {
public partial class MainViewModel public partial class MainViewModel : ObservableObject
{ {
public DefaultVariablesSet UserDefaultSet { get; private set; } = new DefaultVariablesSet(VariablesSet.UserGuid, ResourceLoaderInstance.ResourceLoader.GetString("User"), VariablesSetType.User); public DefaultVariablesSet UserDefaultSet { get; private set; } = new DefaultVariablesSet(VariablesSet.UserGuid, ResourceLoaderInstance.ResourceLoader.GetString("User"), VariablesSetType.User);
@@ -19,6 +22,16 @@ namespace EnvironmentVariables.ViewModels
public ObservableCollection<ProfileVariablesSet> Profiles { get; private set; } = new ObservableCollection<ProfileVariablesSet>(); public ObservableCollection<ProfileVariablesSet> Profiles { get; private set; } = new ObservableCollection<ProfileVariablesSet>();
public ProfileVariablesSet AppliedProfile { get; set; }
internal AddVariablePageKind CurrentAddVariablePage { get; set; }
[ObservableProperty]
private Visibility _showAddNewVariablePage;
[ObservableProperty]
private Visibility _showAddExistingVariablePage;
public MainViewModel() public MainViewModel()
{ {
} }
@@ -30,11 +43,14 @@ namespace EnvironmentVariables.ViewModels
EnvironmentVariablesHelper.GetVariables(EnvironmentVariableTarget.User, UserDefaultSet); EnvironmentVariablesHelper.GetVariables(EnvironmentVariableTarget.User, UserDefaultSet);
var profile1 = new ProfileVariablesSet(Guid.NewGuid(), "profile1"); var profile1 = new ProfileVariablesSet(Guid.NewGuid(), "profile1");
profile1.Variables.Add(new Variable("profile11", "pvalue1", VariablesSetType.Profile)); profile1.Variables.Add(new Variable("testvar1", "pvalue1", VariablesSetType.Profile));
profile1.Variables.Add(new Variable("profile12", "pvalue2", VariablesSetType.Profile)); profile1.Variables.Add(new Variable("p11", "pvalue2", VariablesSetType.Profile));
profile1.PropertyChanged += Profile_PropertyChanged;
var profile2 = new ProfileVariablesSet(Guid.NewGuid(), "profile2"); var profile2 = new ProfileVariablesSet(Guid.NewGuid(), "profile2");
profile2.Variables.Add(new Variable("profile21", "pvalue11", VariablesSetType.Profile)); profile2.Variables.Add(new Variable("ppp22", "pvalue11", VariablesSetType.Profile));
profile2.Variables.Add(new Variable("profile22", "pvalue22", VariablesSetType.Profile)); profile2.Variables.Add(new Variable("pp22", "pvalue22", VariablesSetType.Profile));
profile2.PropertyChanged += Profile_PropertyChanged;
Profiles.Add(profile1); Profiles.Add(profile1);
Profiles.Add(profile2); Profiles.Add(profile2);
@@ -44,5 +60,71 @@ namespace EnvironmentVariables.ViewModels
{ {
original.Update(edited); original.Update(edited);
} }
internal void AddProfile(ProfileVariablesSet profile)
{
profile.PropertyChanged += Profile_PropertyChanged;
if (profile.IsEnabled)
{
UnsetAppliedProfile();
SetAppliedProfile(profile);
}
Profiles.Add(profile);
}
private void Profile_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var profile = sender as ProfileVariablesSet;
if (profile != null)
{
if (e.PropertyName == nameof(ProfileVariablesSet.IsEnabled))
{
if (profile.IsEnabled)
{
UnsetAppliedProfile();
SetAppliedProfile(profile);
}
else
{
UnsetAppliedProfile();
}
}
}
}
private void SetAppliedProfile(ProfileVariablesSet profile)
{
profile.Apply();
AppliedProfile = profile;
}
private void UnsetAppliedProfile()
{
if (AppliedProfile != null)
{
var appliedProfile = AppliedProfile;
appliedProfile.PropertyChanged -= Profile_PropertyChanged;
AppliedProfile.UnApply();
AppliedProfile.IsEnabled = false;
AppliedProfile = null;
appliedProfile.PropertyChanged += Profile_PropertyChanged;
}
}
internal void ChangeToNewVariablePage()
{
ShowAddExistingVariablePage = Visibility.Collapsed;
ShowAddNewVariablePage = Visibility.Visible;
CurrentAddVariablePage = AddVariablePageKind.AddNewVariable;
}
internal void ChangeToExistingVariablePage()
{
ShowAddNewVariablePage = Visibility.Collapsed;
ShowAddExistingVariablePage = Visibility.Visible;
CurrentAddVariablePage = AddVariablePageKind.AddExistingVariable;
}
} }
} }