mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Migrate Program plugin settings to Avalonia and fix scroll issues
- Update ISettingProvider to support CreateSettingPanelAvalonia - Create native Avalonia settings UI for Program Plugin (ProgramSetting.axaml) - Implement ProgramSettingViewModel using CommunityToolkit.Mvvm - Update PluginsSettingsViewModel to detect and load native Avalonia settings - Replace ListBox with ItemsControl in PluginsSettingsPage to fix auto-scroll issues when expanding settings - Align Avalonia package versions (11.2.3) across projects to fix type load errors
This commit is contained in:
parent
fb9721c7f2
commit
dda587493b
13 changed files with 719 additions and 38 deletions
|
|
@ -7,7 +7,7 @@ using Flow.Launcher.Avalonia.Views.Controls;
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Windows.Controls;
|
||||
using AvaloniaControl = Avalonia.Controls.Control;
|
||||
|
||||
namespace Flow.Launcher.Avalonia.ViewModel.SettingPages;
|
||||
|
||||
|
|
@ -68,16 +68,47 @@ public partial class PluginItemViewModel : ObservableObject
|
|||
HasSettings = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (HasSettings && _settingProvider != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Console.WriteLine($"Checking Avalonia settings for {Name}");
|
||||
AvaloniaSettingControl = _settingProvider.CreateSettingPanelAvalonia();
|
||||
HasNativeAvaloniaSettings = AvaloniaSettingControl != null;
|
||||
System.Console.WriteLine($"Avalonia settings for {Name}: {HasNativeAvaloniaSettings}");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
System.Console.WriteLine($"Failed to create Avalonia settings for {Name}: {ex}");
|
||||
Flow.Launcher.Infrastructure.Logger.Log.Exception(nameof(PluginItemViewModel), $"Failed to create Avalonia settings for {Name}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hasSettings;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hasNativeAvaloniaSettings;
|
||||
|
||||
[ObservableProperty]
|
||||
private AvaloniaControl? _avaloniaSettingControl;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isExpanded;
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenSettings()
|
||||
{
|
||||
if (_settingProvider == null) return;
|
||||
|
||||
if (HasNativeAvaloniaSettings)
|
||||
{
|
||||
IsExpanded = !IsExpanded;
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Create the WPF settings panel on demand
|
||||
|
|
|
|||
|
|
@ -17,37 +17,50 @@
|
|||
Watermark="{i18n:Localize search}"
|
||||
Margin="0,0,0,20" />
|
||||
|
||||
<ListBox Grid.Row="2"
|
||||
ItemsSource="{Binding FilteredPlugins}"
|
||||
Background="Transparent"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:PluginItemViewModel">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" Margin="0,5">
|
||||
<Image Grid.Column="0" Source="{Binding IconPath}" Width="32" Height="32" Margin="0,0,15,0" VerticalAlignment="Center" />
|
||||
|
||||
<StackPanel Grid.Column="1" VerticalAlignment="Center">
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
|
||||
<TextBlock Text="{Binding Version}" Foreground="Gray" FontSize="12" VerticalAlignment="Bottom" />
|
||||
</StackPanel>
|
||||
<TextBlock Text="{Binding Description}" Foreground="Gray" FontSize="12" TextWrapping="Wrap" />
|
||||
<TextBlock Text="{Binding Author}" Foreground="LightGray" FontSize="11" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="10" VerticalAlignment="Center">
|
||||
<Button Command="{Binding OpenSettingsCommand}"
|
||||
IsVisible="{Binding HasSettings}"
|
||||
ToolTip.Tip="Settings">
|
||||
<ui:SymbolIcon Symbol="Settings" />
|
||||
</Button>
|
||||
<ScrollViewer Grid.Row="2">
|
||||
<ItemsControl ItemsSource="{Binding FilteredPlugins}"
|
||||
Background="Transparent">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:PluginItemViewModel">
|
||||
<StackPanel Spacing="5" Margin="0,0,10,0">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" Margin="0,5">
|
||||
<Image Grid.Column="0" Source="{Binding IconPath}" Width="32" Height="32" Margin="0,0,15,0" VerticalAlignment="Center" />
|
||||
|
||||
<ToggleSwitch IsChecked="{Binding !IsDisabled}"
|
||||
OnContent="" OffContent="" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<StackPanel Grid.Column="1" VerticalAlignment="Center">
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
|
||||
<TextBlock Text="{Binding Version}" Foreground="Gray" FontSize="12" VerticalAlignment="Bottom" />
|
||||
</StackPanel>
|
||||
<TextBlock Text="{Binding Description}" Foreground="Gray" FontSize="12" TextWrapping="Wrap" />
|
||||
<TextBlock Text="{Binding Author}" Foreground="LightGray" FontSize="11" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="10" VerticalAlignment="Center">
|
||||
<Button Command="{Binding OpenSettingsCommand}"
|
||||
IsVisible="{Binding HasSettings}"
|
||||
ToolTip.Tip="Settings">
|
||||
<ui:SymbolIcon Symbol="Settings" />
|
||||
</Button>
|
||||
|
||||
<ToggleSwitch IsChecked="{Binding !IsDisabled}"
|
||||
OnContent="" OffContent="" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Native Avalonia Settings Panel -->
|
||||
<Border IsVisible="{Binding IsExpanded}"
|
||||
Background="{DynamicResource SolidBackgroundFillColorBase}"
|
||||
BorderBrush="{DynamicResource ControlElevationBorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4"
|
||||
Padding="10"
|
||||
Margin="48,0,0,10">
|
||||
<ContentControl Content="{Binding AvaloniaSettingControl}" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
|
|
|||
|
|
@ -61,6 +61,26 @@
|
|||
"System.IO.Pipelines": "8.0.0"
|
||||
}
|
||||
},
|
||||
"Avalonia": {
|
||||
"type": "Transitive",
|
||||
"resolved": "11.2.3",
|
||||
"contentHash": "pD6woFAUfGcyEvMmrpctntU4jv4fT8752pfx1J5iRORVX3Ob0oQi8PWo0TXVaAJZiSfH0cdKTeKx0w0DzD0/mg==",
|
||||
"dependencies": {
|
||||
"Avalonia.BuildServices": "0.0.29",
|
||||
"Avalonia.Remote.Protocol": "11.2.3",
|
||||
"MicroCom.Runtime": "0.11.0"
|
||||
}
|
||||
},
|
||||
"Avalonia.BuildServices": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.0.29",
|
||||
"contentHash": "U4eJLQdoDNHXtEba7MZUCwrBErBTxFp6sUewXBOdAhU0Kwzwaa/EKFcYm8kpcysjzKtfB4S0S9n0uxKZFz/ikw=="
|
||||
},
|
||||
"Avalonia.Remote.Protocol": {
|
||||
"type": "Transitive",
|
||||
"resolved": "11.2.3",
|
||||
"contentHash": "6V0aNtld48WmO8tAlWwlRlUmXYcOWv+1eJUSl1ETF+1blUe5yhcSmuWarPprO0hDk8Ta6wGfdfcrnVl2gITYcA=="
|
||||
},
|
||||
"Ben.Demystifier": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.4.1",
|
||||
|
|
@ -127,6 +147,11 @@
|
|||
"resolved": "2.5.192",
|
||||
"contentHash": "jaJuwcgovWIZ8Zysdyf3b7b34/BrADw4v82GaEZymUhDd3ScMPrYd/cttekeDteJJPXseJxp04yTIcxiVUjTWg=="
|
||||
},
|
||||
"MicroCom.Runtime": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.11.0",
|
||||
"contentHash": "MEnrZ3UIiH40hjzMDsxrTyi8dtqB5ziv3iBeeU4bXsL/7NLSal9F1lZKpK+tfBRnUoDSdtcW3KufE4yhATOMCA=="
|
||||
},
|
||||
"Microsoft.NET.StringTools": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.6.3",
|
||||
|
|
@ -1161,7 +1186,7 @@
|
|||
"Ben.Demystifier": "[0.4.1, )",
|
||||
"BitFaster.Caching": "[2.5.4, )",
|
||||
"CommunityToolkit.Mvvm": "[8.4.0, )",
|
||||
"Flow.Launcher.Plugin": "[5.0.0, )",
|
||||
"Flow.Launcher.Plugin": "[5.1.0, )",
|
||||
"InputSimulator": "[1.0.4, )",
|
||||
"MemoryPack": "[1.21.4, )",
|
||||
"Microsoft.VisualStudio.Threading": "[17.14.15, )",
|
||||
|
|
@ -1176,6 +1201,7 @@
|
|||
"flow.launcher.plugin": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Avalonia": "[11.2.3, )",
|
||||
"JetBrains.Annotations": "[2025.2.2, )"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,6 +121,26 @@
|
|||
"resolved": "3.1.0.3",
|
||||
"contentHash": "VKcf8sUq/+LyY99WgLhOu7Q32ROEyR30/2xCCj9ADRi45wVC7kpXrYCf9vH1qirkmrIfpL8inoxAbrqAlfXxsQ=="
|
||||
},
|
||||
"Avalonia": {
|
||||
"type": "Transitive",
|
||||
"resolved": "11.2.3",
|
||||
"contentHash": "pD6woFAUfGcyEvMmrpctntU4jv4fT8752pfx1J5iRORVX3Ob0oQi8PWo0TXVaAJZiSfH0cdKTeKx0w0DzD0/mg==",
|
||||
"dependencies": {
|
||||
"Avalonia.BuildServices": "0.0.29",
|
||||
"Avalonia.Remote.Protocol": "11.2.3",
|
||||
"MicroCom.Runtime": "0.11.0"
|
||||
}
|
||||
},
|
||||
"Avalonia.BuildServices": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.0.29",
|
||||
"contentHash": "U4eJLQdoDNHXtEba7MZUCwrBErBTxFp6sUewXBOdAhU0Kwzwaa/EKFcYm8kpcysjzKtfB4S0S9n0uxKZFz/ikw=="
|
||||
},
|
||||
"Avalonia.Remote.Protocol": {
|
||||
"type": "Transitive",
|
||||
"resolved": "11.2.3",
|
||||
"contentHash": "6V0aNtld48WmO8tAlWwlRlUmXYcOWv+1eJUSl1ETF+1blUe5yhcSmuWarPprO0hDk8Ta6wGfdfcrnVl2gITYcA=="
|
||||
},
|
||||
"JetBrains.Annotations": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2025.2.2",
|
||||
|
|
@ -136,6 +156,11 @@
|
|||
"resolved": "1.21.4",
|
||||
"contentHash": "g14EsSS85yn0lHTi0J9ivqlZMf09A2iI51fmI+0KkzIzyCbWOBWPi5mdaY7YWmXprk12aYh9u/qfWHQUYthlwg=="
|
||||
},
|
||||
"MicroCom.Runtime": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.11.0",
|
||||
"contentHash": "MEnrZ3UIiH40hjzMDsxrTyi8dtqB5ziv3iBeeU4bXsL/7NLSal9F1lZKpK+tfBRnUoDSdtcW3KufE4yhATOMCA=="
|
||||
},
|
||||
"Microsoft.VisualStudio.Threading.Analyzers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.14.15",
|
||||
|
|
@ -190,6 +215,7 @@
|
|||
"flow.launcher.plugin": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Avalonia": "[11.2.3, )",
|
||||
"JetBrains.Annotations": "[2025.2.2, )"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0-windows</TargetFramework>
|
||||
|
|
@ -68,6 +68,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.2.3" />
|
||||
<PackageReference Include="Fody" Version="6.9.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Windows.Controls;
|
||||
using System.Windows.Controls;
|
||||
using AvaloniaControl = Avalonia.Controls.Control;
|
||||
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
|
|
@ -12,5 +13,12 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Control CreateSettingPanel();
|
||||
|
||||
/// <summary>
|
||||
/// Create settings panel control for Avalonia version
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
virtual AvaloniaControl CreateSettingPanelAvalonia() => null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,17 @@
|
|||
"version": 1,
|
||||
"dependencies": {
|
||||
"net9.0-windows7.0": {
|
||||
"Avalonia": {
|
||||
"type": "Direct",
|
||||
"requested": "[11.2.3, )",
|
||||
"resolved": "11.2.3",
|
||||
"contentHash": "pD6woFAUfGcyEvMmrpctntU4jv4fT8752pfx1J5iRORVX3Ob0oQi8PWo0TXVaAJZiSfH0cdKTeKx0w0DzD0/mg==",
|
||||
"dependencies": {
|
||||
"Avalonia.BuildServices": "0.0.29",
|
||||
"Avalonia.Remote.Protocol": "11.2.3",
|
||||
"MicroCom.Runtime": "0.11.0"
|
||||
}
|
||||
},
|
||||
"Fody": {
|
||||
"type": "Direct",
|
||||
"requested": "[6.9.3, )",
|
||||
|
|
@ -44,6 +55,21 @@
|
|||
"Fody": "6.6.4"
|
||||
}
|
||||
},
|
||||
"Avalonia.BuildServices": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.0.29",
|
||||
"contentHash": "U4eJLQdoDNHXtEba7MZUCwrBErBTxFp6sUewXBOdAhU0Kwzwaa/EKFcYm8kpcysjzKtfB4S0S9n0uxKZFz/ikw=="
|
||||
},
|
||||
"Avalonia.Remote.Protocol": {
|
||||
"type": "Transitive",
|
||||
"resolved": "11.2.3",
|
||||
"contentHash": "6V0aNtld48WmO8tAlWwlRlUmXYcOWv+1eJUSl1ETF+1blUe5yhcSmuWarPprO0hDk8Ta6wGfdfcrnVl2gITYcA=="
|
||||
},
|
||||
"MicroCom.Runtime": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.11.0",
|
||||
"contentHash": "MEnrZ3UIiH40hjzMDsxrTyi8dtqB5ziv3iBeeU4bXsL/7NLSal9F1lZKpK+tfBRnUoDSdtcW3KufE4yhATOMCA=="
|
||||
},
|
||||
"Microsoft.Build.Tasks.Git": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
|
|
|
|||
|
|
@ -163,6 +163,26 @@
|
|||
"resolved": "6.3.0.90",
|
||||
"contentHash": "WVTb5MxwGqKdeasd3nG5udlV4t6OpvkFanziwI133K0/QJ5FvZmfzRQgpAjGTJhQfIA8GP7AzKQ3sTY9JOFk8Q=="
|
||||
},
|
||||
"Avalonia": {
|
||||
"type": "Transitive",
|
||||
"resolved": "11.2.3",
|
||||
"contentHash": "pD6woFAUfGcyEvMmrpctntU4jv4fT8752pfx1J5iRORVX3Ob0oQi8PWo0TXVaAJZiSfH0cdKTeKx0w0DzD0/mg==",
|
||||
"dependencies": {
|
||||
"Avalonia.BuildServices": "0.0.29",
|
||||
"Avalonia.Remote.Protocol": "11.2.3",
|
||||
"MicroCom.Runtime": "0.11.0"
|
||||
}
|
||||
},
|
||||
"Avalonia.BuildServices": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.0.29",
|
||||
"contentHash": "U4eJLQdoDNHXtEba7MZUCwrBErBTxFp6sUewXBOdAhU0Kwzwaa/EKFcYm8kpcysjzKtfB4S0S9n0uxKZFz/ikw=="
|
||||
},
|
||||
"Avalonia.Remote.Protocol": {
|
||||
"type": "Transitive",
|
||||
"resolved": "11.2.3",
|
||||
"contentHash": "6V0aNtld48WmO8tAlWwlRlUmXYcOWv+1eJUSl1ETF+1blUe5yhcSmuWarPprO0hDk8Ta6wGfdfcrnVl2gITYcA=="
|
||||
},
|
||||
"Ben.Demystifier": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.4.1",
|
||||
|
|
@ -252,6 +272,11 @@
|
|||
"resolved": "3.4.5",
|
||||
"contentHash": "R/d2VSdbRQHDWfPf5sjvMOWrWvxh/CswdMDKODppHTjsDLIkLQbQrnjmtAaVqu0qgUf8KFlVzEfxy3GIVoCK9g=="
|
||||
},
|
||||
"MicroCom.Runtime": {
|
||||
"type": "Transitive",
|
||||
"resolved": "0.11.0",
|
||||
"contentHash": "MEnrZ3UIiH40hjzMDsxrTyi8dtqB5ziv3iBeeU4bXsL/7NLSal9F1lZKpK+tfBRnUoDSdtcW3KufE4yhATOMCA=="
|
||||
},
|
||||
"Microsoft.Extensions.Configuration": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.9",
|
||||
|
|
@ -1599,7 +1624,7 @@
|
|||
"Droplex": "[1.7.0, )",
|
||||
"FSharp.Core": "[9.0.303, )",
|
||||
"Flow.Launcher.Infrastructure": "[1.0.0, )",
|
||||
"Flow.Launcher.Plugin": "[5.0.0, )",
|
||||
"Flow.Launcher.Plugin": "[5.1.0, )",
|
||||
"Meziantou.Framework.Win32.Jobs": "[3.4.5, )",
|
||||
"Microsoft.IO.RecyclableMemoryStream": "[3.0.1, )",
|
||||
"SemanticVersioning": "[3.0.0, )",
|
||||
|
|
@ -1613,7 +1638,7 @@
|
|||
"Ben.Demystifier": "[0.4.1, )",
|
||||
"BitFaster.Caching": "[2.5.4, )",
|
||||
"CommunityToolkit.Mvvm": "[8.4.0, )",
|
||||
"Flow.Launcher.Plugin": "[5.0.0, )",
|
||||
"Flow.Launcher.Plugin": "[5.1.0, )",
|
||||
"InputSimulator": "[1.0.4, )",
|
||||
"MemoryPack": "[1.21.4, )",
|
||||
"Microsoft.VisualStudio.Threading": "[17.14.15, )",
|
||||
|
|
@ -1628,6 +1653,7 @@
|
|||
"flow.launcher.plugin": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Avalonia": "[11.2.3, )",
|
||||
"JetBrains.Annotations": "[2025.2.2, )"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
|
|
@ -62,6 +62,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.2.3" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="FluentAvaloniaUI" Version="2.2.0" />
|
||||
<PackageReference Include="ini-parser" Version="2.5.2" />
|
||||
<PackageReference Include="MemoryPack" Version="1.21.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.9" />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
|
@ -12,11 +12,13 @@ using Flow.Launcher.Plugin.Program.Views.Models;
|
|||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Path = System.IO.Path;
|
||||
using AvaloniaControl = Avalonia.Controls.Control;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Program
|
||||
{
|
||||
public class Main : ISettingProvider, IAsyncPlugin, IPluginI18n, IContextMenu, IAsyncReloadable, IDisposable
|
||||
{
|
||||
|
||||
private static readonly string ClassName = nameof(Main);
|
||||
|
||||
private const string Win32CacheName = "Win32";
|
||||
|
|
@ -417,6 +419,14 @@ namespace Flow.Launcher.Plugin.Program
|
|||
return new ProgramSetting(Context, _settings);
|
||||
}
|
||||
|
||||
public AvaloniaControl CreateSettingPanelAvalonia()
|
||||
{
|
||||
System.Console.WriteLine("Program plugin: CreateSettingPanelAvalonia called!");
|
||||
Context.API.LogInfo(ClassName, "Creating Avalonia setting panel");
|
||||
return new Views.Avalonia.ProgramSetting(Context, _settings);
|
||||
}
|
||||
|
||||
|
||||
public string GetTranslatedPluginTitle()
|
||||
{
|
||||
return Context.API.GetTranslation("flowlauncher_plugin_program_plugin_name");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,337 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Flow.Launcher.Plugin.Program.Views.Models;
|
||||
using Flow.Launcher.Plugin.Program.Views.Commands;
|
||||
using Flow.Launcher.Plugin.Program.Programs;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using Flow.Launcher.Plugin.Program.Views;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Media;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Program.ViewModels;
|
||||
|
||||
public partial class ProgramSettingViewModel : ObservableObject
|
||||
{
|
||||
public static IValueConverter EnabledToColorConverter { get; } = new FuncValueConverter<bool, IBrush>(enabled =>
|
||||
enabled ? Brushes.Green : Brushes.Red);
|
||||
|
||||
public static IValueConverter IsNotNullConverter { get; } = new FuncValueConverter<object?, bool>(obj => obj != null);
|
||||
|
||||
private readonly PluginInitContext _context;
|
||||
private readonly Settings _settings;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _enableUWP;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _enableStartMenuSource;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _enableRegistrySource;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _enablePATHSource;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hideAppsPath;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hideUninstallers;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _enableDescription;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hideDuplicatedWindowsApp;
|
||||
|
||||
public bool ShowUWPCheckbox => UWPPackage.SupportUWP();
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<ProgramSource> _programSources = new();
|
||||
|
||||
[ObservableProperty]
|
||||
private ProgramSource? _selectedProgramSource;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isIndexing;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _customSuffixes;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _customProtocols;
|
||||
|
||||
public ProgramSettingViewModel(PluginInitContext context, Settings settings)
|
||||
{
|
||||
_context = context;
|
||||
_settings = settings;
|
||||
|
||||
_enableUWP = _settings.EnableUWP;
|
||||
_enableStartMenuSource = _settings.EnableStartMenuSource;
|
||||
_enableRegistrySource = _settings.EnableRegistrySource;
|
||||
_enablePATHSource = _settings.EnablePathSource;
|
||||
_hideAppsPath = _settings.HideAppsPath;
|
||||
_hideUninstallers = _settings.HideUninstallers;
|
||||
_enableDescription = _settings.EnableDescription;
|
||||
_hideDuplicatedWindowsApp = _settings.HideDuplicatedWindowsApp;
|
||||
|
||||
_customSuffixes = string.Join(Settings.SuffixSeparator, _settings.CustomSuffixes);
|
||||
_customProtocols = string.Join(Settings.SuffixSeparator, _settings.CustomProtocols);
|
||||
|
||||
LoadProgramSources();
|
||||
}
|
||||
|
||||
partial void OnCustomSuffixesChanged(string value)
|
||||
{
|
||||
_settings.CustomSuffixes = value.Split(Settings.SuffixSeparator, StringSplitOptions.RemoveEmptyEntries);
|
||||
Main.ResetCache();
|
||||
}
|
||||
|
||||
partial void OnCustomProtocolsChanged(string value)
|
||||
{
|
||||
_settings.CustomProtocols = value.Split(Settings.SuffixSeparator, StringSplitOptions.RemoveEmptyEntries);
|
||||
Main.ResetCache();
|
||||
}
|
||||
|
||||
public bool AppRefMS
|
||||
{
|
||||
get => _settings.BuiltinSuffixesStatus["appref-ms"];
|
||||
set
|
||||
{
|
||||
_settings.BuiltinSuffixesStatus["appref-ms"] = value;
|
||||
OnPropertyChanged();
|
||||
Main.ResetCache();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Exe
|
||||
{
|
||||
get => _settings.BuiltinSuffixesStatus["exe"];
|
||||
set
|
||||
{
|
||||
_settings.BuiltinSuffixesStatus["exe"] = value;
|
||||
OnPropertyChanged();
|
||||
Main.ResetCache();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Lnk
|
||||
{
|
||||
get => _settings.BuiltinSuffixesStatus["lnk"];
|
||||
set
|
||||
{
|
||||
_settings.BuiltinSuffixesStatus["lnk"] = value;
|
||||
OnPropertyChanged();
|
||||
Main.ResetCache();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Steam
|
||||
{
|
||||
get => _settings.BuiltinProtocolsStatus["steam"];
|
||||
set
|
||||
{
|
||||
_settings.BuiltinProtocolsStatus["steam"] = value;
|
||||
OnPropertyChanged();
|
||||
Main.ResetCache();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Epic
|
||||
{
|
||||
get => _settings.BuiltinProtocolsStatus["epic"];
|
||||
set
|
||||
{
|
||||
_settings.BuiltinProtocolsStatus["epic"] = value;
|
||||
OnPropertyChanged();
|
||||
Main.ResetCache();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Http
|
||||
{
|
||||
get => _settings.BuiltinProtocolsStatus["http"];
|
||||
set
|
||||
{
|
||||
_settings.BuiltinProtocolsStatus["http"] = value;
|
||||
OnPropertyChanged();
|
||||
Main.ResetCache();
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseCustomSuffixes
|
||||
{
|
||||
get => _settings.UseCustomSuffixes;
|
||||
set
|
||||
{
|
||||
_settings.UseCustomSuffixes = value;
|
||||
OnPropertyChanged();
|
||||
Main.ResetCache();
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseCustomProtocols
|
||||
{
|
||||
get => _settings.UseCustomProtocols;
|
||||
set
|
||||
{
|
||||
_settings.UseCustomProtocols = value;
|
||||
OnPropertyChanged();
|
||||
Main.ResetCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void LoadProgramSources()
|
||||
{
|
||||
// For Avalonia, we want to maintain the same display list if possible,
|
||||
// but for now let's just use a fresh one from settings
|
||||
var sources = ProgramSettingDisplay.LoadProgramSources();
|
||||
ProgramSources = new ObservableCollection<ProgramSource>(sources);
|
||||
|
||||
// We need to set the static list for ProgramSettingDisplay to work
|
||||
ProgramSetting.ProgramSettingDisplayList = sources;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task Reindex()
|
||||
{
|
||||
IsIndexing = true;
|
||||
await Main.IndexProgramsAsync();
|
||||
IsIndexing = false;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task LoadAllPrograms()
|
||||
{
|
||||
await ProgramSettingDisplay.DisplayAllProgramsAsync();
|
||||
// Refresh the observable collection
|
||||
ProgramSources = new ObservableCollection<ProgramSource>(ProgramSetting.ProgramSettingDisplayList);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ToggleStatus()
|
||||
{
|
||||
if (SelectedProgramSource == null) return;
|
||||
|
||||
var status = !SelectedProgramSource.Enabled;
|
||||
await ProgramSettingDisplay.SetProgramSourcesStatusAsync(new List<ProgramSource> { SelectedProgramSource }, status);
|
||||
|
||||
if (status)
|
||||
ProgramSettingDisplay.RemoveDisabledFromSettings();
|
||||
else
|
||||
ProgramSettingDisplay.StoreDisabledInSettings();
|
||||
|
||||
if (await new List<ProgramSource> { SelectedProgramSource }.IsReindexRequiredAsync())
|
||||
await Reindex();
|
||||
|
||||
// Trigger UI update for the item
|
||||
OnPropertyChanged(nameof(ProgramSources));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task DeleteSource()
|
||||
{
|
||||
if (SelectedProgramSource == null) return;
|
||||
|
||||
// Check if it's user added
|
||||
if (!_settings.ProgramSources.Any(x => x.UniqueIdentifier == SelectedProgramSource.UniqueIdentifier))
|
||||
{
|
||||
_context.API.ShowMsgBox(_context.API.GetTranslation("flowlauncher_plugin_program_delete_program_source_select_user_added"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_context.API.ShowMsgBox(_context.API.GetTranslation("flowlauncher_plugin_program_delete_program_source"),
|
||||
string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var toDelete = SelectedProgramSource;
|
||||
_settings.ProgramSources.RemoveAll(x => x.UniqueIdentifier == toDelete.UniqueIdentifier);
|
||||
ProgramSetting.ProgramSettingDisplayList.Remove(toDelete);
|
||||
ProgramSources.Remove(toDelete);
|
||||
|
||||
if (await new List<ProgramSource> { toDelete }.IsReindexRequiredAsync())
|
||||
await Reindex();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task AddSource()
|
||||
{
|
||||
var dialog = new System.Windows.Forms.FolderBrowserDialog();
|
||||
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
||||
{
|
||||
var path = dialog.SelectedPath;
|
||||
if (ProgramSources.Any(x => x.UniqueIdentifier.Equals(path, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
_context.API.ShowMsgBox(_context.API.GetTranslation("flowlauncher_plugin_program_duplicate_program_source"));
|
||||
return;
|
||||
}
|
||||
|
||||
var source = new ProgramSource(path, true);
|
||||
_settings.ProgramSources.Add(source);
|
||||
ProgramSetting.ProgramSettingDisplayList.Add(source);
|
||||
ProgramSources.Add(source);
|
||||
|
||||
await Reindex();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
partial void OnEnableUWPChanged(bool value)
|
||||
{
|
||||
_settings.EnableUWP = value;
|
||||
_ = Reindex();
|
||||
}
|
||||
|
||||
partial void OnEnableStartMenuSourceChanged(bool value)
|
||||
{
|
||||
_settings.EnableStartMenuSource = value;
|
||||
_ = Reindex();
|
||||
}
|
||||
|
||||
partial void OnEnableRegistrySourceChanged(bool value)
|
||||
{
|
||||
_settings.EnableRegistrySource = value;
|
||||
_ = Reindex();
|
||||
}
|
||||
|
||||
partial void OnEnablePATHSourceChanged(bool value)
|
||||
{
|
||||
_settings.EnablePathSource = value;
|
||||
_ = Reindex();
|
||||
}
|
||||
|
||||
partial void OnHideAppsPathChanged(bool value)
|
||||
{
|
||||
_settings.HideAppsPath = value;
|
||||
Main.ResetCache();
|
||||
}
|
||||
|
||||
partial void OnHideUninstallersChanged(bool value)
|
||||
{
|
||||
_settings.HideUninstallers = value;
|
||||
Main.ResetCache();
|
||||
}
|
||||
|
||||
partial void OnEnableDescriptionChanged(bool value)
|
||||
{
|
||||
_settings.EnableDescription = value;
|
||||
Main.ResetCache();
|
||||
}
|
||||
|
||||
partial void OnHideDuplicatedWindowsAppChanged(bool value)
|
||||
{
|
||||
_settings.HideDuplicatedWindowsApp = value;
|
||||
Main.ResetCache();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
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:ui="using:FluentAvalonia.UI.Controls"
|
||||
xmlns:vm="using:Flow.Launcher.Plugin.Program.ViewModels"
|
||||
xmlns:models="using:Flow.Launcher.Plugin.Program.Views.Models"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
|
||||
x:Class="Flow.Launcher.Plugin.Program.Views.Avalonia.ProgramSetting"
|
||||
x:DataType="vm:ProgramSettingViewModel">
|
||||
<StackPanel Spacing="10" Margin="0,0,10,0">
|
||||
<ui:SettingsExpander Header="Index Sources"
|
||||
IconSource="Library"
|
||||
Description="Configure which sources to scan for programs">
|
||||
<ui:SettingsExpander.Items>
|
||||
<ui:SettingsExpanderItem Content="Start Menu">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding EnableStartMenuSource}" OnContent="" OffContent="" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
<ui:SettingsExpanderItem Content="Registry">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding EnableRegistrySource}" OnContent="" OffContent="" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
<ui:SettingsExpanderItem Content="PATH">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding EnablePATHSource}" OnContent="" OffContent="" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
<ui:SettingsExpanderItem Content="UWP Apps" IsVisible="{Binding ShowUWPCheckbox}">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding EnableUWP}" OnContent="" OffContent="" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander.Items>
|
||||
</ui:SettingsExpander>
|
||||
|
||||
<ui:SettingsExpander Header="Program Options"
|
||||
IconSource="Settings"
|
||||
Description="General behavior for program search">
|
||||
<ui:SettingsExpander.Items>
|
||||
<ui:SettingsExpanderItem Content="Hide App Path">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding HideAppsPath}" OnContent="" OffContent="" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
<ui:SettingsExpanderItem Content="Hide Uninstallers">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding HideUninstallers}" OnContent="" OffContent="" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
<ui:SettingsExpanderItem Content="Enable Description">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding EnableDescription}" OnContent="" OffContent="" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
<ui:SettingsExpanderItem Content="Hide Duplicated Windows App">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch IsChecked="{Binding HideDuplicatedWindowsApp}" OnContent="" OffContent="" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander.Items>
|
||||
</ui:SettingsExpander>
|
||||
|
||||
<ui:SettingsExpander Header="Advanced Options"
|
||||
IconSource="Repair"
|
||||
Description="Configure file suffixes and protocols">
|
||||
<ui:SettingsExpander.Items>
|
||||
<ui:SettingsExpanderItem Content="Executable Suffixes">
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<CheckBox Content="appref-ms" IsChecked="{Binding AppRefMS}" />
|
||||
<CheckBox Content="exe" IsChecked="{Binding Exe}" />
|
||||
<CheckBox Content="lnk" IsChecked="{Binding Lnk}" />
|
||||
</StackPanel>
|
||||
</ui:SettingsExpanderItem>
|
||||
<ui:SettingsExpanderItem Content="Custom Suffixes">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<CheckBox IsChecked="{Binding UseCustomSuffixes}" />
|
||||
<TextBox Text="{Binding CustomSuffixes}" Width="200" IsEnabled="{Binding UseCustomSuffixes}" Watermark="e.g. py;bat" />
|
||||
</StackPanel>
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
<ui:SettingsExpanderItem Content="Protocols">
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<CheckBox Content="Steam" IsChecked="{Binding Steam}" />
|
||||
<CheckBox Content="Epic" IsChecked="{Binding Epic}" />
|
||||
<CheckBox Content="Http" IsChecked="{Binding Http}" />
|
||||
</StackPanel>
|
||||
</ui:SettingsExpanderItem>
|
||||
<ui:SettingsExpanderItem Content="Custom Protocols">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<CheckBox IsChecked="{Binding UseCustomProtocols}" />
|
||||
<TextBox Text="{Binding CustomProtocols}" Width="200" IsEnabled="{Binding UseCustomProtocols}" Watermark="e.g. obsidian;vscode" />
|
||||
</StackPanel>
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander.Items>
|
||||
</ui:SettingsExpander>
|
||||
|
||||
<ui:SettingsExpander Header="Program Sources"
|
||||
IconSource="Folder"
|
||||
Description="Manage custom indexing locations">
|
||||
<ui:SettingsExpander.Footer>
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<Button Content="Reindex" Command="{Binding ReindexCommand}" />
|
||||
<Button Content="Load All" Command="{Binding LoadAllProgramsCommand}" />
|
||||
<ProgressBar IsIndeterminate="True" IsVisible="{Binding IsIndexing}" VerticalAlignment="Center" Width="80" />
|
||||
</StackPanel>
|
||||
</ui:SettingsExpander.Footer>
|
||||
<ui:SettingsExpander.Items>
|
||||
<ui:SettingsExpanderItem>
|
||||
<Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,*" MinHeight="250">
|
||||
<ListBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
|
||||
ItemsSource="{Binding ProgramSources}"
|
||||
SelectedItem="{Binding SelectedProgramSource}"
|
||||
Background="{DynamicResource CardBackgroundFillColorDefault}"
|
||||
BorderBrush="{DynamicResource ControlStrokeColorDefault}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:ProgramSource">
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0" VerticalAlignment="Center" Margin="5">
|
||||
<TextBlock Text="{Binding Name}" FontWeight="Medium" />
|
||||
<TextBlock Text="{Binding Location}" FontSize="12" Foreground="Gray" TextTrimming="CharacterEllipsis" />
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{Binding Enabled}"
|
||||
VerticalAlignment="Center"
|
||||
Margin="10,0"
|
||||
Foreground="{Binding Enabled, Converter={x:Static vm:ProgramSettingViewModel.EnabledToColorConverter}}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
|
||||
<StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal" Spacing="5" HorizontalAlignment="Right" Margin="0,0,0,5">
|
||||
<Button Content="Add" Command="{Binding AddSourceCommand}" />
|
||||
<Button Content="Toggle Status" Command="{Binding ToggleStatusCommand}" IsEnabled="{Binding SelectedProgramSource, Converter={x:Static vm:ProgramSettingViewModel.IsNotNullConverter}}" />
|
||||
<Button Content="Delete" Command="{Binding DeleteSourceCommand}" IsEnabled="{Binding SelectedProgramSource, Converter={x:Static vm:ProgramSettingViewModel.IsNotNullConverter}}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander.Items>
|
||||
</ui:SettingsExpander>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Flow.Launcher.Plugin.Program.ViewModels;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Program.Views.Avalonia;
|
||||
|
||||
public partial class ProgramSetting : UserControl
|
||||
{
|
||||
public ProgramSetting()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public ProgramSetting(PluginInitContext context, Settings settings)
|
||||
{
|
||||
DataContext = new ProgramSettingViewModel(context, settings);
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue