Remove variable and profile logic

This commit is contained in:
Stefan Markovic
2023-09-18 17:11:50 +02:00
parent fd1cfef3f8
commit d708e8bb7c
7 changed files with 282 additions and 41 deletions

View File

@@ -11,6 +11,105 @@
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" /> <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<!-- Other merged dictionaries here --> <!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="SubtleButtonBackground" Color="{ThemeResource SubtleFillColorTransparent}" />
<SolidColorBrush x:Key="SubtleButtonBackgroundPointerOver" Color="{ThemeResource SubtleFillColorSecondary}" />
<SolidColorBrush x:Key="SubtleButtonBackgroundPressed" Color="{ThemeResource SubtleFillColorTertiary}" />
<SolidColorBrush x:Key="SubtleButtonBackgroundDisabled" Color="{ThemeResource ControlFillColorDisabled}" />
<SolidColorBrush x:Key="SubtleButtonForeground" Color="{ThemeResource TextFillColorPrimary}" />
<SolidColorBrush x:Key="SubtleButtonForegroundPointerOver" Color="{ThemeResource TextFillColorPrimary}" />
<SolidColorBrush x:Key="SubtleButtonForegroundPressed" Color="{ThemeResource TextFillColorSecondary}" />
<SolidColorBrush x:Key="SubtleButtonForegroundDisabled" Color="{ThemeResource TextFillColorDisabled}" />
<Style x:Key="SubtleButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{ThemeResource SubtleButtonBackground}" />
<Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
<Setter Property="Foreground" Value="{ThemeResource SubtleButtonForeground}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Padding" Value="{StaticResource ButtonPadding}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
<Setter Property="FocusVisualMargin" Value="-3" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ContentPresenter
x:Name="ContentPresenter"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
AnimatedIcon.State="Normal"
AutomationProperties.AccessibilityView="Raw"
Background="{TemplateBinding Background}"
BackgroundSizing="{TemplateBinding BackgroundSizing}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}"
CornerRadius="{TemplateBinding CornerRadius}">
<ContentPresenter.BackgroundTransition>
<BrushTransition Duration="0:0:0.083" />
</ContentPresenter.BackgroundTransition>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<VisualState.Setters>
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="PointerOver" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPressed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<VisualState.Setters>
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Pressed" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<VisualState.Setters>
<!-- DisabledVisual Should be handled by the control, not the animated icon. -->
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Normal" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</ContentPresenter>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>
</Application> </Application>

View File

@@ -10,10 +10,10 @@
xmlns:i="using:Microsoft.Xaml.Interactivity" xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:ic="using:Microsoft.Xaml.Interactions.Core" xmlns:ic="using:Microsoft.Xaml.Interactions.Core"
xmlns:labs="using:CommunityToolkit.Labs.WinUI" xmlns:labs="using:CommunityToolkit.Labs.WinUI"
xmlns:local="using:EnvironmentVariables.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="using:EnvironmentVariables.Models" xmlns:models="using:EnvironmentVariables.Models"
xmlns:ui="using:CommunityToolkit.WinUI.UI" xmlns:ui="using:CommunityToolkit.WinUI.UI"
x:Name="RootPage"
mc:Ignorable="d"> mc:Ignorable="d">
<Page.Resources> <Page.Resources>
<DataTemplate x:Key="VariableTemplate" x:DataType="models:Variable"> <DataTemplate x:Key="VariableTemplate" x:DataType="models:Variable">
@@ -21,11 +21,22 @@
Click="EditVariable_Click" Click="EditVariable_Click"
CommandParameter="{x:Bind (models:Variable)}" CommandParameter="{x:Bind (models:Variable)}"
Header="{x:Bind Name, Mode=TwoWay}" Header="{x:Bind Name, Mode=TwoWay}"
IsClickEnabled="{x:Bind Editable, Mode=OneWay}"> IsClickEnabled="{x:Bind IsEditable, Mode=OneWay}">
<TextBlock <TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}" Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True" IsTextSelectionEnabled="True"
Text="{x:Bind Values, Mode=TwoWay}" /> Text="{x:Bind Values, Mode=TwoWay}" />
<labs:SettingsCard.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem
x:Uid="DeleteMenuItem"
Click="Delete_Variable_Click"
CommandParameter="{x:Bind (models:Variable)}"
Icon="Delete"
IsEnabled="{x:Bind IsEditable, Mode=OneWay}" />
</MenuFlyout>
</labs:SettingsCard.ContextFlyout>
</labs:SettingsCard> </labs:SettingsCard>
</DataTemplate> </DataTemplate>
@@ -98,7 +109,19 @@
ItemTemplate="{StaticResource VariableTemplate}" ItemTemplate="{StaticResource VariableTemplate}"
ItemsSource="{x:Bind Variables, Mode=OneWay}"> ItemsSource="{x:Bind Variables, Mode=OneWay}">
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind Mode=TwoWay, Path=IsEnabled}" /> <StackPanel Orientation="Horizontal">
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind Mode=TwoWay, Path=IsEnabled}" />
<Button
x:Name="RemoveProfileBtn"
x:Uid="RemoveProfileBtn"
Width="40"
Height="36"
Click="RemoveProfileBtn_Click"
CommandParameter="{x:Bind (models:ProfileVariablesSet)}"
Content="&#xE74D;"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Style="{StaticResource SubtleButtonStyle}" />
</StackPanel>
</labs:SettingsExpander> </labs:SettingsExpander>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>

View File

@@ -17,7 +17,7 @@ namespace EnvironmentVariables.Views
{ {
public MainViewModel ViewModel { get; private set; } public MainViewModel ViewModel { get; private set; }
public ICommand EditCommand => new RelayCommand<Variable>(EditVariable); public ICommand EditCommand => new RelayCommand<SettingsCard>(EditVariable);
public ICommand NewProfileCommand => new AsyncRelayCommand(AddProfileAsync); public ICommand NewProfileCommand => new AsyncRelayCommand(AddProfileAsync);
@@ -32,15 +32,16 @@ namespace EnvironmentVariables.Views
DataContext = ViewModel; DataContext = ViewModel;
} }
private async Task ShowEditDialogAsync(Variable variable) private async Task ShowEditDialogAsync(SettingsCard card)
{ {
var resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader; var resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
EditVariableDialog.Title = resourceLoader.GetString("EditVariableDialog_Title"); EditVariableDialog.Title = resourceLoader.GetString("EditVariableDialog_Title");
EditVariableDialog.PrimaryButtonText = resourceLoader.GetString("SaveBtn"); EditVariableDialog.PrimaryButtonText = resourceLoader.GetString("SaveBtn");
EditVariableDialog.PrimaryButtonCommand = EditCommand; EditVariableDialog.PrimaryButtonCommand = EditCommand;
EditVariableDialog.PrimaryButtonCommandParameter = variable; EditVariableDialog.PrimaryButtonCommandParameter = card;
var variable = card.CommandParameter as Variable;
var clone = variable.Clone(); var clone = variable.Clone();
EditVariableDialog.DataContext = clone; EditVariableDialog.DataContext = clone;
@@ -52,14 +53,16 @@ namespace EnvironmentVariables.Views
SettingsCard card = sender as SettingsCard; SettingsCard card = sender as SettingsCard;
if (card != null) if (card != null)
{ {
await ShowEditDialogAsync(card.CommandParameter as Variable); await ShowEditDialogAsync(card);
} }
} }
private void EditVariable(Variable original) private void EditVariable(SettingsCard card)
{ {
var variableSet = card.DataContext as ProfileVariablesSet;
var original = card.CommandParameter as Variable;
var edited = EditVariableDialog.DataContext as Variable; var edited = EditVariableDialog.DataContext as Variable;
ViewModel.EditVariable(original, edited); ViewModel.EditVariable(original, edited, variableSet);
} }
private async Task AddProfileAsync() private async Task AddProfileAsync()
@@ -80,6 +83,30 @@ namespace EnvironmentVariables.Views
ViewModel.AddProfile(profile); ViewModel.AddProfile(profile);
} }
private async void RemoveProfileBtn_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
var button = sender as Button;
var profile = button.CommandParameter as ProfileVariablesSet;
if (profile != null)
{
var resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
ContentDialog dialog = new ContentDialog();
dialog.XamlRoot = RootPage.XamlRoot;
dialog.Title = profile.Name;
dialog.PrimaryButtonText = resourceLoader.GetString("Yes");
dialog.CloseButtonText = resourceLoader.GetString("No");
dialog.DefaultButton = ContentDialogButton.Primary;
dialog.Content = new TextBlock() { Text = resourceLoader.GetString("Delete_Dialog_Description") };
dialog.PrimaryButtonClick += (s, args) =>
{
ViewModel.RemoveProfile(profile);
};
var result = await dialog.ShowAsync();
}
}
private void AddVariable() private void AddVariable()
{ {
var profile = AddProfileDialog.DataContext as ProfileVariablesSet; var profile = AddProfileDialog.DataContext as ProfileVariablesSet;
@@ -104,5 +131,17 @@ namespace EnvironmentVariables.Views
ExistingVariablesListView.SelectedItems.Clear(); ExistingVariablesListView.SelectedItems.Clear();
AddVariableFlyout.Hide(); AddVariableFlyout.Hide();
} }
private void Delete_Variable_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
MenuFlyoutItem menuItem = sender as MenuFlyoutItem;
var variableSet = menuItem.DataContext as ProfileVariablesSet;
var variable = menuItem.CommandParameter as Variable;
if (variable != null)
{
ViewModel.DeleteVariable(variable, variableSet);
}
}
} }
} }

