now it works

This commit is contained in:
2024-11-06 00:04:12 +05:00
parent 612976dfe6
commit 1f663902e2
54 changed files with 763 additions and 586 deletions

View File

@@ -0,0 +1,26 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Mlaumcherb.Client.Avalonia.зримое.NetworkTaskView"
Padding="4" MinHeight="90" MinWidth="200"
VerticalAlignment="Top"
HorizontalAlignment="Stretch"
BorderThickness="1" BorderBrush="#999999">
<Grid RowDefinitions="* * *" ColumnDefinitions="* 30">
<TextBlock Name="NameText"
Grid.Row="0" Grid.Column="0" />
<Button Grid.Row="0" Grid.Column="1"
Classes="button_no_border"
Background="Transparent"
Foreground="#FF4040"
FontSize="12"
Click="RemoveFromList">
[X]
</Button>
<TextBlock Name="StatusText"
Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
FontSize="15"/>
<TextBlock Name="ProgressText"
Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"
FontSize="14"/>
</Grid>
</UserControl>

View File

@@ -0,0 +1,66 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Threading;
using Mlaumcherb.Client.Avalonia.сеть;
namespace Mlaumcherb.Client.Avalonia.зримое;
public partial class NetworkTaskView : UserControl
{
public readonly NetworkTask Task;
private readonly Action<NetworkTaskView> _removeFromList;
public NetworkTaskView()
{
throw new Exception();
}
public NetworkTaskView(NetworkTask task, Action<NetworkTaskView> removeFromList)
{
Task = task;
_removeFromList = removeFromList;
InitializeComponent();
NameText.Text = task.Name;
StatusText.Text = task.DownloadStatus.ToString();
task.OnStart += OnTaskOnStart;
task.OnProgress += ReportProgress;
task.OnStop += OnTaskStop;
}
private void OnTaskOnStart()
{
Dispatcher.UIThread.Invoke(() =>
{
StatusText.Text = Task.DownloadStatus.ToString();
});
}
private void OnTaskStop(NetworkTask.Status status)
{
Dispatcher.UIThread.Invoke(() =>
{
StatusText.Text = status.ToString();
if(!string.IsNullOrEmpty(ProgressText.Text))
{
int speedIndex = ProgressText.Text.IndexOf('(');
if(speedIndex > 0)
ProgressText.Text = ProgressText.Text.Remove(speedIndex);
}
});
}
void ReportProgress(DownloadProgress progress)
{
Dispatcher.UIThread.Invoke(() =>
{
ProgressText.Text = progress.ToString();
});
}
private void RemoveFromList(object? sender, RoutedEventArgs e)
{
Task.Cancel();
Dispatcher.UIThread.Invoke(() => _removeFromList.Invoke(this));
}
}

View File

@@ -0,0 +1,59 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Mlaumcherb.Client.Avalonia.зримое.LauncherApp"
RequestedThemeVariant="Dark">
<Application.Styles>
<SimpleTheme />
<Style Selector="Border.dark_tr_bg">
<Setter Property="Background" Value="#cc232333"/>
</Style>
<Style Selector="Border.white_border">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="White"/>
</Style>
<Style Selector="Button.button_no_border">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="Transparent"/>
</Style>
<Style Selector="Button.menu_button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Padding" Value="4 0"/>
<Setter Property="MinWidth" Value="50"/>
</Style>
<Style Selector="Border.menu_separator">
<Setter Property="Background" Value="#ff505050"/>
<Setter Property="Width" Value="1"/>
<Setter Property="Margin" Value="4"/>
</Style>
<Style Selector="ScrollBar /template/ Border">
<Setter Property="Width" Value="5"/>
<Setter Property="Margin" Value="4"/>
<Setter Property="ClipToBounds" Value="True"/>
<Setter Property="CornerRadius" Value="0"/>
</Style>
<Style Selector="ScrollBar /template/ Rectangle">
<Setter Property="Fill" Value="#d8ceb9"/>
</Style>
<Style Selector="ScrollBar /template/ Thumb">
<Setter Property="Background" Value="#fd7300"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="CornerRadius" Value="0"/>
</Style>
<Style Selector="ScrollBar /template/ Thumb /template/ Border">
<Setter Property="ClipToBounds" Value="True"/>
<Setter Property="CornerRadius" Value="0"/>
<Setter Property="Width" Value="5"/>
</Style>
</Application.Styles>
<Application.Resources>
<FontFamily x:Key="MonospaceFont">avares://млаумчерб/капитал/IBMPlexMono-Regular.ttf</FontFamily>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,29 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace Mlaumcherb.Client.Avalonia.зримое;
public class LauncherApp : Application
{
public static LauncherLogger Logger = new();
public static Config Config = new();
public override void Initialize()
{
Logger.LogInfo(nameof(LauncherApp), "приложение запущено");
Config = Config.LoadFromFile();
Logger.DebugLogEnabled = Config.debug;
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow();
}
base.OnFrameworkInitializationCompleted();
}
}

View File

@@ -0,0 +1,13 @@
<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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Mlaumcherb.Client.Avalonia.зримое.LogMessageView"
FontSize="14">
<SelectableTextBlock Name="ContentTextBox"
FontSize="{Binding $parent.FontSize}"
TextWrapping="Wrap"
VerticalAlignment="Top"
Background="Transparent"/>
</UserControl>

View File

@@ -0,0 +1,23 @@
using Avalonia.Controls;
namespace Mlaumcherb.Client.Avalonia.зримое;
public partial class LogMessageView : UserControl
{
public static ILogFormat ShortLogFormat = new DefaultLogFormat
{
TimeStampFormat = MyTimeFormat.TimeOnly,
PrintContext = false
};
public LogMessageView()
{
throw new Exception();
}
public LogMessageView(LauncherLogger.LogMessage m)
{
InitializeComponent();
ContentTextBox.Text = ShortLogFormat.CreateMessage(m.context, m.severity, m.message);
}
}

View File

@@ -0,0 +1,139 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:gif="clr-namespace:Avalonia.Labs.Gif;assembly=Avalonia.Labs.Gif"
xmlns:local="clr-namespace:Mlaumcherb.Client.Avalonia"
x:Class="Mlaumcherb.Client.Avalonia.зримое.MainWindow"
Name="window"
Title="млаумчерб"
Icon="avares://млаумчерб/капитал/кубе.ico"
FontFamily="{StaticResource MonospaceFont}" FontSize="18"
MinWidth="1200" MinHeight="700"
Width="800" Height="500"
WindowStartupLocation="CenterScreen">
<Grid>
<Grid.RowDefinitions>* 30</Grid.RowDefinitions>
<Image Grid.RowSpan="2" Stretch="UniformToFill"
Source="avares://млаумчерб/капитал/фоне.png"/>
<Grid Grid.Row="0" Margin="10">
<Grid.ColumnDefinitions>* 300 *</Grid.ColumnDefinitions>
<!-- Central panel -->
<Border Grid.Column="1"
Classes="dark_tr_bg white_border"
Margin="10 0">
<Grid>
<Grid.RowDefinitions>* 60</Grid.RowDefinitions>
<StackPanel Orientation="Vertical" Margin="10" Spacing="10">
<TextBlock>Версия:</TextBlock>
<ComboBox Name="VersionComboBox"/>
<TextBlock>Ник:</TextBlock>
<TextBox Background="Transparent"
Text="{Binding #window.PlayerName}"/>
<TextBlock>
<Run>Выделенная память:</Run>
<TextBox Background="Transparent" Padding="0"
BorderThickness="1"
BorderBrush="#777777"
Text="{Binding #window.MemoryLimit}">
</TextBox>
<Run>Мб</Run>
</TextBlock>
<Slider Minimum="2048" Maximum="8192"
Value="{Binding #window.MemoryLimit}">
</Slider>
<CheckBox IsChecked="{Binding #window.CheckGameFiles}">
Проверять файлы игры
</CheckBox>
<CheckBox IsChecked="{Binding #window.EnableJavaDownload}">
Скачивать джаву
</CheckBox>
</StackPanel>
<Button Name="LaunchButton" Grid.Row="1"
Margin="10" Padding="0 0 0 4"
Classes="button_no_border"
Background="#BBfd7300"
Click="Запуск">
Запуск
</Button>
</Grid>
</Border>
<!-- Left panel -->
<Border Grid.Column="0"
Classes="dark_tr_bg white_border">
<Grid>
<Grid.RowDefinitions>36 *</Grid.RowDefinitions>
<Border Classes="white_border" Margin="-1" Padding="4">
<DockPanel>
<TextBlock FontWeight="Bold"
DockPanel.Dock="Left">
Лог
</TextBlock>
<Button Click="ClearLogPanel"
Classes="menu_button"
DockPanel.Dock="Right"
HorizontalAlignment="Right">
очистить
</Button>
</DockPanel>
</Border>
<ScrollViewer Name="LogScrollViewer" Grid.Row="1"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Visible"
Background="Transparent">
<StackPanel Name="LogPanel"
VerticalAlignment="Top"/>
</ScrollViewer>
</Grid>
</Border>
<!-- Right panel -->
<Border Grid.Column="2" Classes="dark_tr_bg white_border">
<Grid>
<Grid.RowDefinitions>36 *</Grid.RowDefinitions>
<Border Classes="white_border" Margin="-1" Padding="4">
<DockPanel>
<TextBlock FontWeight="Bold"
DockPanel.Dock="Left">
Загрузки
</TextBlock>
<Button Click="ClearDownloadsPanel"
Classes="menu_button"
DockPanel.Dock="Right"
HorizontalAlignment="Right">
очистить
</Button>
</DockPanel>
</Border>
<ScrollViewer Grid.Row="1"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Visible"
Background="Transparent"
Padding="1">
<StackPanel Name="DownloadsPanel"
VerticalAlignment="Top"/>
</ScrollViewer>
</Grid>
</Border>
</Grid>
<Border Grid.Row="1" Background="#954808B0">
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Button Classes="menu_button button_no_border" Click="ОткрытьПапкуЛаунчера">папка лаунчера</Button>
<Border Classes="menu_separator"/>
<Button Classes="menu_button button_no_border" Click="ОткрытьФайлЛогов">лог-файл</Button>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Classes="menu_button button_no_border" Click="ОткрытьРепозиторий">исходный код</Button>
<gif:GifImage
Width="30" Height="30" Stretch="Uniform"
Source="avares://млаумчерб/капитал/лисик.gif"/>
</StackPanel>
</Grid>
</Border>
</Grid>
</Window>

