Skip to content

Commit f7d33ca

Browse files
committed
Added installOptions to the packages details
1 parent f9f28fc commit f7d33ca

7 files changed

Lines changed: 468 additions & 411 deletions

File tree

src/UniGetUI.Avalonia/ViewModels/DialogPages/InstallOptionsViewModel.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,10 @@ public InstallOptionsViewModel(IPackage package, OperationType operation, Instal
305305
}
306306

307307
// ── Commands ──────────────────────────────────────────────────────────────
308+
309+
/// <summary>Captures the current UI state into the options object without closing.</summary>
310+
public void ApplyChanges() => ApplyToOptions();
311+
308312
[RelayCommand]
309313
private void Save()
310314
{

src/UniGetUI.Avalonia/Views/DialogPages/InstallOptionsControl.axaml

Lines changed: 362 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using Avalonia.Controls;
2+
using Avalonia.Input;
3+
using Avalonia.Interactivity;
4+
using Avalonia.Platform.Storage;
5+
using UniGetUI.Avalonia.ViewModels;
6+
7+
namespace UniGetUI.Avalonia.Views.DialogPages;
8+
9+
public partial class InstallOptionsControl : UserControl
10+
{
11+
private InstallOptionsViewModel ViewModel => (InstallOptionsViewModel)DataContext!;
12+
13+
public InstallOptionsControl()
14+
{
15+
InitializeComponent();
16+
}
17+
18+
public void FocusProfileSelector() => ProfileSelectorComboBox.Focus();
19+
20+
private async void SelectDir_Click(object? sender, RoutedEventArgs e)
21+
{
22+
var topLevel = TopLevel.GetTopLevel(this);
23+
if (topLevel is null) return;
24+
var results = await topLevel.StorageProvider.OpenFolderPickerAsync(
25+
new FolderPickerOpenOptions { AllowMultiple = false });
26+
if (results is [{ } folder])
27+
ViewModel.LocationText = folder.TryGetLocalPath() ?? folder.Name;
28+
}
29+
30+
private void KillProcessBox_KeyDown(object? sender, KeyEventArgs e)
31+
{
32+
if (e.Key is Key.Return or Key.Enter or Key.OemComma)
33+
ViewModel.AddKillProcessCommand.Execute(null);
34+
}
35+
}

src/UniGetUI.Avalonia/Views/DialogPages/InstallOptionsWindow.axaml

Lines changed: 6 additions & 354 deletions
Large diffs are not rendered by default.
Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
using Avalonia.Controls;
2-
using Avalonia.Input;
3-
using Avalonia.Interactivity;
4-
using Avalonia.Platform.Storage;
52
using Avalonia.Threading;
63
using UniGetUI.Avalonia.ViewModels;
74
using UniGetUI.PackageEngine.Enums;
@@ -26,21 +23,6 @@ public InstallOptionsWindow(IPackage package, OperationType operation, InstallOp
2623
protected override void OnOpened(EventArgs e)
2724
{
2825
base.OnOpened(e);
29-
Dispatcher.UIThread.Post(() => ProfileSelectorComboBox.Focus(), DispatcherPriority.Background);
30-
}
31-
32-
private async void SelectDir_Click(object? sender, RoutedEventArgs e)
33-
{
34-
var results = await StorageProvider.OpenFolderPickerAsync(
35-
new FolderPickerOpenOptions { AllowMultiple = false });
36-
if (results is [{ } folder])
37-
((InstallOptionsViewModel)DataContext!).LocationText =
38-
folder.TryGetLocalPath() ?? folder.Name;
39-
}
40-
41-
private void KillProcessBox_KeyDown(object? sender, KeyEventArgs e)
42-
{
43-
if (e.Key is Key.Return or Key.Enter or Key.OemComma)
44-
((InstallOptionsViewModel)DataContext!).AddKillProcessCommand.Execute(null);
26+
Dispatcher.UIThread.Post(() => OptionsControl.FocusProfileSelector(), DispatcherPriority.Background);
4527
}
4628
}

src/UniGetUI.Avalonia/Views/DialogPages/PackageDetailsWindow.axaml

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -163,39 +163,53 @@
163163

164164
<!-- ── Actions ── -->
165165
<Border Background="{DynamicResource SettingsCardBackground}" CornerRadius="8" Padding="16">
166-
<StackPanel Spacing="8">
167-
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="4">
168-
<Button x:Name="MainActionButton"
169-
Content="{Binding MainActionLabel}"
170-
automation:AutomationProperties.Name="{Binding MainActionLabel}"
171-
HorizontalAlignment="Stretch"
172-
HorizontalContentAlignment="Center"
173-
Classes="accent"
174-
Padding="16,8"/>
175-
<Button x:Name="ActionVariantsButton"
176-
Grid.Column="1"
177-
Content=""
166+
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="4">
167+
<Button x:Name="MainActionButton"
168+
Content="{Binding MainActionLabel}"
178169
automation:AutomationProperties.Name="{Binding MainActionLabel}"
179-
automation:AutomationProperties.HelpText="{Binding HeaderDetails}"
180-
Width="30"
181-
Height="30"
182-
FontSize="30"
183-
HorizontalContentAlignment="Center"
184-
Padding="0"/>
185-
</Grid>
186-
<Button x:Name="InstallOptionsButton"
187170
HorizontalAlignment="Stretch"
188171
HorizontalContentAlignment="Center"
189-
Padding="16,6">
190-
<StackPanel Orientation="Horizontal" Spacing="8">
191-
<controls:SvgIcon Path="avares://UniGetUI.Avalonia/Assets/Symbols/options.svg"
192-
Width="14" Height="14" VerticalAlignment="Center"/>
193-
<TextBlock Text="{t:Translate Installation options}" VerticalAlignment="Center"/>
194-
</StackPanel>
195-
</Button>
196-
</StackPanel>
172+
Classes="accent"
173+
Padding="16,8"/>
174+
<Button x:Name="ActionVariantsButton"
175+
Grid.Column="1"
176+
Content=""
177+
automation:AutomationProperties.Name="{Binding MainActionLabel}"
178+
automation:AutomationProperties.HelpText="{Binding HeaderDetails}"
179+
Width="30"
180+
Height="30"
181+
FontSize="30"
182+
HorizontalContentAlignment="Center"
183+
Padding="0"/>
184+
</Grid>
197185
</Border>
198186

187+
<!-- ── Installation options (inline expander) ── -->
188+
<Expander HorizontalAlignment="Stretch"
189+
HorizontalContentAlignment="Stretch"
190+
IsExpanded="False"
191+
CornerRadius="8"
192+
Padding="4"
193+
Background="{DynamicResource SettingsCardBackground}">
194+
<Expander.Header>
195+
<StackPanel Orientation="Horizontal" Spacing="8">
196+
<controls:SvgIcon Path="avares://UniGetUI.Avalonia/Assets/Symbols/options.svg"
197+
Width="16" Height="16" VerticalAlignment="Center"/>
198+
<TextBlock Text="{t:Translate Installation options}"
199+
FontWeight="SemiBold"
200+
VerticalAlignment="Center"/>
201+
</StackPanel>
202+
</Expander.Header>
203+
<StackPanel Margin="16,8" Spacing="8">
204+
<ContentControl x:Name="InstallOptionsHolder"/>
205+
<Button x:Name="InstallOptionsSaveButton"
206+
Content="{t:Translate Save}"
207+
Classes="accent"
208+
HorizontalAlignment="Right"
209+
Padding="16,6"/>
210+
</StackPanel>
211+
</Expander>
212+
199213
<!-- ── Screenshots ── -->
200214
<Border Background="{DynamicResource SettingsCardBackground}"
201215
CornerRadius="8" Padding="8"

src/UniGetUI.Avalonia/Views/DialogPages/PackageDetailsWindow.axaml.cs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using UniGetUI.PackageEngine.Interfaces;
99
using UniGetUI.PackageEngine.Operations;
1010
using UniGetUI.PackageEngine.PackageClasses;
11+
using UniGetUI.PackageEngine.Serializable;
1112
using UniGetUI.PackageOperations;
1213

1314
namespace UniGetUI.Avalonia.Views;
@@ -21,6 +22,8 @@ public partial class PackageDetailsWindow : Window
2122
public bool ShouldProceedWithOperation { get; private set; }
2223

2324
private readonly PackageDetailsViewModel _vm;
25+
private InstallOptionsViewModel? _installVm;
26+
private InstallOptions? _installOpts;
2427

2528
public PackageDetailsWindow(IPackage package, OperationType operation)
2629
{
@@ -32,15 +35,28 @@ public PackageDetailsWindow(IPackage package, OperationType operation)
3235

3336
MainActionButton.Click += (_, _) => OnMainAction();
3437
ActionVariantsButton.Flyout = BuildActionFlyout();
35-
InstallOptionsButton.Click += (_, _) => _ = OnInstallOptionsAsync();
38+
InstallOptionsSaveButton.Click += (_, _) => _ = SaveInstallOptionsAsync();
3639
}
3740

38-
protected override void OnOpened(EventArgs e)
41+
protected override async void OnOpened(EventArgs e)
3942
{
4043
base.OnOpened(e);
4144
Dispatcher.UIThread.Post(() => MainActionButton.Focus(), DispatcherPriority.Background);
4245
_ = _vm.LoadDetailsAsync();
4346
TelemetryHandler.PackageDetails(_vm.Package, _vm.OperationRole.ToString());
47+
48+
_installOpts = await InstallOptionsFactory.LoadForPackageAsync(_vm.Package);
49+
_installVm = new InstallOptionsViewModel(_vm.Package, _vm.OperationRole, _installOpts);
50+
var embed = new InstallOptionsControl();
51+
embed.DataContext = _installVm;
52+
InstallOptionsHolder.Content = embed;
53+
}
54+
55+
private async Task SaveInstallOptionsAsync()
56+
{
57+
if (_installVm is null || _installOpts is null) return;
58+
_installVm.ApplyChanges();
59+
await InstallOptionsFactory.SaveForPackageAsync(_installOpts, _vm.Package);
4460
}
4561

4662
private MenuFlyout BuildActionFlyout()
@@ -85,14 +101,6 @@ private MenuFlyout BuildActionFlyout()
85101

86102
// ── Action handlers ────────────────────────────────────────────────────────
87103

88-
private async Task OnInstallOptionsAsync()
89-
{
90-
var opts = await InstallOptionsFactory.LoadForPackageAsync(_vm.Package);
91-
var dialog = new InstallOptionsWindow(_vm.Package, _vm.OperationRole, opts);
92-
await dialog.ShowDialog(this);
93-
await InstallOptionsFactory.SaveForPackageAsync(opts, _vm.Package);
94-
}
95-
96104
private void OnMainAction()
97105
{
98106
ShouldProceedWithOperation = true;

0 commit comments

Comments
 (0)