From ecc5c70799b78be3a9f834f0d39d534ca106bea7 Mon Sep 17 00:00:00 2001 From: FN-FAL113 Date: Sat, 20 Jun 2026 13:07:57 +0800 Subject: [PATCH 1/4] feat: measure packet loss and QOL improvements - Introduce packet-loss metrics, colored ping/packet-loss displays, additional sorting, and relay probing logic. = Adds PacketLossComparer, PingColorConverter and PacketLossColorConverter. - Dedicated DataGrid style and include it in App.axaml. - ServerModel.PingServer was refactored to select the best relay, probe it multiple times, and populate Ping, PacketLoss and Status accordingly. - MainWindow updated to show colored Ping and Packet Loss template columns, wire header click handlers to custom comparers, and adjust various UI/layout properties. - MainViewModel code chores - Project/Program updates bump version to 1.1.0 - Upgrade Avalonia to latest v12 build and related packages, switch icon package - Tweak resource helper to support loading resources like images in avalonia design previewer. - Minor cleanup: remove obsolete Avalonia data-annotation removal helper and simplify window chrome/title bar code in several user windows. --- ServerPickerX/App.axaml | 1 + ServerPickerX/App.axaml.cs | 16 +---- ServerPickerX/Comparers/PacketLossComparer.cs | 35 ++++++++++ .../Converters/PacketLossColorConverter.cs | 27 ++++++++ .../Converters/PingColorConverter.cs | 29 ++++++++ ServerPickerX/Helpers/ResourceHelper.cs | 2 +- ServerPickerX/Models/ServerModel.cs | 59 ++++++++++++---- ServerPickerX/Program.cs | 11 ++- ServerPickerX/ServerPickerX.csproj | 30 ++++---- ServerPickerX/Styles/DataGridStyle.axaml | 39 +++++++++++ .../ViewModels/MainWindowViewModel.cs | 28 +++----- ServerPickerX/Views/MainWindow.axaml | 68 +++++++++++-------- ServerPickerX/Views/MainWindow.axaml.cs | 35 ++++++---- .../UserWindows/PresetManagerWindow.axaml | 52 +------------- .../Views/UserWindows/SettingsWindow.axaml | 15 +--- 15 files changed, 273 insertions(+), 174 deletions(-) create mode 100644 ServerPickerX/Comparers/PacketLossComparer.cs create mode 100644 ServerPickerX/Converters/PacketLossColorConverter.cs create mode 100644 ServerPickerX/Converters/PingColorConverter.cs create mode 100644 ServerPickerX/Styles/DataGridStyle.axaml diff --git a/ServerPickerX/App.axaml b/ServerPickerX/App.axaml index c9f0643..1b2d8d5 100644 --- a/ServerPickerX/App.axaml +++ b/ServerPickerX/App.axaml @@ -31,5 +31,6 @@ + diff --git a/ServerPickerX/App.axaml.cs b/ServerPickerX/App.axaml.cs index c427192..8ac84a3 100644 --- a/ServerPickerX/App.axaml.cs +++ b/ServerPickerX/App.axaml.cs @@ -111,26 +111,12 @@ public override void OnFrameworkInitializationCompleted() if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { // Avoid duplicate validations from both Avalonia and the CommunityToolkit. - // More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins - DisableAvaloniaDataAnnotationValidation(); + // More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugin desktop.MainWindow = new MainWindow(); } base.OnFrameworkInitializationCompleted(); } - // Reflection is partially used here and might not be trim-compatible unless JsonSerializerIsReflectionEnabledByDefault is set to true in .csproj - private void DisableAvaloniaDataAnnotationValidation() - { - // Get an array of plugins to remove - var dataValidationPluginsToRemove = - BindingPlugins.DataValidators.OfType().ToArray(); - - // remove each entry found - foreach (var plugin in dataValidationPluginsToRemove) - { - BindingPlugins.DataValidators.Remove(plugin); - } - } } } diff --git a/ServerPickerX/Comparers/PacketLossComparer.cs b/ServerPickerX/Comparers/PacketLossComparer.cs new file mode 100644 index 0000000..23b37fc --- /dev/null +++ b/ServerPickerX/Comparers/PacketLossComparer.cs @@ -0,0 +1,35 @@ +using ServerPickerX.Models; +using System; +using System.Collections; +using System.ComponentModel; +using System.Text.RegularExpressions; + +namespace ServerPickerX.Comparers +{ + public class PacketLossComparer : IComparer + { + public ListSortDirection _direction; + + public PacketLossComparer(ListSortDirection direction) + { + _direction = direction; + } + + public int Compare(object? x, object? y) + { + ServerModel? model1 = x as ServerModel; + ServerModel? model2 = y as ServerModel; + + // Remove "%" suffix + string? loss1Str = Regex.Replace(model1?.PacketLoss ?? "", @"[^\d]", ""); + string? loss2Str = Regex.Replace(model2?.PacketLoss ?? "", @"[^\d]", ""); + + int loss1 = int.Parse(!String.IsNullOrEmpty(loss1Str) ? loss1Str : "100"); + int loss2 = int.Parse(!String.IsNullOrEmpty(loss2Str) ? loss2Str : "100"); + + var result = loss1.CompareTo(loss2); + + return _direction == ListSortDirection.Descending ? result : -result; + } + } +} \ No newline at end of file diff --git a/ServerPickerX/Converters/PacketLossColorConverter.cs b/ServerPickerX/Converters/PacketLossColorConverter.cs new file mode 100644 index 0000000..63dcec0 --- /dev/null +++ b/ServerPickerX/Converters/PacketLossColorConverter.cs @@ -0,0 +1,27 @@ +using System; +using System.Globalization; +using Avalonia.Data.Converters; +using Avalonia.Media; + +namespace ServerPickerX.Converters +{ + public class PacketLossColorConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + string str = value as string ?? "0%"; + if (double.TryParse(str.Replace("%", ""), NumberStyles.Any, culture, out double val)) + { + if (val < 5) return Brushes.LimeGreen; + if (val <= 20) return Brushes.Orange; + return Brushes.Red; + } + return Brushes.White; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotSupportedException(); + } + } +} \ No newline at end of file diff --git a/ServerPickerX/Converters/PingColorConverter.cs b/ServerPickerX/Converters/PingColorConverter.cs new file mode 100644 index 0000000..252095f --- /dev/null +++ b/ServerPickerX/Converters/PingColorConverter.cs @@ -0,0 +1,29 @@ +using Avalonia.Data.Converters; +using Avalonia.Media; +using System; +using System.Globalization; + +namespace ServerPickerX.Converters +{ + public class PingColorConverter : IValueConverter + { + public object? Convert(object? value, Type targetType, + object? parameter, System.Globalization.CultureInfo culture) + { + string str = value as string ?? "0ms"; + if (double.TryParse(str.Replace("ms", ""), NumberStyles.Any, culture, out double val)) + { + if (val <= 75) return Brushes.LimeGreen; + if (val <= 150) return Brushes.Orange; + return Brushes.Red; + } + return Brushes.White; + } + + public object? ConvertBack(object? value, Type targetType, + object? parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException("Not implemented."); + } + } +} \ No newline at end of file diff --git a/ServerPickerX/Helpers/ResourceHelper.cs b/ServerPickerX/Helpers/ResourceHelper.cs index cdd8cff..840e498 100644 --- a/ServerPickerX/Helpers/ResourceHelper.cs +++ b/ServerPickerX/Helpers/ResourceHelper.cs @@ -18,7 +18,7 @@ public static Uri CreateResourceUriFromPath(string path) var assemblyName = Assembly.GetEntryAssembly()?.GetName().Name; // Use actual assembly name for designer previewer since it loads in a different context - if (assemblyName == "Avalonia.Designer.HostApp") + if (assemblyName is "Avalonia.Designer.HostApp" or "AvaloniaUI.Previewer.HostApp") { assemblyName = "ServerPickerX"; } diff --git a/ServerPickerX/Models/ServerModel.cs b/ServerPickerX/Models/ServerModel.cs index 49f07c1..846c6a1 100644 --- a/ServerPickerX/Models/ServerModel.cs +++ b/ServerPickerX/Models/ServerModel.cs @@ -22,6 +22,9 @@ public partial class ServerModel : ObservableObject [ObservableProperty] public string? status; + + [ObservableProperty] + public string? packetLoss; public List RelayModels { get; set; } = []; @@ -29,12 +32,9 @@ public partial class ServerModel : ObservableObject public async void PingServer() { - // If there's an ongoing ping operation then cancel it through token signals - // Linux ICMP behaves differently. Executing too many ping operations may result in a timeout if (this._cancelTokenSource != null) { this._cancelTokenSource.Cancel(); - } this._cancelTokenSource = new CancellationTokenSource(); @@ -44,11 +44,14 @@ public async void PingServer() Ping = "Pinging server"; + RelayModel? bestRelay = null; + long bestRtt = long.MaxValue; + + // Phase 1, Find the best relay (lowest RTT) foreach (RelayModel relay in RelayModels) { try { - // Cancellable async operation var res = await ping.SendPingAsync( address: IPAddress.Parse(relay.IPv4), timeout: TimeSpan.FromMilliseconds(800), @@ -56,24 +59,52 @@ public async void PingServer() cancellationToken: cancelToken ); - if (res.Status == IPStatus.Success && res.RoundtripTime >= 0) + if (res.Status == IPStatus.Success && res.RoundtripTime >= 0 && res.RoundtripTime < bestRtt) { - Ping = res.RoundtripTime + "ms"; - Status = "✅"; - - break; + bestRtt = res.RoundtripTime; + bestRelay = relay; } } - catch (Exception ex) when (ex is PingException or OperationCanceledException) + catch (Exception ex) when(ex is OperationCanceledException) { } + } + + if (bestRelay != null) + { + PacketLoss = "Probing"; + + // Phase 2, Probe the best relay 4 times + int successCount = 0; + long finalBestRtt = long.MaxValue; + const int probeCount = 4; + + for (int i = 0; i < probeCount; i++) { - break; + try + { + var res = await ping.SendPingAsync( + address: IPAddress.Parse(bestRelay.IPv4), + timeout: TimeSpan.FromMilliseconds(2000), + options: new PingOptions(), + cancellationToken: cancelToken + ); + + if (res.Status == IPStatus.Success && res.RoundtripTime >= 0) + { + successCount++; + finalBestRtt = Math.Min(finalBestRtt, res.RoundtripTime); + } + } + catch (Exception ex) when (ex is OperationCanceledException) { } } - } - // if pinging status remains depite ping all relays then its blocked or unreachable - if (Ping == "Pinging server") + double lossPercent = (1 - (successCount / probeCount)) * 100; + Ping = successCount > 0 ? finalBestRtt + "ms" : ""; + Status = successCount > 0 ? "✅" : "❌"; + PacketLoss = $"{lossPercent:F0}%"; + } else if (Ping == "Pinging server") { Ping = ""; + PacketLoss = ""; Status = "❌"; } } diff --git a/ServerPickerX/Program.cs b/ServerPickerX/Program.cs index 89ad75a..7eae8d0 100644 --- a/ServerPickerX/Program.cs +++ b/ServerPickerX/Program.cs @@ -1,7 +1,7 @@ using Avalonia; -using Projektanker.Icons.Avalonia; -using Projektanker.Icons.Avalonia.FontAwesome; using System; +using Optris.Icons.Avalonia; +using Optris.Icons.Avalonia.FontAwesome; namespace ServerPickerX { @@ -22,10 +22,9 @@ public static AppBuilder BuildAvaloniaApp() return AppBuilder.Configure() .UsePlatformDetect() - .With(new Win32PlatformOptions - { - RenderingMode = [Win32RenderingMode.Software], - }) +#if DEBUG + .WithDeveloperTools() +#endif .WithInterFont() .LogToTrace(); } diff --git a/ServerPickerX/ServerPickerX.csproj b/ServerPickerX/ServerPickerX.csproj index cd1f1d1..d03f7b2 100644 --- a/ServerPickerX/ServerPickerX.csproj +++ b/ServerPickerX/ServerPickerX.csproj @@ -22,8 +22,8 @@ https://github.com/FN-FAL113/server-picker-x https://github.com/FN-FAL113/server-picker-x Assets\favicon.ico - 1.0.6.0 - 1.0.6.0 + 1.1.0.0 + 1.1.0.0 @@ -31,25 +31,23 @@ + libs\Interop.NetFwTypeLib.dll true - - - - - + + + + + - - None - All - - - - - - + + + + + + diff --git a/ServerPickerX/Styles/DataGridStyle.axaml b/ServerPickerX/Styles/DataGridStyle.axaml new file mode 100644 index 0000000..8c8d510 --- /dev/null +++ b/ServerPickerX/Styles/DataGridStyle.axaml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + diff --git a/ServerPickerX/ViewModels/MainWindowViewModel.cs b/ServerPickerX/ViewModels/MainWindowViewModel.cs index 26fe4fa..17768e4 100644 --- a/ServerPickerX/ViewModels/MainWindowViewModel.cs +++ b/ServerPickerX/ViewModels/MainWindowViewModel.cs @@ -151,7 +151,7 @@ public async Task SetClusterStateAsync(bool isClustered, bool shouldUnblockCurre await _jsonSetting.SaveSettingsAsync(); - await MarkPresetSelectionDirtyAsync(); + await ClearLastSelectedPresetByGameModeAsync(); } ServerData serverData = _serverDataService.GetServerData(); @@ -275,7 +275,7 @@ public void PingServers(ICollection serverModels) } catch (InvalidOperationException) { - // when user suddenly tries to cluster or uncluster the servers while ServerModels is being iterated + // when user suddenly tries to cluster or uncluster the servers while server models are being iterated } } @@ -319,24 +319,14 @@ await _messageBoxService.ShowMessageBoxAsync( } [RelayCommand] - public async Task UnblockAllAsync() + public async Task UnblockAllAsync(bool? shouldClearLastSelectedPreset = true) { if (ServerModels == null || ServerModels.Count == 0) { return false; } - return await PerformOperationAsync(false, FilteredServerModels); - } - - public async Task UnblockCurrentGameServersAsync() - { - if (ServerModels.Count == 0) - { - return true; - } - - return await PerformOperationAsync(false, new ObservableCollection(ServerModels), false); + return await PerformOperationAsync(false, ServerModels, shouldClearLastSelectedPreset ?? true); } [RelayCommand] @@ -360,7 +350,7 @@ await _messageBoxService.ShowMessageBoxAsync( public async Task PerformOperationAsync( bool shouldBlock, ObservableCollection serverModels, - bool shouldUpdatePresetSelection = true + bool shouldClearLastSelectedPreset = true ) { if (PendingOperation) @@ -394,12 +384,12 @@ await _messageBoxService.ShowMessageBoxAsync( await _loggerService.LogInfoAsync("Servers unblocked successfully"); } - if (shouldUpdatePresetSelection) + if (shouldClearLastSelectedPreset) { - await MarkPresetSelectionDirtyAsync(); + await ClearLastSelectedPresetByGameModeAsync(); } - // Ping servers (parallel operation) + // Ping servers (parallel/fire-forget operation) PingServers(serverModels); return true; @@ -583,7 +573,7 @@ private ObservableCollection GetMatchingServerModels(PresetModel se ); } - private async Task MarkPresetSelectionDirtyAsync() + private async Task ClearLastSelectedPresetByGameModeAsync() { await _jsonSetting.ClearLastSelectedPresetNameByGameModeAsync(); diff --git a/ServerPickerX/Views/MainWindow.axaml b/ServerPickerX/Views/MainWindow.axaml index 3e747da..25c14ce 100644 --- a/ServerPickerX/Views/MainWindow.axaml +++ b/ServerPickerX/Views/MainWindow.axaml @@ -9,36 +9,25 @@ mc:Ignorable="d" x:Class="ServerPickerX.Views.MainWindow" x:DataType="vm:MainWindowViewModel" + Title="Server Picker X" + FontWeight="SemiBold" Width="920" - MinWidth="920" - MaxWidth="1280" Height="640" - MinHeight="500" - MaxHeight="960" Icon="/Assets/favicon.ico" - Title="ServerPickerX" - SystemDecorations="BorderOnly" ExtendClientAreaToDecorationsHint="True" - ExtendClientAreaChromeHints="NoChrome" ExtendClientAreaTitleBarHeightHint="-1" WindowStartupLocation="CenterScreen" Loaded="Window_Loaded"> - - - - - - - + + + BorderThickness="1.0"> @@ -50,14 +39,11 @@ - - Server Picker X - - @@ -214,9 +200,31 @@ - + + + + + + + + + + + + + + - + + - - - - - - + BorderThickness="1"> @@ -72,14 +34,6 @@ - - diff --git a/ServerPickerX/Views/UserWindows/SettingsWindow.axaml b/ServerPickerX/Views/UserWindows/SettingsWindow.axaml index b929321..024bd09 100644 --- a/ServerPickerX/Views/UserWindows/SettingsWindow.axaml +++ b/ServerPickerX/Views/UserWindows/SettingsWindow.axaml @@ -10,9 +10,9 @@ Height="250" x:Class="ServerPickerX.SettingsWindow" x:DataType="viewmodels:SettingsWindowViewModel" - SystemDecorations="BorderOnly" + Title="Settings" + FontWeight="SemiBold" ExtendClientAreaToDecorationsHint="True" - ExtendClientAreaChromeHints="NoChrome" ExtendClientAreaTitleBarHeightHint="-1" WindowStartupLocation="CenterOwner" CanResize="False" @@ -38,7 +38,7 @@ Classes="wBorder" Background="#373B78" BorderBrush="#575cd4" - BorderThickness="1.35"> + BorderThickness="1"> @@ -51,15 +51,6 @@ - - Settings - - From 3213269179f2258ee8bd87485c655c8303138c7e Mon Sep 17 00:00:00 2001 From: FN-FAL113 Date: Sat, 20 Jun 2026 13:22:55 +0800 Subject: [PATCH 2/4] chore: update readme screenshot and chores --- README.md | 4 ++-- ServerPickerX.Tests/ViewModels/MainWindowViewModelTest.cs | 4 ---- ServerPickerX/Converters/PacketLossColorConverter.cs | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e6ca298..4ab0da7 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,14 @@ Lightweight server picker for CS2 and Deadlock with cross-platform support for * ### [Releases](https://github.com/FN-FAL113/server-picker-x/releases) ## 📷 Screenshot -![ServerPickerX](https://github.com/user-attachments/assets/97f8316f-fd09-4242-b996-56c971d97416) +![ServerPickerX](https://github.com/user-attachments/assets/50593e29-f01d-4f32-b586-80fc859d9016)
Windows Short Demo ![Windows Short Demo Video](https://github.com/FN-FAL113/server-picker-x/blob/chore/readme-assets/readme_assets/ServerPickerX-Windows.gif)
- Linux (Arch) Short Demo + Linux Arch Short Demo ![Linux Arch Short Demo Video](https://github.com/FN-FAL113/server-picker-x/blob/chore/readme-assets/readme_assets/ServerPickerX-Linux-Arch.gif)
diff --git a/ServerPickerX.Tests/ViewModels/MainWindowViewModelTest.cs b/ServerPickerX.Tests/ViewModels/MainWindowViewModelTest.cs index 905ac04..89ae0fc 100644 --- a/ServerPickerX.Tests/ViewModels/MainWindowViewModelTest.cs +++ b/ServerPickerX.Tests/ViewModels/MainWindowViewModelTest.cs @@ -272,11 +272,9 @@ public async Task Test_BlockSelectedAsync_WithSelection() { if (item.index is 0 or 2) { - Assert.Empty(item.value.Ping); Assert.Equal("❌", item.value.Status); } else { - Assert.True(item.value.Ping.Contains("ms")); Assert.Equal("✅", item.value.Status); } } @@ -329,7 +327,6 @@ public async Task Test_UnblockAllAsync_WithServers() Assert.True(result); foreach (var srv in _vm.ServerModels) { - Assert.Contains("ms", srv.Ping); Assert.Equal("✅", srv.Status); } } @@ -458,7 +455,6 @@ public async Task Test_PerformOperationAsync_Unblocking_Success() Assert.True(result); foreach (var srv in serverModels) { - Assert.Contains("ms", srv.Ping); Assert.Equal("✅", srv.Status); } } diff --git a/ServerPickerX/Converters/PacketLossColorConverter.cs b/ServerPickerX/Converters/PacketLossColorConverter.cs index 63dcec0..a8183da 100644 --- a/ServerPickerX/Converters/PacketLossColorConverter.cs +++ b/ServerPickerX/Converters/PacketLossColorConverter.cs @@ -7,7 +7,7 @@ namespace ServerPickerX.Converters { public class PacketLossColorConverter : IValueConverter { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { string str = value as string ?? "0%"; if (double.TryParse(str.Replace("%", ""), NumberStyles.Any, culture, out double val)) From aaef83f9b9bc483ea08d0306a32ee1accbe48fbd Mon Sep 17 00:00:00 2001 From: FN-FAL113 Date: Sat, 20 Jun 2026 15:50:33 +0800 Subject: [PATCH 3/4] refactor: title bar changes - Revert back title bar buttons and add maximize button functionality except on settings window - Code cleanup and chores - Fix Linux error regarding top-level dependency, uninstalled and converted back as transitive dependency (tmds,dbus.protocol) --- .../Converters/PacketLossColorConverter.cs | 2 +- ServerPickerX/ServerPickerX.csproj | 1 - .../Styles/TitleBarButtonStyle.axaml | 18 +++++++++++++---- ServerPickerX/Views/MainWindow.axaml | 5 +++++ .../Views/UserControls/FooterButtons.axaml.cs | 4 ++-- .../Views/UserControls/TitleBarButtons.axaml | 7 ++++++- .../UserControls/TitleBarButtons.axaml.cs | 20 +++++++++++++++++++ .../UserWindows/PresetManagerWindow.axaml | 14 ++++++++++--- .../Views/UserWindows/SettingsWindow.axaml | 15 ++++++++++++-- 9 files changed, 72 insertions(+), 14 deletions(-) diff --git a/ServerPickerX/Converters/PacketLossColorConverter.cs b/ServerPickerX/Converters/PacketLossColorConverter.cs index a8183da..08a9fe6 100644 --- a/ServerPickerX/Converters/PacketLossColorConverter.cs +++ b/ServerPickerX/Converters/PacketLossColorConverter.cs @@ -19,7 +19,7 @@ public class PacketLossColorConverter : IValueConverter return Brushes.White; } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { throw new NotSupportedException(); } diff --git a/ServerPickerX/ServerPickerX.csproj b/ServerPickerX/ServerPickerX.csproj index d03f7b2..513fecd 100644 --- a/ServerPickerX/ServerPickerX.csproj +++ b/ServerPickerX/ServerPickerX.csproj @@ -48,6 +48,5 @@ - diff --git a/ServerPickerX/Styles/TitleBarButtonStyle.axaml b/ServerPickerX/Styles/TitleBarButtonStyle.axaml index 0d36d37..d0c3371 100644 --- a/ServerPickerX/Styles/TitleBarButtonStyle.axaml +++ b/ServerPickerX/Styles/TitleBarButtonStyle.axaml @@ -1,10 +1,14 @@  - + - - + + + + + + @@ -21,11 +25,17 @@ + diff --git a/ServerPickerX/Views/MainWindow.axaml b/ServerPickerX/Views/MainWindow.axaml index 25c14ce..de5f654 100644 --- a/ServerPickerX/Views/MainWindow.axaml +++ b/ServerPickerX/Views/MainWindow.axaml @@ -14,6 +14,7 @@ Width="920" Height="640" Icon="/Assets/favicon.ico" + WindowDecorations="BorderOnly" ExtendClientAreaToDecorationsHint="True" ExtendClientAreaTitleBarHeightHint="-1" WindowStartupLocation="CenterScreen" @@ -44,6 +45,10 @@ Width="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=Width}" IsIndeterminate="True" IsVisible="{Binding ShowProgressBar}"/> + + Server Picker X + +
diff --git a/ServerPickerX/Views/UserControls/FooterButtons.axaml.cs b/ServerPickerX/Views/UserControls/FooterButtons.axaml.cs index cea0992..7b6fee5 100644 --- a/ServerPickerX/Views/UserControls/FooterButtons.axaml.cs +++ b/ServerPickerX/Views/UserControls/FooterButtons.axaml.cs @@ -20,14 +20,14 @@ public FooterButtons() private async void PaypalBtn_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { -await ServiceLocator + await ServiceLocator .GetRequiredService() .OpenUrl("https://www.paypal.com/paypalme/fnfal113"); } private async void GithubBtn_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { -await ServiceLocator + await ServiceLocator .GetRequiredService() .OpenUrl("https://github.com/FN-FAL113/server-picker-x"); } diff --git a/ServerPickerX/Views/UserControls/TitleBarButtons.axaml b/ServerPickerX/Views/UserControls/TitleBarButtons.axaml index 1f16fd8..81166dd 100644 --- a/ServerPickerX/Views/UserControls/TitleBarButtons.axaml +++ b/ServerPickerX/Views/UserControls/TitleBarButtons.axaml @@ -12,8 +12,13 @@ Classes="titlebar-btn minimize-btn" Width="32" Height="32" - BorderThickness="0" Click="MinimizeBtn_Click" /> +