View File

@@ -0,0 +1,199 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using Mlaumcherb.Client.Avalonia.классы;
using Mlaumcherb.Client.Avalonia.холопы;
namespace Mlaumcherb.Client.Avalonia.зримое;
public partial class MainWindow : Window
{
public static readonly StyledProperty<string> PlayerNameProperty =
AvaloniaProperty.Register<MainWindow, string>(nameof(PlayerName),
defaultBindingMode: BindingMode.TwoWay);
public string PlayerName
{
get => GetValue(PlayerNameProperty);
set => SetValue(PlayerNameProperty, value);
}
public static readonly StyledProperty<int> MemoryLimitProperty =
AvaloniaProperty.Register<MainWindow, int>(nameof(MemoryLimit),
defaultBindingMode: BindingMode.TwoWay, defaultValue: 2048);
public int MemoryLimit
{
get => GetValue(MemoryLimitProperty);
set => SetValue(MemoryLimitProperty, value);
}
public static readonly StyledProperty<bool> CheckGameFilesProperty =
AvaloniaProperty.Register<MainWindow, bool>(nameof(CheckGameFiles),
defaultBindingMode: BindingMode.TwoWay, defaultValue: false);
public bool CheckGameFiles
{
get => GetValue(CheckGameFilesProperty);
set => SetValue(CheckGameFilesProperty, value);
}
public static readonly StyledProperty<bool> EnableJavaDownloadProperty =
AvaloniaProperty.Register<MainWindow, bool>(nameof(EnableJavaDownload),
defaultBindingMode: BindingMode.TwoWay, defaultValue: true);
public bool EnableJavaDownload
{
get => GetValue(EnableJavaDownloadProperty);
set => SetValue(EnableJavaDownloadProperty, value);
}
public MainWindow()
{
InitializeComponent();
}
protected override async void OnLoaded(RoutedEventArgs e)
{
try
{
LauncherApp.Logger.OnLogMessage += GuiLogMessage;
PlayerName = LauncherApp.Config.player_name;
MemoryLimit = LauncherApp.Config.max_memory;
EnableJavaDownload = LauncherApp.Config.download_java;
VersionComboBox.SelectedIndex = 0;
VersionComboBox.IsEnabled = false;
var versions = await GameVersion.GetAllVersionsAsync();
Dispatcher.UIThread.Invoke(() =>
{
foreach (var p in versions)
{
VersionComboBox.Items.Add(new VersionItemView(p));
if (LauncherApp.Config.last_launched_version != null &&
p.Name == LauncherApp.Config.last_launched_version)
VersionComboBox.SelectedIndex = VersionComboBox.Items.Count - 1;
}
VersionComboBox.IsEnabled = true;
});
}
catch (Exception ex)
{
ErrorHelper.ShowMessageBox(nameof(MainWindow), ex);
}
}
private void GuiLogMessage(LauncherLogger.LogMessage msg)
{
if (msg.severity == LogSeverity.Debug) return;
Dispatcher.UIThread.Invoke(() =>
{
double offsetFromBottom = LogScrollViewer.Extent.Height - LogScrollViewer.Offset.Y - LogScrollViewer.Viewport.Height;
bool is_scrolled_to_end = offsetFromBottom < 20.0; // scrolled less then one line up
LogPanel.Children.Add(new LogMessageView(msg));
if (is_scrolled_to_end) LogScrollViewer.ScrollToEnd();
});
}
private async void Запуск(object? sender, RoutedEventArgs e)
{
try
{
Dispatcher.UIThread.Invoke(() => LaunchButton.IsEnabled = false);
var selectedVersionView = (VersionItemView?)VersionComboBox.SelectedItem;
var selectedVersion = selectedVersionView?.Props;
LauncherApp.Config.last_launched_version = selectedVersion?.Name;
LauncherApp.Config.player_name = PlayerName;
LauncherApp.Config.max_memory = MemoryLimit;
LauncherApp.Config.download_java = EnableJavaDownload;
LauncherApp.Config.SaveToFile();
if (selectedVersion == null)
return;
var v = await GameVersion.CreateFromPropsAsync(selectedVersion);
await v.UpdateFiles(CheckGameFiles, nt =>
{
Dispatcher.UIThread.Invoke(() =>
{
DownloadsPanel.Children.Add(new NetworkTaskView(nt,
ntv => DownloadsPanel.Children.Remove(ntv)));
});
}
);
Dispatcher.UIThread.Invoke(() =>
{
CheckGameFiles = false;
});
await v.Launch();
}
catch (Exception ex)
{
ErrorHelper.ShowMessageBox(nameof(MainWindow), ex);
}
finally
{
Dispatcher.UIThread.Invoke(() =>
{
LaunchButton.IsEnabled = true;
});
}
}
private void ОткрытьПапкуЛаунчера(object? s, RoutedEventArgs e)
{
try
{
Launcher.LaunchDirectoryInfoAsync(new DirectoryInfo(Directory.GetCurrent().ToString()))
.ConfigureAwait(false);
}
catch (Exception ex)
{
ErrorHelper.ShowMessageBox(nameof(MainWindow), ex);
}
}
private void ОткрытьФайлЛогов(object? sender, RoutedEventArgs e)
{
try
{
Launcher.LaunchFileInfoAsync(new FileInfo(LauncherApp.Logger.LogfileName.ToString()))
.ConfigureAwait(false);
}
catch (Exception ex)
{
ErrorHelper.ShowMessageBox(nameof(MainWindow), ex);
}
}
private void ОткрытьРепозиторий(object? sender, RoutedEventArgs e)
{
try
{
Launcher.LaunchUriAsync(new Uri("https://timerix.ddns.net:3322/Timerix/mlaumcherb"))
.ConfigureAwait(false);
}
catch (Exception ex)
{
ErrorHelper.ShowMessageBox(nameof(MainWindow), ex);
}
}
private void ClearLogPanel(object? sender, RoutedEventArgs e)
{
LogPanel.Children.Clear();
}
private void ClearDownloadsPanel(object? sender, RoutedEventArgs e)
{
foreach (var control in DownloadsPanel.Children)
{
var nt = (NetworkTaskView)control;
nt.Task.Cancel();
}
DownloadsPanel.Children.Clear();
}
}

View File

@@ -0,0 +1,9 @@
<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:local="clr-namespace:Mlaumcherb.Client.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Mlaumcherb.Client.Avalonia.зримое.VersionItemView">
<TextBlock Name="text" Background="Transparent"/>
</UserControl>

View File

@@ -0,0 +1,31 @@
using Avalonia.Controls;
using Avalonia.Media;
using Mlaumcherb.Client.Avalonia.классы;
namespace Mlaumcherb.Client.Avalonia.зримое;
public partial class VersionItemView : ListBoxItem
{
public GameVersionProps Props { get; }
private SolidColorBrush _avaliableColor = new(Color.FromRgb(30, 130, 40));
private SolidColorBrush _unavaliableColor = new(Color.FromRgb(170, 70, 70));
public VersionItemView()
{
throw new Exception();
}
public VersionItemView(GameVersionProps props)
{
Props = props;
InitializeComponent();
text.Text = props.Name;
props.OnDownloadCompleted += UpdateBackground;
UpdateBackground();
}
private void UpdateBackground()
{
Background = Props.IsDownloaded ? _avaliableColor : _unavaliableColor;
}
}