View File

@@ -2,9 +2,9 @@
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// 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.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using EnvironmentVariables.Helpers; using EnvironmentVariables.Helpers;
@@ -19,7 +19,7 @@ namespace EnvironmentVariables.Models
private string _values; private string _values;
[JsonIgnore] [JsonIgnore]
public bool Editable public bool IsEditable
{ {
get get
{ {
@@ -49,44 +49,37 @@ namespace EnvironmentVariables.Models
} }
} }
internal void Update(Variable edited) internal Task Update(Variable edited, bool propagateChange)
{ {
bool changed = Name != edited.Name || Values != edited.Values;
bool nameChanged = Name != edited.Name; bool nameChanged = Name != edited.Name;
bool success = false; bool success = true;
if (changed) var clone = this.Clone();
// Update state
Name = edited.Name;
Values = edited.Values;
ValuesList = new List<string>(Values.Split(';'));
return Task.Run(() =>
{ {
// Apply changes // Apply changes
if (nameChanged) if (propagateChange)
{ {
success = EnvironmentVariablesHelper.UnsetVariable(this); if (nameChanged)
}
success = EnvironmentVariablesHelper.SetVariable(edited);
// Update state
if (success)
{
Name = edited.Name;
Values = edited.Values;
ValuesList = new List<string>();
var splitValues = Values.Split(';');
if (splitValues.Length > 0)
{ {
foreach (var splitValue in splitValues) success = EnvironmentVariablesHelper.UnsetVariable(clone);
{
ValuesList.Add(splitValue);
}
} }
success = EnvironmentVariablesHelper.SetVariable(this);
} }
else
if (!success)
{ {
// show error // Show error
} }
} });
} }
internal Variable Clone(bool profile = false) internal Variable Clone(bool profile = false)

View File

@@ -18,7 +18,7 @@ namespace EnvironmentVariables.Models
private static readonly string SystemIconPath = "/Assets/EnvironmentVariables/SystemIcon.png"; private static readonly string SystemIconPath = "/Assets/EnvironmentVariables/SystemIcon.png";
protected static readonly string ProfileIconPath = "/Assets/EnvironmentVariables/ProfileIcon.png"; protected static readonly string ProfileIconPath = "/Assets/EnvironmentVariables/ProfileIcon.png";
public Guid Id { get; } public Guid Id { get; set; }
[ObservableProperty] [ObservableProperty]
private string _name; private string _name;

