Profile settings

This commit is contained in:
2025-04-07 09:12:52 +02:00
parent 2b1e4def50
commit c021f7ee24
14 changed files with 179 additions and 71 deletions

View File

@ -1,9 +1,9 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35919.96 d17.13
VisualStudioVersion = 17.13.35919.96
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "proxmox-spice-launcher", "src\proxmox-spice-launcher\proxmox-spice-launcher\proxmox-spice-launcher.csproj", "{9A4EC4BE-8920-461F-8653-A9650A68B727}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "proxmox-spice-launcher", "src\proxmox-spice-launcher\proxmox-spice-launcher.csproj", "{8F3740FC-DC36-1459-2ECB-B04F0BBA4ED8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -11,10 +11,10 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9A4EC4BE-8920-461F-8653-A9650A68B727}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9A4EC4BE-8920-461F-8653-A9650A68B727}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9A4EC4BE-8920-461F-8653-A9650A68B727}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9A4EC4BE-8920-461F-8653-A9650A68B727}.Release|Any CPU.Build.0 = Release|Any CPU
{8F3740FC-DC36-1459-2ECB-B04F0BBA4ED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8F3740FC-DC36-1459-2ECB-B04F0BBA4ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8F3740FC-DC36-1459-2ECB-B04F0BBA4ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8F3740FC-DC36-1459-2ECB-B04F0BBA4ED8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -0,0 +1,16 @@
using System.Collections.ObjectModel;
namespace Proxmox.SpiceLauncher.Extensions;
internal static class ObservableCollectionExtensions
{
public static ObservableCollection<T> Sort<T>(this ObservableCollection<T> collection, Comparison<T> comparison)
{
var sortableList = new List<T>(collection);
sortableList.Sort(comparison);
for (int i = 0; i < sortableList.Count; i++)
{
collection.Move(collection.IndexOf(sortableList[i]), i);
}
return collection;
}
}

View File

@ -7,7 +7,7 @@
xmlns:local="clr-namespace:Proxmox.SpiceLauncher"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
mc:Ignorable="d"
Title="Proxmox Spice Launcher" Height="391" Width="350" ResizeMode="NoResize"
Title="Proxmox Spice Launcher" Height="391" Width="539" ResizeMode="NoResize"
Initialized="MetroWindow_Initialized" Closing="MetroWindow_Closing" SizeToContent="Height"
Icon="pack://application:,,,/Proxmox.SpiceLauncher.png">
<mah:MetroWindow.Resources>
@ -29,18 +29,24 @@
</mah:MetroWindow.RightWindowCommands>
<Grid>
<StackPanel Margin="10,10,10,10">
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal">
<mah:MetroHeader Header="Profile">
<ComboBox x:Name="ComboProfiles" SelectionChanged="ComboProfiles_OnSelectionChanged" />
<StackPanel>
<StackPanel Grid.IsSharedSizeScope="True" Margin="10,10,10,10">
<mah:MetroHeader Header="Profile" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid>
<ComboBox x:Name="ComboProfiles" SelectionChanged="ComboProfiles_OnSelectionChanged" Margin="0,0,100,0" />
<Button x:Name="ButtonConnect" Content="Connect" Click="ButtonConnect_Click"
HorizontalAlignment="Right" Width="90" />
</Grid>
</mah:MetroHeader>
<Button x:Name="ButtonConnect" Content="Connect" Click="ButtonConnect_Click" Margin="5,0,0,0" />
</StackPanel>
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal">
<mah:MetroHeader Header="VM">
<ComboBox x:Name="ComboVms" />
<mah:MetroHeader Header="VM" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid>
<ComboBox x:Name="ComboVms" Margin="0,0,100,0" />
<Button x:Name="ButtonSpice" Content="Spice" Click="ButtonSpice_Click"
HorizontalAlignment="Right" IsEnabled="False" Width="90" />
</Grid>
</mah:MetroHeader>
<Button x:Name="ButtonSpice" Content="Spice" Click="ButtonSpice_Click" IsEnabled="False" Margin="5,0,0,0" RenderTransformOrigin="4.474,0.491" />
</StackPanel>
</StackPanel>
</Grid>

View File

@ -1,4 +1,5 @@
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text.Json;
@ -19,7 +20,7 @@ namespace Proxmox.SpiceLauncher;
public partial class MainWindow
{
private PveClient? _client;
private List<Settings> _settings;
private ObservableCollection<Settings> _settings;
private List<VmWrapper> _vms = new();
private readonly string _settingsFile;
@ -30,22 +31,13 @@ public partial class MainWindow
}
private async void MetroWindow_Initialized(object sender, EventArgs e)
{
List<Settings>? settings = null;
ObservableCollection<Settings>? settings = null;
if (File.Exists(_settingsFile))
{
await using var f = File.OpenRead(_settingsFile);
settings = await JsonSerializer.DeserializeAsync<List<Settings>>(f);
}
if (settings == null || settings.Count == 0)
{
settings =
[
new Settings()
{
Server = "192.168.0.190"
}
];
settings = await JsonSerializer.DeserializeAsync<ObservableCollection<Settings>>(f);
}
settings ??= [];
_settings = settings;
ComboProfiles.ItemsSource = _settings;
ComboProfiles.SelectedIndex = 0;
@ -54,13 +46,12 @@ public partial class MainWindow
private void ButtonSettings_Click(object sender, RoutedEventArgs e)
{
/*
var settings = new SettingsWindow
{
Owner = this
Owner = this,
Settings = _settings
};
settings.ShowDialog();
*/
}
private async Task DoEvent(Func<Task> e)
@ -105,6 +96,12 @@ public partial class MainWindow
throw new Exception("Login failed");
}
var nodes = await _client.GetHostAndIpAsync();
var current = nodes.FirstOrDefault(x => x.Value == _client.Host);
if (current.Key != null)
{
settings.Name = current.Key;
}
var vms = await _client.GetVmsAsync();
foreach (var vm in vms)
{

View File

@ -0,0 +1,50 @@
using System.Text.Json.Serialization;
namespace Proxmox.SpiceLauncher.Models;
public class Settings{
private string? _password;
private string? _passwordEnc;
public string? Server { get; set; }
public string? Name { get; set; }
public string? Username { get; set; }
[JsonIgnore]
public string? Password
{
get => _password;
set
{
_password = value;
_passwordEnc = value != null ? DPApi.EncryptStringToBase64(value) : null;
}
}
public string? PasswordEnc
{
get => _passwordEnc;
set
{
_passwordEnc = value;
try
{
_password = value == null ? null : DPApi.DecryptStringFromBase64(value);
}
catch (Exception)
{
_password = null;
_passwordEnc = null;
}
}
}
public override string ToString()
{
var server = (Username != null ? $"{Username}@" : null) + Server ?? "Unknown";
if (Name != null)
{
return $"{Name} ({server})";
}
return server;
}
}

View File

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@ -0,0 +1,26 @@
<mah:MetroWindow x:Class="Proxmox.SpiceLauncher.SettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:local="clr-namespace:Proxmox.SpiceLauncher"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
mc:Ignorable="d"
Title="Settings" Height="368" Width="625" ResizeMode="NoResize" WindowStartupLocation="CenterOwner"
SizeToContent="Height">
<mah:MetroWindow.Resources>
<Style BasedOn="{StaticResource MahApps.Styles.MetroHeader.Horizontal}" TargetType="mah:MetroHeader" />
</mah:MetroWindow.Resources>
<Grid>
<StackPanel Margin="10,10,10,10">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,0,10">
<Button x:Name="ButtonAdd" Content="{iconPacks:FontAwesome Kind=PlusSolid}" Click="ButtonAdd_Click"
Margin="0,0,5,0" />
<Button x:Name="ButtonDelete" Content="{iconPacks:FontAwesome Kind=MinusSolid}" Click="ButtonDelete_Click"
Margin="0,0,5,0" />
</StackPanel>
<ListBox Name="ListSettings"></ListBox>
</StackPanel>
</Grid>
</mah:MetroWindow>

View File

@ -0,0 +1,50 @@
using System.Collections.ObjectModel;
using Proxmox.SpiceLauncher.Models;
using System.Windows;
using MahApps.Metro.Controls.Dialogs;
using Proxmox.SpiceLauncher.Extensions;
namespace Proxmox.SpiceLauncher;
/// <summary>
/// Interaction logic for SettingsWindow.xaml
/// </summary>
public partial class SettingsWindow
{
private ObservableCollection<Settings> _settings;
public SettingsWindow()
{
InitializeComponent();
}
public ObservableCollection<Settings> Settings
{
get => _settings;
set
{
_settings = value;
ListSettings.ItemsSource = _settings;
}
}
private async void ButtonAdd_Click(object sender, RoutedEventArgs e)
{
var server = await this.ShowInputAsync("Proxmox Server", "Proxmox server IP or Hostname");
if (string.IsNullOrEmpty(server))
return;
Settings.Add(new Settings
{
Server = server
});
Settings.Sort((a, b) => string.Compare(a.ToString(), b.ToString(), StringComparison.Ordinal));
}
private void ButtonDelete_Click(object sender, RoutedEventArgs e)
{
if (ListSettings.SelectedItem is Settings settings)
{
_settings.Remove(settings);
}
}
}

View File

@ -1,37 +0,0 @@
using System.Text.Json.Serialization;
namespace Proxmox.SpiceLauncher.Models;
class Settings
{
private string _password;
private string _passwordEnc;
public string Server { get; set; }
public string Username { get; set; }
[JsonIgnore]
public string Password
{
get => _password;
set
{
_password = value;
_passwordEnc = DPApi.EncryptStringToBase64(value);
}
}
public string PasswordEnc
{
get => _passwordEnc;
set
{
_passwordEnc = value;
_password = DPApi.DecryptStringFromBase64(value);
}
}
public override string ToString()
{
return Server;
}
}