View File

@@ -203,7 +203,19 @@
<data name="NewProfileVariablesListViewHeader.Text" xml:space="preserve"> <data name="NewProfileVariablesListViewHeader.Text" xml:space="preserve">
<value>Variables</value> <value>Variables</value>
</data> </data>
<data name="DeleteMenuItem.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="Delete_Dialog_Description" xml:space="preserve">
<value>Are you sure you want to delete this profile? Deleting applied profile will remove all profile variables.</value>
</data>
<data name="EditSystemDefaultSetInfoBar.Title" xml:space="preserve"> <data name="EditSystemDefaultSetInfoBar.Title" xml:space="preserve">
<value>You need to run as administrator to edit System environment variables</value> <value>You need to run as administrator to edit System environment variables</value>
</data> </data>
<data name="No" xml:space="preserve">
<value>No</value>
</data>
<data name="Yes" xml:space="preserve">
<value>Yes</value>
</data>
</root> </root>

View File

@@ -8,12 +8,14 @@ using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Linq;
using CommunityToolkit.Mvvm.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 ManagedCommon; using ManagedCommon;
using Microsoft.UI.Dispatching; using Microsoft.UI.Dispatching;
using Windows.Foundation.Collections;
namespace EnvironmentVariables.ViewModels namespace EnvironmentVariables.ViewModels
{ {
@@ -111,9 +113,25 @@ namespace EnvironmentVariables.ViewModels
AppliedVariables = new ObservableCollection<Variable>(variables); AppliedVariables = new ObservableCollection<Variable>(variables);
} }
internal void EditVariable(Variable original, Variable edited) internal void EditVariable(Variable original, Variable edited, ProfileVariablesSet variablesSet)
{ {
original.Update(edited); bool propagateChange = variablesSet == null /* not a profile */ || variablesSet.Id.Equals(AppliedProfile?.Id);
bool changed = original.Name != edited.Name || original.Values != edited.Values;
if (changed)
{
ApplyingChanges = true;
var task = original.Update(edited, propagateChange);
PopulateAppliedVariables();
task.ContinueWith(x =>
{
_dispatcherQueue.TryEnqueue(() =>
{
ApplyingChanges = false;
});
});
_ = Task.Run(SaveAsync);
}
} }
internal void AddProfile(ProfileVariablesSet profile) internal void AddProfile(ProfileVariablesSet profile)
@@ -202,5 +220,62 @@ namespace EnvironmentVariables.ViewModels
PopulateAppliedVariables(); PopulateAppliedVariables();
} }
} }
internal void RemoveProfile(ProfileVariablesSet profile)
{
if (profile.IsEnabled)
{
UnsetAppliedProfile();
}
Profiles.Remove(profile);
_ = Task.Run(SaveAsync);
}
internal void DeleteVariable(Variable variable, ProfileVariablesSet profile)
{
bool propagateChange = true;
if (profile != null)
{
// Profile variable
profile.Variables.Remove(variable);
if (!profile.IsEnabled)
{
propagateChange = false;
}
}
else
{
if (variable.ParentType == VariablesSetType.User)
{
UserDefaultSet.Variables.Remove(variable);
}
else if (variable.ParentType == VariablesSetType.System)
{
SystemDefaultSet.Variables.Remove(variable);
}
}
if (propagateChange)
{
ApplyingChanges = true;
var task = Task.Run(() =>
{
EnvironmentVariablesHelper.UnsetVariable(variable);
});
task.ContinueWith((a) =>
{
_dispatcherQueue.TryEnqueue(() =>
{
ApplyingChanges = false;
});
});
}
PopulateAppliedVariables();
}
} }
} }