first commit

This commit is contained in:
2025-07-06 00:23:46 +02:00
commit 38f50c8819
1788 changed files with 112878 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
using Avalonia.Controls;
namespace Nitrox.Launcher.Views.Abstract;
public abstract class ModalBase : Window
{
protected override void OnInitialized() => this.ApplyOsWindowStyling();
}

View File

@@ -0,0 +1,7 @@
using Avalonia.Controls;
using Nitrox.Launcher.ViewModels.Abstract;
namespace Nitrox.Launcher.Views.Abstract;
public abstract class RoutableViewBase<TViewModel> : UserControl
where TViewModel : RoutableViewModelBase;

View File

@@ -0,0 +1,21 @@
using System;
using Avalonia.Controls;
using Nitrox.Launcher.Models.Design;
using Nitrox.Launcher.ViewModels.Abstract;
namespace Nitrox.Launcher.Views.Abstract;
public abstract class WindowEx<T> : Window where T : ViewModelBase
{
protected override void OnInitialized()
{
this.ApplyOsWindowStyling();
// On Linux systems, Avalonia has trouble allowing windows to resize without "decorations". So we enable it in full, but hide the custom titlebar as it'll look bad.
if (OperatingSystem.IsLinux())
{
SystemDecorations = SystemDecorations.Full;
NitroxAttached.SetUseCustomTitleBar(this, false);
}
}
}

View File

@@ -0,0 +1,91 @@
<Window
MaxWidth="1000"
MinHeight="200"
MinWidth="500"
SizeToContent="WidthAndHeight"
Title="{Binding Title}"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d"
x:Class="Nitrox.Launcher.Views.BackupRestoreModal"
x:DataType="vm:BackupRestoreViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:controls="clr-namespace:Nitrox.Launcher.Models.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Nitrox.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.DataContext>
<vm:BackupRestoreViewModel />
</Design.DataContext>
<Grid RowDefinitions="Auto, *, Auto">
<controls:CustomTitlebar CanMinimize="False" Grid.Row="0" />
<TextBlock
Classes="modalHeader"
Grid.Row="0"
Margin="24,24,24,0"
Text="{Binding Title}" />
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Disabled">
<Panel>
<!-- Backups Don't Exist -->
<StackPanel
HorizontalAlignment="Center"
IsVisible="{Binding !Backups.Count}"
Spacing="50"
VerticalAlignment="Center">
<Image
Margin="-25,0,0,0"
Source="/Assets/Images/world-manager/server.png"
Stretch="None"
Width="300" />
<TextBlock
FontSize="32"
FontWeight="600"
Margin="0,0,0,40"
Text="No backups here yet"
TextAlignment="Center" />
</StackPanel>
<!-- Backups Exist -->
<ListBox
IsVisible="{Binding Backups.Count}"
ItemsSource="{Binding Backups}"
Margin="24"
SelectedItem="{Binding SelectedBackup, Mode=TwoWay}"
VerticalAlignment="Top">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Spacing="14" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" ToolTip.Tip="{Binding BackupFileName}">
<TextBlock Text="Backup " />
<TextBlock Text="{Binding BackupDate}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
</Panel>
</ScrollViewer>
<Border Classes="footer" Grid.Row="2">
<Panel>
<Button
Command="{Binding CloseCommand}"
Content="Back"
FontWeight="Bold"
HotKey="Escape" />
<Button
Classes="primary"
Command="{Binding RestoreBackupCommand}"
Content="Restore"
HorizontalAlignment="Right"
HotKey="Enter" />
</Panel>
</Border>
</Grid>
</Window>

View File

@@ -0,0 +1,11 @@
using Nitrox.Launcher.Views.Abstract;
namespace Nitrox.Launcher.Views;
public partial class BackupRestoreModal : ModalBase
{
public BackupRestoreModal()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,94 @@
<UserControl
d:DesignHeight="600"
d:DesignWidth="1000"
mc:Ignorable="d"
x:Class="Nitrox.Launcher.Views.BlogView"
x:DataType="vm:BlogViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:controls="clr-namespace:Nitrox.Launcher.Models.Controls"
xmlns:converters="clr-namespace:Nitrox.Launcher.Models.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:design="clr-namespace:Nitrox.Launcher.Models.Design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Nitrox.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.DataContext>
<vm:BlogViewModel>
<vm:BlogViewModel.NitroxBlogs>
<design:NitroxBlog
Date="1996-05-01"
Title="Example"
Url="example.com" />
<design:NitroxBlog
Date="2020-01-10"
Title="Nitrox Website"
Url="nitrox.rux.gg" />
</vm:BlogViewModel.NitroxBlogs>
</vm:BlogViewModel>
</Design.DataContext>
<ScrollViewer Classes="main">
<StackPanel
Background="{DynamicResource BrandWhite}"
Classes="viewPadding"
Spacing="20">
<StackPanel Spacing="15">
<TextBlock Classes="header" Text="Blog" />
<controls:RichTextBlock>
Read the latest news from the Nitrox team ! You can view all our blogs [here](nitroxblog.rux.gg)
</controls:RichTextBlock>
</StackPanel>
<ItemsControl ItemsSource="{Binding NitroxBlogs, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<controls:FittingWrapPanel HorizontalAlignment="Center" MinItemWidth="340" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="design:NitroxBlog">
<Button
Classes="anycontent"
Command="{Binding $parent[ItemsControl].((vm:BlogViewModel)DataContext).BlogEntryClickCommand}"
CommandParameter="{Binding Url, Mode=OneTime}"
Margin="0,0,15,15"
VerticalAlignment="Top">
<StackPanel Orientation="Vertical" Spacing="5">
<Border ClipToBounds="True" CornerRadius="12">
<Viewbox>
<Panel
Height="9"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="16">
<Image
IsVisible="{Binding Image, Converter={converters:EqualityConverter}}"
Source="{Binding $parent[ItemsControl].((vm:BlogViewModel)DataContext).FallbackImage}"
Stretch="UniformToFill" />
<Image Source="{Binding Image}" Stretch="UniformToFill" />
</Panel>
</Viewbox>
</Border>
<TextBlock
FontSize="16"
FontWeight="Bold"
HorizontalAlignment="Left"
Text="{Binding Title, TargetNullValue='No title'}" />
<TextBlock
FontSize="10"
FontWeight="Bold"
Foreground="{DynamicResource BrandSubText}"
HorizontalAlignment="Left"
Text="{Binding Date, StringFormat='{}{0:D}', TargetNullValue=''}"
ToolTip.Tip="{Binding Date, Converter={converters:DateToRelativeDateConverter}}" />
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,12 @@
using Nitrox.Launcher.ViewModels;
using Nitrox.Launcher.Views.Abstract;
namespace Nitrox.Launcher.Views;
public partial class BlogView : RoutableViewBase<BlogViewModel>
{
public BlogView()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,165 @@
<UserControl
d:DesignWidth="1000"
mc:Ignorable="d"
x:Class="Nitrox.Launcher.Views.CommunityView"
x:DataType="vm:CommunityViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:controls="clr-namespace:Nitrox.Launcher.Models.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Nitrox.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.DataContext>
<vm:CommunityViewModel />
</Design.DataContext>
<UserControl.Styles>
<Style Selector="controls|FittingWrapPanel">
<Setter Property="Margin" Value="-4 0" />
<Style Selector="^ > Border">
<Setter Property="Background" Value="{DynamicResource BrandPanelBackground}" />
<Setter Property="CornerRadius" Value="12" />
<Setter Property="Padding" Value="22" />
<Setter Property="Margin" Value="8" />
<Style Selector="^ > StackPanel">
<Setter Property="Spacing" Value="10" />
<Style Selector="^ TextBlock:nth-child(2)">
<Setter Property="FontSize" Value="16" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
</Style>
<Style Selector="^ Button">
<Setter Property="Height" Value="56" />
</Style>
<Style Selector="^ Button.anycontent > Border">
<Setter Property="Padding" Value="13" />
<Setter Property="CornerRadius" Value="10" />
<Style Selector="^ > Svg">
<Setter Property="Width" Value="30" />
<Setter Property="Height" Value="30" />
</Style>
</Style>
<Style Selector="^ Button:nth-child(2)">
<Setter Property="HorizontalAlignment" Value="Right" />
</Style>
</Style>
</Style>
</UserControl.Styles>
<ScrollViewer Classes="main">
<StackPanel
Background="{DynamicResource BrandWhite}"
Classes="viewPadding"
Spacing="23">
<TextBlock Classes="header" Text="Community" />
<Image MaxWidth="1580" Source="/Assets/Images/banners/community.png" />
<controls:FittingWrapPanel
Margin="-8,0"
MaxWidth="2500"
MinItemWidth="280">
<Border>
<StackPanel>
<Panel>
<Button
Classes="anycontent"
Command="{Binding DiscordLinkCommand}"
ToolTip.Tip="Join Discord Server">
<Border Background="#5865F2">
<Svg Path="/Assets/Icons/brand/discord.svg" />
</Border>
</Button>
<Button
Command="{Binding DiscordLinkCommand}"
Content="Join Discord"
ToolTip.Tip="Join Discord Server" />
</Panel>
<TextBlock Text="Discord" />
<TextBlock Text="Join our active Discord server to find servers to play on, get help from the support team and much more." />
</StackPanel>
</Border>
<Border>
<StackPanel>
<Grid>
<Button
Classes="anycontent"
Command="{Binding TwitterLinkCommand}"
ToolTip.Tip="Follow on Twitter">
<Border Background="#00AAEC">
<Svg Path="/Assets/Icons/brand/twitter.svg" />
</Border>
</Button>
<Button
Command="{Binding TwitterLinkCommand}"
Content="Follow"
ToolTip.Tip="Follow on Twitter" />
</Grid>
<TextBlock Text="Twitter" />
<TextBlock Text="Follow the Nitrox Twitter account to always be up to date on latest developments, events, and releases." />
</StackPanel>
</Border>
<Border>
<StackPanel>
<Grid>
<Button
Classes="anycontent"
Command="{Binding BlueskyLinkCommand}"
ToolTip.Tip="Follow on Bluesky">
<Border Background="#1185fe">
<Svg Path="/Assets/Icons/brand/bluesky.svg" />
</Border>
</Button>
<Button
Command="{Binding BlueskyLinkCommand}"
Content="Follow"
ToolTip.Tip="Follow on Bluesky" />
</Grid>
<TextBlock Text="Bluesky" />
<TextBlock Text="Follow the Nitrox Bluesky account to always be up to date on latest developments, events, and releases." />
</StackPanel>
</Border>
<Border>
<StackPanel>
<Grid>
<Button
Classes="anycontent"
Command="{Binding RedditLinkCommand}"
ToolTip.Tip="Join Nitrox Subreddit">
<Border Background="#FF4500">
<Svg Path="/Assets/Icons/brand/reddit.svg" />
</Border>
</Button>
<Button
Command="{Binding RedditLinkCommand}"
Content="Join Subreddit"
ToolTip.Tip="Join Nitrox Subreddit" />
</Grid>
<TextBlock Text="Reddit" />
<TextBlock Text="Join the Nitrox Subreddit to stay informed about latest releases and post your own Nitrox content." />
</StackPanel>
</Border>
<Border>
<StackPanel>
<Grid>
<Button
Classes="anycontent"
Command="{Binding GithubLinkCommand}"
ToolTip.Tip="Contribute on GitHub">
<Border Background="#000000">
<Svg Path="/Assets/Icons/brand/github.svg" />
</Border>
</Button>
<Button
Command="{Binding GithubLinkCommand}"
Content="Contribute"
ToolTip.Tip="Contribute on GitHub" />
</Grid>
<TextBlock Text="GitHub" />
<TextBlock Text="Want to be part of the team? Contribute from code to translations to the Nitrox project on GitHub." />
</StackPanel>
</Border>
</controls:FittingWrapPanel>
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,12 @@
using Nitrox.Launcher.ViewModels;
using Nitrox.Launcher.Views.Abstract;
namespace Nitrox.Launcher.Views;
public partial class CommunityView : RoutableViewBase<CommunityViewModel>
{
public CommunityView()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,76 @@
<Window
MinHeight="100"
MinWidth="800"
SizeToContent="WidthAndHeight"
Title="{Binding Title, TargetNullValue='Crash Report'}"
Width="1200"
WindowStartupLocation="CenterScreen"
d:DesignHeight="400"
d:DesignWidth="800"
mc:Ignorable="d"
x:Class="Nitrox.Launcher.Views.CrashWindow"
x:DataType="viewModels:CrashWindowViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:controls="clr-namespace:Nitrox.Launcher.Models.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:design="clr-namespace:Nitrox.Launcher.Models.Design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Nitrox.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.DataContext>
<viewModels:CrashWindowViewModel>
<!-- TODO: Why isn't displayed as multi-line? -->
<viewModels:CrashWindowViewModel.Message>
<![CDATA[System.Exception: oops
at Nitrox.Launcher.ViewModels.MainWindowViewModel.OpenOptionsViewAsync() in Nitrox.Launcher/ViewModels/MainWindowViewModel.cs:line 152
at CommunityToolkit.Mvvm.Input.AsyncRelayCommand.AwaitAndThrowIfFailed(Task executionTask)
at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0(Object state)
at Avalonia.Threading.SendOrPostCallbackDispatcherOperation.InvokeCore()
at Avalonia.Threading.DispatcherOperation.Execute()
at Avalonia.Threading.Dispatcher.ExecuteJob(DispatcherOperation job)
at Avalonia.Threading.Dispatcher.ExecuteJobsCore(Boolean fromExplicitBackgroundProcessingCallback)
at Avalonia.Threading.Dispatcher.Signaled()
at Avalonia.X11.X11PlatformThreading.CheckSignaled()
at Avalonia.X11.X11PlatformThreading.RunLoop(CancellationToken cancellationToken)
at Avalonia.Threading.DispatcherFrame.Run(IControlledDispatcherImpl impl)
at Avalonia.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
at Avalonia.Threading.Dispatcher.MainLoop(CancellationToken cancellationToken)
at Avalonia.Controls.ApplicationLifetimes.ClassicDesktopStyleApplicationLifetime.Start(String[] args)
at Avalonia.ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime(AppBuilder builder, String[] args, Action`1 lifetimeBuilder)
at Nitrox.Launcher.Program.LoadAvalonia(String[] args) in Nitrox.Launcher/Program.cs:line 117
]]>
</viewModels:CrashWindowViewModel.Message>
</viewModels:CrashWindowViewModel>
</Design.DataContext>
<DockPanel LastChildFill="True">
<controls:CustomTitlebar DockPanel.Dock="Top" IsVisible="{Binding $parent[Window].(design:NitroxAttached.UseCustomTitleBar)}" ShowTitle="True" />
<Grid RowDefinitions="*,Auto">
<ScrollViewer>
<!-- TODO: Nice color highlighting (with option to click on line to open the source file?) -->
<SelectableTextBlock
FontSize="14"
Foreground="#FF4444"
Padding="10"
Text="{Binding Message}" />
</ScrollViewer>
<Border
Grid.Row="1"
Background="{DynamicResource BrandPanelBackground}"
Padding="10">
<StackPanel
HorizontalAlignment="Right"
Orientation="Horizontal"
Spacing="10">
<Button
Background="Transparent"
Command="{Binding CopyToClipboardCommand}"
CommandParameter="{Binding $self}"
Content="Copy to clipboard"
HotKey="Ctrl+Shift+C" />
<Button Command="{Binding ReportCommand}" Content="Report on GitHub" />
<Button Command="{Binding RestartCommand}" Content="Restart Nitrox" />
</StackPanel>
</Border>
</Grid>
</DockPanel>
</Window>

View File

@@ -0,0 +1,12 @@
using Nitrox.Launcher.ViewModels;
using Nitrox.Launcher.Views.Abstract;
namespace Nitrox.Launcher.Views;
public partial class CrashWindow : WindowEx<CrashWindowViewModel>
{
public CrashWindow()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,64 @@
<Window
CanResize="False"
SizeToContent="WidthAndHeight"
Title="Create a server"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d"
x:Class="Nitrox.Launcher.Views.CreateServerModal"
x:DataType="vm:CreateServerViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:controls="clr-namespace:Nitrox.Launcher.Models.Controls"
xmlns:converters="clr-namespace:Nitrox.Launcher.Models.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:server="clr-namespace:NitroxModel.Server;assembly=NitroxModel"
xmlns:vm="clr-namespace:Nitrox.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:behaviors="clr-namespace:Nitrox.Launcher.Models.Behaviors">
<Design.DataContext>
<vm:CreateServerViewModel Name="My Server Name" SelectedGameMode="CREATIVE" />
</Design.DataContext>
<Grid>
<controls:CustomTitlebar CanMinimize="False" />
<StackPanel>
<StackPanel Classes="form" Margin="24">
<TextBlock Classes="modalHeader" Text="Create a server" />
<StackPanel Classes="form">
<TextBlock Text="SERVER NAME" />
<TextBox
MaxLength="120"
MaxWidth="400"
Text="{Binding Name, Converter={converters:TrimConverter}}"
Watermark="My server">
<Interaction.Behaviors>
<behaviors:FocusOnViewShowBehavior />
</Interaction.Behaviors>
</TextBox>
</StackPanel>
<StackPanel Classes="form">
<TextBlock Text="GAMEMODE" />
<controls:RadioButtonGroup
Classes="radioGroup"
Enum="{x:Type server:NitroxGameMode}"
SelectedItem="{Binding SelectedGameMode, Mode=TwoWay}" />
</StackPanel>
</StackPanel>
<Border Classes="footer">
<Panel>
<Button
Command="{Binding CloseCommand}"
Content="Back"
FontWeight="Bold"
HotKey="Escape" />
<Button
Classes="primary"
Command="{Binding CreateCommand}"
Content="Create"
HorizontalAlignment="Right"
HotKey="Enter" />
</Panel>
</Border>
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,11 @@
using Nitrox.Launcher.Views.Abstract;
namespace Nitrox.Launcher.Views;
public partial class CreateServerModal : ModalBase
{
public CreateServerModal()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,107 @@
<Window
CanResize="False"
MaxWidth="1000"
MinWidth="400"
SizeToContent="WidthAndHeight"
Title="{Binding WindowTitle, TargetNullValue='Dialog Window'}"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d"
x:Class="Nitrox.Launcher.Views.DialogBoxModal"
x:DataType="vm:DialogBoxViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:controls="clr-namespace:Nitrox.Launcher.Models.Controls"
xmlns:converters="clr-namespace:Nitrox.Launcher.Models.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Nitrox.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.DataContext>
<vm:DialogBoxViewModel Description="Description Text" Title="Title Text" />
</Design.DataContext>
<Grid RowDefinitions="Auto,*,Auto">
<controls:CustomTitlebar
CanMinimize="False"
Grid.RowSpan="2"
ShowTitle="False" />
<TextBlock
FontSize="{Binding TitleFontSize}"
FontWeight="{Binding TitleFontWeight}"
Grid.Row="0"
IsVisible="{Binding Title, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
Margin="24,20,24,-10"
Text="{Binding Title}"
TextWrapping="Wrap" />
<ScrollViewer
Grid.Row="1"
HorizontalScrollBarVisibility="Disabled"
Margin="24,20,10,20">
<SelectableTextBlock
FontSize="{Binding DescriptionFontSize}"
FontWeight="{Binding DescriptionFontWeight}"
IsVisible="{Binding Description, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
Margin="0,0,14,0"
Text="{Binding Description, FallbackValue=Dialog Description}" />
</ScrollViewer>
<Border Classes="footer" Grid.Row="2">
<Panel>
<!-- Ok Button Option -->
<Button
Classes="primary"
Command="{Binding CloseCommand}"
Content="OK"
FontWeight="Bold"
HorizontalAlignment="Right"
HorizontalContentAlignment="Center"
HotKey="{Binding OkHotkey}"
IsEnabled="{Binding ButtonOptions, Converter={converters:EqualityConverter}, ConverterParameter={x:Static vm:ButtonOptions.Ok}}"
IsVisible="{Binding ButtonOptions, Converter={converters:EqualityConverter}, ConverterParameter={x:Static vm:ButtonOptions.Ok}}"
Width="80" />
<!-- OkClipboard Button Option -->
<StackPanel
HorizontalAlignment="Right"
IsEnabled="{Binding ButtonOptions, Converter={converters:EqualityConverter}, ConverterParameter={x:Static vm:ButtonOptions.OkClipboard}}"
IsVisible="{Binding ButtonOptions, Converter={converters:EqualityConverter}, ConverterParameter={x:Static vm:ButtonOptions.OkClipboard}}"
Orientation="Horizontal"
Spacing="8">
<Button
Command="{Binding CopyToClipboardCommand}"
CommandParameter="{Binding $self}"
Content="Copy to clipboard"
HotKey="{Binding CopyToClipboardHotkey}" />
<Button
Classes="primary"
Command="{Binding CloseCommand}"
CommandParameter="{x:Static vm:ButtonOptions.Ok}"
Content="OK"
HotKey="{Binding OkHotkey}" />
</StackPanel>
<!-- YesNo Button Option -->
<Panel IsEnabled="{Binding ButtonOptions, Converter={converters:EqualityConverter}, ConverterParameter={x:Static vm:ButtonOptions.YesNo}}" IsVisible="{Binding ButtonOptions, Converter={converters:EqualityConverter}, ConverterParameter={x:Static vm:ButtonOptions.YesNo}}">
<!-- The yes button doesn't have a hotkey to prevent accidental confirmation. -->
<Button
Command="{Binding CloseCommand}"
CommandParameter="{x:Static vm:ButtonOptions.Yes}"
Content="Yes"
FontWeight="Bold"
HorizontalAlignment="Left"
HorizontalContentAlignment="Center"
Width="80" />
<Button
Classes="primary"
Command="{Binding CloseCommand}"
CommandParameter="{x:Static vm:ButtonOptions.No}"
Content="No"
HorizontalAlignment="Right"
HorizontalContentAlignment="Center"
HotKey="{Binding NoHotkey}"
Width="80" />
</Panel>
</Panel>
</Border>
</Grid>
</Window>

View File

@@ -0,0 +1,11 @@
using Nitrox.Launcher.Views.Abstract;
namespace Nitrox.Launcher.Views;
public partial class DialogBoxModal : ModalBase
{
public DialogBoxModal()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,152 @@
<UserControl
d:DesignWidth="700"
mc:Ignorable="d"
x:Class="Nitrox.Launcher.Views.EmbeddedServerView"
x:DataType="vm:EmbeddedServerViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Nitrox.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:behaviors="clr-namespace:Nitrox.Launcher.Models.Behaviors"
xmlns:controls="clr-namespace:Nitrox.Launcher.Models.Controls"
xmlns:design="clr-namespace:Nitrox.Launcher.Models.Design"
xmlns:converters="clr-namespace:Nitrox.Launcher.Models.Converters">
<Design.DataContext>
<vm:EmbeddedServerViewModel />
</Design.DataContext>
<UserControl.KeyBindings>
<KeyBinding
Command="{Binding ClearInputCommand}"
CommandParameter="{Binding #ServerCommandTextBox}"
Gesture="Escape" />
<KeyBinding
Command="{Binding CommandHistoryGoForwardCommand}"
CommandParameter="{Binding #ServerCommandTextBox}"
Gesture="Down" />
<KeyBinding
Command="{Binding CommandHistoryGoBackCommand}"
CommandParameter="{Binding #ServerCommandTextBox}"
Gesture="Up" />
</UserControl.KeyBindings>
<Grid Classes="viewPadding" RowDefinitions="Auto,*,Auto">
<Grid Grid.Row="0">
<Button
Background="Transparent"
Command="{Binding BackCommand}"
Cursor="Hand"
HorizontalAlignment="Left">
<Image
Height="24"
HorizontalAlignment="Left"
Source="/Assets/Images/world-manager/back.png"
Width="24" />
</Button>
<TextBlock
Classes="header"
HorizontalAlignment="Center"
Text="{Binding ServerEntry.Name}" />
<CheckBox
Content="Auto scroll"
HorizontalAlignment="Right"
IsChecked="{Binding ShouldAutoScroll}"
VerticalAlignment="Center" />
</Grid>
<ScrollViewer
Grid.Row="1" BringIntoViewOnFocusChange="False"
Margin="0,20,0,20"
x:Name="ScrollViewer">
<!-- Transparent background for improved QoL selecting text (otherwise: if cursor between lines won't allow selecting something) -->
<ItemsControl Background="Transparent" ItemsSource="{Binding ServerOutput}">
<ItemsControl.Styles>
<Style Selector="Run.command">
<Setter Property="Foreground" Value="{DynamicResource BrandPrimary}" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style Selector="Run.debug">
<Setter Property="Foreground" Value="{DynamicResource BrandSubText}" />
</Style>
<Style Selector="Run.warning">
<Setter Property="Foreground" Value="{DynamicResource BrandWarning}" />
</Style>
<Style Selector="Run.error">
<Setter Property="Foreground" Value="{DynamicResource BrandError}" />
</Style>
</ItemsControl.Styles>
<Interaction.Behaviors>
<BehaviorCollection>
<EventTriggerBehavior EventName="SizeChanged">
<InvokeCommandAction Command="{Binding OutputSizeChangedCommand}" PassEventArgsToCommand="True" />
</EventTriggerBehavior>
</BehaviorCollection>
</Interaction.Behaviors>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<!-- TODO: Fix highlighting only happening one line at a time -->
<DataTemplate x:DataType="design:OutputLine">
<SelectableTextBlock Focusable="False">
<Run Text="{Binding Timestamp}" Foreground="{DynamicResource BrandSubText}" />
<Run Text="" />
<Run Text="{Binding LogText}"
Classes.command="{Binding Type, Converter={converters:EqualityConverter}, ConverterParameter={x:Static design:OutputLineType.COMMAND}}"
Classes.debug="{Binding Type, Converter={converters:EqualityConverter}, ConverterParameter={x:Static design:OutputLineType.DEBUG_LOG}}"
Classes.warning="{Binding Type, Converter={converters:EqualityConverter}, ConverterParameter={x:Static design:OutputLineType.WARNING_LOG}}"
Classes.error="{Binding Type, Converter={converters:EqualityConverter}, ConverterParameter={x:Static design:OutputLineType.ERROR_LOG}}"
/>
</SelectableTextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<Grid ColumnDefinitions="*,Auto" Grid.Row="2">
<TextBox
Grid.Column="0"
Text="{Binding ServerCommand}"
Watermark="A server command here"
x:Name="ServerCommandTextBox">
<Interaction.Behaviors>
<behaviors:FocusOnViewShowBehavior />
</Interaction.Behaviors>
</TextBox>
<StackPanel
Grid.Column="1"
Margin="12,0,0,0"
Orientation="Horizontal"
Spacing="12">
<StackPanel.Styles>
<Style Selector="Button">
<Setter Property="Width" Value="60" />
<Setter Property="VerticalAlignment" Value="Stretch" />
</Style>
</StackPanel.Styles>
<Button
Classes="primary"
Command="{Binding SendServerCommand}"
CommandParameter="{Binding #ServerCommandTextBox}"
Cursor="Hand"
HotKey="Enter"
ToolTip.Tip="Send command">
<Svg
Classes="theme"
Height="20"
Path="/Assets/Icons/send.svg" />
</Button>
<Button
Classes="abort"
Command="{Binding StopServerCommand}"
CommandParameter="{Binding #ServerCommandTextBox}"
Cursor="Hand"
ToolTip.Tip="Stop the server">
<Rectangle
Fill="White"
Height="20"
Width="20" />
</Button>
</StackPanel>
</Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,12 @@
using Nitrox.Launcher.ViewModels;
using Nitrox.Launcher.Views.Abstract;
namespace Nitrox.Launcher.Views;
public partial class EmbeddedServerView : RoutableViewBase<EmbeddedServerViewModel>
{
public EmbeddedServerView()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,160 @@
<UserControl
x:Class="Nitrox.Launcher.Views.LaunchGameView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Nitrox.Launcher.Models.Controls"
xmlns:converters="clr-namespace:Nitrox.Launcher.Models.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Nitrox.Launcher.ViewModels"
xmlns:behaviors="clr-namespace:Nitrox.Launcher.Models.Behaviors"
d:DesignWidth="1000"
x:DataType="vm:LaunchGameViewModel"
mc:Ignorable="d">
<Design.DataContext>
<vm:LaunchGameViewModel />
</Design.DataContext>
<ScrollViewer Classes="main">
<StackPanel
Margin="20,0,20,20"
Background="{DynamicResource BrandWhite}"
Spacing="20">
<Panel Height="535" Margin="-20,0">
<Image Source="/Assets/Images/banners/home.png" Stretch="UniformToFill" />
<Image
Height="48"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="/Assets/Images/subnautica-name.png"
Stretch="Uniform" />
</Panel>
<Grid ColumnDefinitions="*,*">
<TextBlock
Grid.Column="0"
Margin="0,0,20,0"
FontSize="14"
Text="Start your Subnautica adventure in multiplayer mode together with your friends, powered by the Nitrox mod: An open-source, modification for the game Subnautica. The project is maintained by the community with regular support and updates from its contributors." />
<!-- Play buttons - Negative offset to add some overlap with the background image -->
<StackPanel Grid.Column="1" Margin="0,-110,0,0">
<Border ClipToBounds="True" CornerRadius="10">
<controls:BlurControl BlurStrength="7">
<Border Background="{DynamicResource BrandPanelBackground}" Opacity=".90">
<StackPanel
Margin="24,20"
Orientation="Vertical"
Spacing="9">
<Grid
Height="26"
Margin="0,0,0,9"
HorizontalAlignment="Stretch">
<StackPanel HorizontalAlignment="Left" Orientation="Vertical">
<StackPanel.Styles>
<Style Selector="TextBlock">
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="FontSize" Value="10" />
</Style>
</StackPanel.Styles>
<TextBlock Foreground="{DynamicResource BrandSubText}" Text="PLAY NITROX" />
<TextBlock Text="{Binding Version, FallbackValue='PHASE x.x.x.x'}" />
</StackPanel>
<Image
Width="19"
Height="19"
Margin="3,0"
HorizontalAlignment="Right"
Source="{Binding GamePlatform, Converter={converters:PlatformToIconConverter}}"
ToolTip.Tip="{Binding PlatformToolTip}" />
</Grid>
<Button
HorizontalAlignment="Stretch"
Classes="big primary"
Command="{Binding StartMultiplayerCommand}"
ToolTip.Tip="Launch Subnautica with multiplayer enabled">
<Interaction.Behaviors>
<behaviors:FocusOnViewShowBehavior />
</Interaction.Behaviors>
<StackPanel>
<TextBlock Text="PLAY" />
<TextBlock Text="MULTIPLAYER" />
</StackPanel>
</Button>
<Button
Height="48"
HorizontalAlignment="Stretch"
Classes="big"
Command="{Binding StartSingleplayerCommand}"
ToolTip.Tip="Launch Subnautica">
<TextBlock
Margin="0,-4,0,0"
Padding="0,4,0,0"
HorizontalAlignment="Center"
FontSize="14"
Foreground="White"
Text="SINGLEPLAYER" />
</Button>
</StackPanel>
</Border>
</controls:BlurControl>
</Border>
</StackPanel>
</Grid>
<ItemsControl ItemsSource="{Binding GalleryImageSources, Mode=OneTime}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding GalleryImageSources, Converter={converters:ToIntConverter}}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border
Margin="6,0"
ClipToBounds="True"
CornerRadius="12">
<Image Source="{Binding}" Stretch="UniformToFill" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel Orientation="Vertical">
<TextBlock
Margin="0,11,0,12"
FontWeight="Bold"
Text="Contributors" />
<StackPanel Margin="0,11,0,0" Spacing="6">
<StackPanel.Styles>
<Style Selector="StackPanel > TextBlock:nth-child(2n+1)">
<Setter Property="FontSize" Value="10" />
<Setter Property="Foreground" Value="{DynamicResource BrandSubText}" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style Selector="StackPanel > TextBlock:nth-child(2n)">
<Setter Property="LineHeight" Value="24" />
</Style>
</StackPanel.Styles>
<TextBlock Text="AUTHOR" />
<controls:RichTextBlock>[Sunrunner](github.com/Sunrunner37)</controls:RichTextBlock>
<!-- Active contributors which have higher role than junior. -->
<TextBlock Text="CONTRIBUTORS" />
<WrapPanel>
<TextBlock Margin="0 0 12 0" VerticalAlignment="Center" Text="Tornac, iCleeem, Jannify, _HeN_, spacemonkeyy, Ohm, NinjaPedroX, Meas" />
<Button Padding="5 2" Content="See contributions made in the last year" Command="{Binding OpenContributionsOfYearCommand}" />
</WrapPanel>
<TextBlock Text="STAFF" />
<TextBlock>
Werewolfs, Shalix, SavageJay, and a big thanks to the discord support team (Arctic-Peepers, Peepers)
</TextBlock>
<TextBlock Text="DESIGN" />
<controls:RichTextBlock>
[Rux](rux.gg)
</controls:RichTextBlock>
</StackPanel>
</StackPanel>
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,12 @@
using Nitrox.Launcher.ViewModels;
using Nitrox.Launcher.Views.Abstract;
namespace Nitrox.Launcher.Views;
public partial class LaunchGameView : RoutableViewBase<LaunchGameViewModel>
{
public LaunchGameView()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,20 @@
<UserControl
d:DesignWidth="1000"
mc:Ignorable="d"
x:Class="Nitrox.Launcher.Views.LibraryView"
x:DataType="vm:LibraryViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Nitrox.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.DataContext>
<vm:LibraryViewModel />
</Design.DataContext>
<!-- TODO: WIP -->
<ScrollViewer Classes="main">
<Grid Background="{DynamicResource BrandWhite}">
<TextBlock Text="Library View" />
</Grid>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,12 @@
using Nitrox.Launcher.ViewModels;
using Nitrox.Launcher.Views.Abstract;
namespace Nitrox.Launcher.Views;
public partial class LibraryView : RoutableViewBase<LibraryViewModel>
{
public LibraryView()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,405 @@
<Window
Height="720"
MinHeight="600"
MinWidth="800"
Title="Nitrox Launcher"
Width="1200"
WindowStartupLocation="CenterScreen"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d"
x:Class="Nitrox.Launcher.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:controls="clr-namespace:Nitrox.Launcher.Models.Controls"
xmlns:converters="clr-namespace:Nitrox.Launcher.Models.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:design="clr-namespace:Nitrox.Launcher.Models.Design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:views="clr-namespace:Nitrox.Launcher.Views"
xmlns:vm="clr-namespace:Nitrox.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:generic="clr-namespace:System.Collections.Generic;assembly=System.Collections">
<Design.DataContext>
<vm:MainWindowViewModel />
</Design.DataContext>
<Window.Styles>
<Style Selector="Border.nav">
<Setter Property="Background" Value="{DynamicResource BrandPanelBackground}" />
<Style Selector="^ TextBlock.header">
<Setter Property="Margin" Value="34 28 0 20" />
<Setter Property="FontFamily" Value="Barlow" />
<Setter Property="FontStyle" Value="Italic" />
</Style>
<Style Selector="^ TextBlock.subheader">
<Setter Property="FontSize" Value="10" />
<Setter Property="Margin" Value="34 20 0 10" />
<Setter Property="Opacity" Value="0.5" />
</Style>
<Style Selector="^ RadioButton">
<Setter Property="GroupName" Value="nav" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="FontSize" Value="16" />
<Setter Property="Padding" Value="34 10 10 10" />
<!-- Scale to max horizontal so user can more easily click the navigation. -->
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Opacity" Value="0.7" />
<Setter Property="ToolTip.Placement" Value="Pointer" />
<Setter Property="Template">
<ControlTemplate>
<Panel Background="{TemplateBinding Background}"
HorizontalAlignment="Stretch">
<Grid Margin="{TemplateBinding Padding}"
ColumnDefinitions="48,*">
<Image Grid.Column="0"
Source="{TemplateBinding Tag, Converter={converters:BitmapAssetValueConverter}}"
Stretch="UniformToFill" />
<TextBlock Grid.Column="1"
TextAlignment="Start"
VerticalAlignment="Center"
FontSize="{TemplateBinding FontSize}"
Text="{TemplateBinding Content}" />
</Grid>
</Panel>
</ControlTemplate>
</Setter>
<Style Selector="^ Image">
<Setter Property="Width" Value="24" />
<Setter Property="Height" Value="24" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style Selector="^[IsChecked=True]">
<Setter Property="Opacity" Value="1" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
</Style>
</Style>
<Style Selector=".notifications Border">
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Duration="0:0:.2" Property="Opacity" />
</Transitions>
</Setter>
<Style Selector="^.info">
<Setter Property="Background" Value="{DynamicResource BrandInformation}" />
<Style Selector="^ :is(Svg).icon">
<Setter Property="Path" Value="/Assets/Icons/info.svg" />
</Style>
</Style>
<Style Selector="^.success">
<Setter Property="Background" Value="{DynamicResource BrandSuccess}" />
<Style Selector="^ :is(Svg).icon">
<Setter Property="Path" Value="/Assets/Icons/success.svg" />
</Style>
</Style>
<Style Selector="^.warning">
<Setter Property="Background" Value="{DynamicResource BrandWarning}" />
<Style Selector="^ :is(Svg).icon">
<Setter Property="Path" Value="/Assets/Icons/warning.svg" />
</Style>
</Style>
<Style Selector="^.error">
<Setter Property="Background" Value="{DynamicResource BrandError}" />
<Style Selector="^ :is(Svg).icon">
<Setter Property="Path" Value="/Assets/Icons/error.svg" />
</Style>
</Style>
<Style Selector="^:not(.dismiss)">
<Style Selector="^:not(:pointerover)">
<Setter Property="Opacity" Value=".98" />
</Style>
<Style Selector="^:pointerover">
<Setter Property="Opacity" Value=".9" />
</Style>
<Style.Animations>
<Animation Duration="0:0:0:.2">
<KeyFrame Cue="0%">
<Setter Property="Opacity" Value="0.0" />
<Setter Property="TranslateTransform.X" Value="280" />
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="1.0" />
<Setter Property="TranslateTransform.X" Value="0" />
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
<Style Selector="^.dismiss">
<Style.Animations>
<Animation Duration="0:0:0:.2" FillMode="Forward">
<KeyFrame Cue="0%">
<Setter Property="Opacity" Value="1.0" />
<Setter Property="TranslateTransform.X" Value="0" />
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="0.0" />
<Setter Property="TranslateTransform.X" Value="280" />
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</Style>
</Window.Styles>
<Interaction.Behaviors>
<BehaviorCollection>
<EventTriggerBehavior EventName="Closing">
<InvokeCommandAction Command="{Binding ClosingCommand}" PassEventArgsToCommand="True" />
</EventTriggerBehavior>
</BehaviorCollection>
</Interaction.Behaviors>
<Panel>
<controls:CustomTitlebar IsVisible="{Binding $parent[Window].(design:NitroxAttached.UseCustomTitleBar)}" Opacity="0.6" />
<Grid ColumnDefinitions="274,*" RowDefinitions="*">
<!-- Navigation (left side) -->
<Border Classes="nav" Grid.Column="0">
<Grid RowDefinitions="80,*">
<TextBlock Classes="header">NITROX</TextBlock>
<Panel Grid.Row="1">
<StackPanel Classes="nav">
<TextBlock Classes="subheader">PLAY</TextBlock>
<RadioButton
Command="{Binding OpenLaunchGameViewCommand}"
Content="Play game"
Tag="/Assets/Images/tabs-icons/play.png"
ToolTip.Tip="Play game">
<RadioButton.IsChecked>
<Binding Path="ActiveViewModel" Converter="{converters:IsTypeConverter}" Mode="OneWay">
<Binding.ConverterParameter>
<generic:List x:TypeArguments="system:Type">
<x:Type TypeName="vm:LaunchGameViewModel" />
</generic:List>
</Binding.ConverterParameter>
</Binding>
</RadioButton.IsChecked>
</RadioButton>
<RadioButton
Command="{Binding OpenServersViewCommand}"
Content="Servers"
Tag="/Assets/Images/tabs-icons/server.png"
ToolTip.Tip="Configure and start servers">
<RadioButton.IsChecked>
<Binding Path="ActiveViewModel" Converter="{converters:IsTypeConverter}" Mode="OneWay">
<Binding.ConverterParameter>
<generic:List x:TypeArguments="system:Type">
<x:Type TypeName="vm:ServersViewModel" />
<x:Type TypeName="vm:ManageServerViewModel" />
<x:Type TypeName="vm:EmbeddedServerViewModel" />
</generic:List>
</Binding.ConverterParameter>
</Binding>
</RadioButton.IsChecked>
</RadioButton>
<TextBlock Classes="subheader">EXPLORE</TextBlock>
<RadioButton
Command="{Binding OpenCommunityViewCommand}"
Content="Community"
Tag="/Assets/Images/tabs-icons/community.png"
ToolTip.Tip="Join Nitrox community">
<RadioButton.IsChecked>
<Binding Path="ActiveViewModel" Converter="{converters:IsTypeConverter}" Mode="OneWay">
<Binding.ConverterParameter>
<generic:List x:TypeArguments="system:Type">
<x:Type TypeName="vm:CommunityViewModel" />
</generic:List>
</Binding.ConverterParameter>
</Binding>
</RadioButton.IsChecked>
</RadioButton>
<RadioButton
Command="{Binding OpenBlogViewCommand}"
Content="Blog"
Tag="/Assets/Images/tabs-icons/blog.png"
ToolTip.Tip="Read latest news from the Dev Blog">
<RadioButton.IsChecked>
<Binding Path="ActiveViewModel" Converter="{converters:IsTypeConverter}" Mode="OneWay">
<Binding.ConverterParameter>
<generic:List x:TypeArguments="system:Type">
<x:Type TypeName="vm:BlogViewModel" />
</generic:List>
</Binding.ConverterParameter>
</Binding>
</RadioButton.IsChecked>
</RadioButton>
</StackPanel>
<StackPanel
Classes="nav"
Margin="0,0,0,20"
VerticalAlignment="Bottom">
<RadioButton
Command="{Binding OpenUpdatesViewCommand}"
Content="Updates"
Tag="{Binding UpdateAvailableOrUnofficial, Converter={converters:BoolToIconConverter True='/Assets/Images/tabs-icons/update-available.png', False='/Assets/Images/tabs-icons/update.png'}}"
ToolTip.Tip="Check latest updates from Nitrox">
<RadioButton.IsChecked>
<Binding Path="ActiveViewModel" Converter="{converters:IsTypeConverter}" Mode="OneWay">
<Binding.ConverterParameter>
<generic:List x:TypeArguments="system:Type">
<x:Type TypeName="vm:UpdatesViewModel" />
</generic:List>
</Binding.ConverterParameter>
</Binding>
</RadioButton.IsChecked>
</RadioButton>
<RadioButton
Command="{Binding OpenOptionsViewCommand}"
Content="Options"
Tag="/Assets/Images/tabs-icons/options.png"
ToolTip.Tip="Configure your Nitrox setup">
<RadioButton.IsChecked>
<Binding Path="ActiveViewModel" Converter="{converters:IsTypeConverter}" Mode="OneWay">
<Binding.ConverterParameter>
<generic:List x:TypeArguments="system:Type">
<x:Type TypeName="vm:OptionsViewModel" />
</generic:List>
</Binding.ConverterParameter>
</Binding>
</RadioButton.IsChecked>
</RadioButton>
</StackPanel>
</Panel>
</Grid>
</Border>
<!-- Navigated content (right side) -->
<ContentControl
Classes="content"
Content="{Binding ActiveViewModel, TargetNullValue='No view set. You should not see this message.'}"
Grid.Column="1">
<ContentControl.ContentTemplate>
<design:MultiDataTemplate>
<!-- Null view (which will be a string, see TargetNullValue) -->
<DataTemplate x:DataType="system:String">
<TextBox
Focusable="False"
FontSize="24"
FontWeight="Bold"
HorizontalAlignment="Center"
IsHitTestVisible="False"
IsReadOnly="True"
Text="{Binding}"
TextWrapping="Wrap"
VerticalAlignment="Center" />
</DataTemplate>
<!-- Loading view -->
<DataTemplate x:DataType="system:Uri">
<Svg Path="{Binding AbsolutePath}" Classes="theme" Height="50"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Svg.RenderTransform>
<TransformGroup>
<RotateTransform/>
</TransformGroup>
</Svg.RenderTransform>
<Svg.Styles>
<Style Selector="Svg">
<Style.Animations>
<Animation Duration="0:0:.5" IterationCount="INFINITE">
<KeyFrame Cue="0%">
<Setter Property="RotateTransform.Angle" Value="0" />
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="RotateTransform.Angle" Value="360" />
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</Svg.Styles>
</Svg>
</DataTemplate>
<DataTemplate x:DataType="vm:LaunchGameViewModel">
<views:LaunchGameView />
</DataTemplate>
<DataTemplate x:DataType="vm:ServersViewModel">
<views:ServersView />
</DataTemplate>
<DataTemplate x:DataType="vm:ManageServerViewModel">
<views:ManageServerView />
</DataTemplate>
<DataTemplate x:DataType="vm:EmbeddedServerViewModel">
<views:EmbeddedServerView />
</DataTemplate>
<DataTemplate x:DataType="vm:CommunityViewModel">
<views:CommunityView />
</DataTemplate>
<DataTemplate x:DataType="vm:BlogViewModel">
<views:BlogView />
</DataTemplate>
<DataTemplate x:DataType="vm:UpdatesViewModel">
<views:UpdatesView />
</DataTemplate>
<DataTemplate x:DataType="vm:OptionsViewModel">
<views:OptionsView />
</DataTemplate>
<!-- Fallback view. This DataTemplate must be kept at the bottom. -->
<DataTemplate x:DataType="system:Object">
<TextBlock>
<Run Text="No view for" />
<Run Text="{Binding}" />
</TextBlock>
</DataTemplate>
</design:MultiDataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
<!-- Notifications -->
<Grid Classes="notifications" ColumnDefinitions="*,280">
<ItemsControl
Grid.Column="1"
ItemsSource="{Binding Notifications}"
Margin="0,10,10,10"
Opacity="0.95"
VerticalAlignment="Bottom">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border
Classes.dismiss="{Binding Dismissed}"
Classes.error="{Binding Type, Converter={converters:EqualityConverter}, ConverterParameter={x:Static NotificationType.Error}}"
Classes.info="{Binding Type, Converter={converters:EqualityConverter}, ConverterParameter={x:Static NotificationType.Information}}"
Classes.success="{Binding Type, Converter={converters:EqualityConverter}, ConverterParameter={x:Static NotificationType.Success}}"
Classes.warning="{Binding Type, Converter={converters:EqualityConverter}, ConverterParameter={x:Static NotificationType.Warning}}"
CornerRadius="5"
Margin="5"
MinHeight="60"
Padding="5,10,10,10">
<Grid ColumnDefinitions="50,*,20">
<Svg
Classes="theme icon"
Grid.Column="0"
Margin="0,0,5,0"
VerticalAlignment="Center"
Width="25" />
<TextBlock
Grid.Column="1"
Text="{Binding Message}"
VerticalAlignment="Center" />
<Button
Classes="anycontent"
Command="{Binding CloseCommand}"
Grid.Column="2"
HorizontalAlignment="Right"
VerticalAlignment="Top">
<Svg
Classes="theme"
Path="/Assets/Icons/close.svg"
VerticalAlignment="Top"
Width="10" />
</Button>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Panel>
</Window>

View File

@@ -0,0 +1,24 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Reactive;
using Nitrox.Launcher.Models.Utils;
using Nitrox.Launcher.ViewModels;
namespace Nitrox.Launcher.Views;
public partial class MainWindow : Abstract.WindowEx<MainWindowViewModel>
{
public MainWindow()
{
InitializeComponent();
PointerPressedEvent.Raised.Subscribe(new AnonymousObserver<(object, RoutedEventArgs)>(args =>
{
if (args.Item2 is { Handled: false, Source: Control { Tag: string url } control } && control.Classes.Contains("link"))
{
ProcessUtils.OpenUrl(url);
args.Item2.Handled = true;
}
}));
}
}

View File

@@ -0,0 +1,463 @@
<UserControl
d:DesignWidth="700"
mc:Ignorable="d"
x:Class="Nitrox.Launcher.Views.ManageServerView"
x:DataType="vm:ManageServerViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:controls="clr-namespace:Nitrox.Launcher.Models.Controls"
xmlns:converters="clr-namespace:Nitrox.Launcher.Models.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:design="clr-namespace:Nitrox.Launcher.Models.Design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:server="clr-namespace:NitroxModel.Server;assembly=NitroxModel"
xmlns:vm="clr-namespace:Nitrox.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:behaviors="clr-namespace:Nitrox.Launcher.Models.Behaviors">
<Design.DataContext>
<vm:ManageServerViewModel ServerName="My fun server" />
</Design.DataContext>
<UserControl.KeyBindings>
<KeyBinding Command="{Binding ForceDeleteServerCommand}" Gesture="Shift+Delete" />
</UserControl.KeyBindings>
<Grid
Background="{DynamicResource BrandWhite}"
Classes="viewPadding"
RowDefinitions="Auto,*,Auto">
<StackPanel>
<Button
Background="Transparent"
Command="{Binding BackCommand}"
Cursor="Hand"
HotKey="Escape"
Margin="0,0,0,26">
<Image
Height="24"
HorizontalAlignment="Left"
Source="/Assets/Images/world-manager/back.png"
Width="24" />
</Button>
<Grid ColumnDefinitions="Auto,*">
<StackPanel Margin="0,0,20,0">
<Border
ClipToBounds="True"
CornerRadius="12"
Height="72">
<Grid>
<Button
Classes="anycontent"
Command="{Binding ChangeServerIconCommand}"
IsHitTestVisible="{Binding !ServerIsOnline}"
ToolTip.Tip="Set a custom server icon">
<Grid>
<Image IsVisible="{Binding ServerIcon, Converter={converters:EqualityConverter}}" Source="/Assets/Images/subnautica-icon.png" />
<Border
CornerRadius="12"
Height="70"
Width="70">
<Border.Background>
<ImageBrush Source="{Binding ServerIcon}" Stretch="UniformToFill" />
</Border.Background>
</Border>
</Grid>
<Button.Styles>
<Style Selector="Button:pointerover /template/ ContentPresenter">
<Setter Property="Cursor" Value="Hand" />
</Style>
</Button.Styles>
</Button>
<controls:GrayscaleControl IsHitTestVisible="False" Opacity="{Binding !ServerIsOnline}">
<controls:GrayscaleControl.Transitions>
<Transitions>
<DoubleTransition Duration="0:0:0.10" Property="Opacity" />
</Transitions>
</controls:GrayscaleControl.Transitions>
</controls:GrayscaleControl>
</Grid>
</Border>
<TextBlock
FontSize="7.5"
Foreground="{DynamicResource BrandSubText}"
HorizontalAlignment="Center"
Text="Click to change icon" />
</StackPanel>
<StackPanel Grid.Column="1">
<StackPanel
Classes="description"
HorizontalAlignment="Left"
Margin="0,0,0,10"
Orientation="Horizontal"
Width="223">
<StackPanel.Styles>
<Style Selector="StackPanel.description > :is(Control)">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="6 0" />
</Style>
<Style Selector="StackPanel.description > :is(Control):nth-child(1)">
<Setter Property="Margin" Value="0 0 6 0" />
</Style>
<Style Selector="StackPanel.description > :is(Control):nth-last-child(1)">
<Setter Property="Margin" Value="6 0 0 0" />
</Style>
<Style Selector="Ellipse">
<Setter Property="Height" Value="6" />
<Setter Property="Width" Value="6" />
</Style>
<Style Selector="TextBlock">
<Setter Property="Opacity" Value="0.5" />
</Style>
</StackPanel.Styles>
<Panel>
<StackPanel IsVisible="{Binding ServerIsOnline}" Orientation="Horizontal">
<Ellipse Fill="{DynamicResource BrandOnColor}" Margin="0,0,6,0" />
<TextBlock IsVisible="{Binding ServerIsOnline}" Text="Online" />
</StackPanel>
<StackPanel IsVisible="{Binding !ServerIsOnline}" Orientation="Horizontal">
<Ellipse Fill="{DynamicResource BrandOffColor}" Margin="0,0,6,0" />
<TextBlock IsVisible="{Binding !ServerIsOnline}" Text="Offline" />
</StackPanel>
</Panel>
<TextBlock Text="{Binding ServerGameMode, Converter={converters:ToStringConverter}}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ServerPlayers}" />
<TextBlock Text="/" />
<TextBlock Text="{Binding ServerMaxPlayers}" />
<TextBlock Margin="6,0,0,0" Text="playing" />
</StackPanel>
</StackPanel>
<Grid ColumnDefinitions="*,200">
<ScrollViewer
Grid.Column="0"
Margin="0,0,10,0"
design:NitroxAttached.PrimaryScrollWheelDirection="Horizontal">
<TextBlock
Classes="header"
Foreground="White"
Text="{Binding ServerName}" />
</ScrollViewer>
<StackPanel
Grid.Column="1"
Height="40"
HorizontalAlignment="Right"
Orientation="Horizontal"
Spacing="5">
<Button
Classes="abort"
Command="{Binding UndoCommand}"
Content="UNDO"
HorizontalAlignment="Stretch"
HotKey="Ctrl+Alt+Z" />
<Button
Classes="primary"
Command="{Binding SaveCommand}"
Content="SAVE"
HorizontalAlignment="Stretch"
HotKey="Ctrl+S"
Margin="5,0,0,0" />
</StackPanel>
</Grid>
</StackPanel>
</Grid>
</StackPanel>
<ScrollViewer
Grid.Row="1"
HorizontalScrollBarVisibility="Disabled"
Margin="0,26"
design:NitroxAttached.AutoScrollToHome="True">
<StackPanel Spacing="42" VerticalAlignment="Stretch">
<StackPanel.Styles>
<Style Selector="ScrollViewer > StackPanel > Grid > StackPanel > TextBlock, ScrollViewer > StackPanel > StackPanel > TextBlock">
<Setter Property="Margin" Value="0 0 0 11" />
</Style>
</StackPanel.Styles>
<Grid ColumnDefinitions="*,30,*">
<StackPanel>
<TextBlock
FontSize="10"
FontWeight="700"
Opacity=".5"
Text="SERVER NAME" />
<TextBox
IsEnabled="{Binding !ServerIsOnline}"
Text="{Binding ServerName, Converter={converters:TrimConverter}}"
Watermark="My server">
<Interaction.Behaviors>
<behaviors:FocusOnViewShowBehavior />
</Interaction.Behaviors>
</TextBox>
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock
FontSize="10"
FontWeight="700"
Opacity=".5"
Text="PASSWORD" />
<TextBox
Classes="revealPasswordButton"
IsEnabled="{Binding !ServerIsOnline}"
Text="{Binding ServerPassword, Converter={converters:TrimConverter}}"
Watermark="Server password" />
</StackPanel>
</Grid>
<StackPanel>
<TextBlock
FontSize="10"
FontWeight="700"
Opacity=".5"
Text="GAMEMODE" />
<controls:RadioButtonGroup
Classes="radioGroup"
Enum="{x:Type server:NitroxGameMode}"
IsEnabled="{Binding !ServerIsOnline}"
SelectedItem="{Binding ServerGameMode, Mode=TwoWay}" />
</StackPanel>
<Grid ColumnDefinitions="*,30,*,30,*">
<StackPanel>
<TextBlock
FontSize="10"
FontWeight="700"
Opacity=".5"
Text="SEED" />
<TextBox
IsEnabled="{Binding !ServerIsOnline}"
IsReadOnly="{Binding !Server.IsNewServer}"
MaxLength="10"
Text="{Binding ServerSeed, Converter={converters:ToStringConverter}, ConverterParameter=upper}"
Watermark="Server seed">
<TextBox.Styles>
<Style Selector="TextBox[IsReadOnly=False]">
<Setter Property="ToolTip.Tip" Value="Leave blank to automatically generate one" />
</Style>
<Style Selector="TextBox[IsReadOnly=True]">
<Setter Property="ToolTip.Tip" Value="Seed cannot be changed for servers that have been run" />
</Style>
</TextBox.Styles>
</TextBox>
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock
FontSize="10"
FontWeight="700"
Opacity=".5"
Text="DEFAULT PERMISSIONS" />
<ComboBox
FontSize="13"
HorizontalAlignment="Stretch"
IsEnabled="{Binding !ServerIsOnline}"
ItemsSource="{Binding PlayerPerms}"
PlaceholderText="Default Perm"
SelectedValue="{Binding ServerDefaultPlayerPerm}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={converters:ToStringConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
<StackPanel Grid.Column="4">
<TextBlock
FontSize="10"
FontWeight="700"
Opacity=".5"
Text="AUTO SAVE INTERVAL (s)" />
<TextBox
FontSize="13"
HorizontalAlignment="Stretch"
IsEnabled="{Binding !ServerIsOnline}"
MaxLength="9"
Text="{Binding ServerAutoSaveInterval, Converter={converters:IntToStringConverter}}"
Watermark="Autosave interval"
design:NitroxAttached.IsNumericInput="True" />
</StackPanel>
</Grid>
<Grid ColumnDefinitions="*,30,*">
<StackPanel>
<TextBlock
FontSize="10"
FontWeight="700"
Opacity=".5"
Text="PLAYER LIMIT" />
<TextBox
IsEnabled="{Binding !ServerIsOnline}"
MaxLength="9"
Text="{Binding ServerMaxPlayers, Converter={converters:IntToStringConverter}}"
Watermark="Enter a player limit"
design:NitroxAttached.IsNumericInput="True" />
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock
FontSize="10"
FontWeight="700"
Opacity=".5"
Text="SERVER PORT" />
<TextBox
IsEnabled="{Binding !ServerIsOnline}"
MaxLength="5"
Text="{Binding ServerPort, Converter={converters:IntToStringConverter}}"
Watermark="Enter a port number"
design:NitroxAttached.IsNumericInput="True" />
</StackPanel>
</Grid>
<StackPanel>
<TextBlock
FontSize="10"
FontWeight="700"
Margin="0,0,0,15"
Opacity=".5"
Text="OPTIONS" />
<StackPanel IsEnabled="{Binding !ServerIsOnline}" Spacing="20">
<StackPanel.Styles>
<Style Selector="Grid > TextBlock">
<Setter Property="FontSize" Value="16" />
<Setter Property="FontWeight" Value="400" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</StackPanel.Styles>
<Grid>
<TextBlock Text="Enable auto port forwarding" />
<CheckBox
Classes="switch"
HorizontalAlignment="Right"
IsChecked="{Binding ServerAutoPortForward}" />
</Grid>
<Grid>
<TextBlock Text="Allow LAN Discovery" />
<CheckBox
Classes="switch"
HorizontalAlignment="Right"
IsChecked="{Binding ServerAllowLanDiscovery}" />
</Grid>
<Grid>
<TextBlock Text="Enable Commands" />
<CheckBox
Classes="switch"
HorizontalAlignment="Right"
IsChecked="{Binding ServerAllowCommands}" />
</Grid>
</StackPanel>
</StackPanel>
<StackPanel>
<TextBlock
FontSize="10"
FontWeight="700"
Margin="0,0,0,20"
Opacity=".5"
Text="ADVANCED" />
<StackPanel Spacing="20">
<StackPanel.Styles>
<Style Selector="Button">
<Setter Property="FontSize" Value="16" />
<Setter Property="FontWeight" Value="400" />
</Style>
</StackPanel.Styles>
<Button Classes="icon" Command="{Binding ShowAdvancedSettingsCommand}">
<StackPanel>
<Image Source="/Assets/Images/world-manager/cog.png" />
<TextBlock Text="Advanced settings" />
</StackPanel>
</Button>
<Button Classes="icon" Command="{Binding OpenWorldFolderCommand}">
<StackPanel>
<Image Source="/Assets/Images/world-manager/window.png" />
<TextBlock Text="Open world folder" />
</StackPanel>
</Button>
<Button Classes="icon" Command="{Binding RestoreBackupCommand}">
<StackPanel>
<Image Source="/Assets/Images/world-manager/export.png" />
<TextBlock Text="Restore a backup" />
</StackPanel>
</Button>
<Button
Classes="icon"
Command="{Binding DeleteServerCommand}"
Foreground="#ec1c24"
HotKey="Delete">
<StackPanel>
<Image Source="/Assets/Images/world-manager/delete.png" />
<TextBlock Text="Delete server" />
</StackPanel>
</Button>
</StackPanel>
</StackPanel>
</StackPanel>
</ScrollViewer>
<Border
Background="{DynamicResource BrandPanelBackground}"
CornerRadius="12"
Grid.Row="2"
Padding="25,17">
<Grid
ColumnDefinitions="*,*"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<StackPanel Grid.Column="0"
IsEnabled="{Binding !ServerIsOnline}"
Background="Transparent">
<OnPlatform>
<OnPlatform.Default>
<StackPanel>
<RadioButton Content="External"
GroupName="Choose runtime option"
IsChecked="{Binding !ServerEmbedded}"
ToolTip.ShowOnDisabled="True"
ToolTip.Tip="Run server in external mode as a separate command line window" />
<RadioButton Content="Embedded"
GroupName="Choose runtime option"
IsChecked="{Binding ServerEmbedded}"
ToolTip.ShowOnDisabled="True"
ToolTip.Tip="Run server in embedded mode within the launcher UI" />
</StackPanel>
</OnPlatform.Default>
<OnPlatform.macOS>
<RadioButton Content="Embedded"
GroupName="Choose runtime option"
IsChecked="True"
ToolTip.ShowOnDisabled="True"
ToolTip.Tip="Run server in embedded mode within the launcher UI" />
</OnPlatform.macOS>
</OnPlatform>
</StackPanel>
<Panel Grid.Column="1" HorizontalAlignment="Stretch">
<Button
Classes="abort big"
Command="{Binding StopServerCommand}"
Content="STOP SERVER"
HorizontalAlignment="Stretch"
IsVisible="{Binding ServerIsOnline}" />
<Button
Classes="primary big"
Command="{Binding StartServerCommand}"
HorizontalAlignment="Stretch"
IsVisible="{Binding !ServerIsOnline}">
<StackPanel>
<TextBlock Text="START SERVER" />
<TextBlock Text="MULTIPLAYER" />
</StackPanel>
</Button>
</Panel>
</Grid>
</Border>
</Grid>
</UserControl>

View File

@@ -0,0 +1,12 @@
using Nitrox.Launcher.ViewModels;
using Nitrox.Launcher.Views.Abstract;
namespace Nitrox.Launcher.Views;
public partial class ManageServerView : RoutableViewBase<ManageServerViewModel>
{
public ManageServerView()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,127 @@
<Window
MaxWidth="1000"
MinHeight="200"
MinWidth="700"
SizeToContent="Width"
Title="{Binding Title}"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d"
x:Class="Nitrox.Launcher.Views.ObjectPropertyEditorModal"
x:DataType="vm:ObjectPropertyEditorViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:controls="clr-namespace:Nitrox.Launcher.Models.Controls"
xmlns:converters="clr-namespace:Nitrox.Launcher.Models.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:design="clr-namespace:Nitrox.Launcher.Models.Design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:serialization="clr-namespace:NitroxModel.Serialization;assembly=NitroxModel"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:vm="clr-namespace:Nitrox.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.DataContext>
<vm:ObjectPropertyEditorViewModel>
<vm:ObjectPropertyEditorViewModel.OwnerObject>
<serialization:SubnauticaServerConfig />
</vm:ObjectPropertyEditorViewModel.OwnerObject>
</vm:ObjectPropertyEditorViewModel>
</Design.DataContext>
<Grid RowDefinitions="Auto, *, Auto">
<controls:CustomTitlebar
CanMinimize="False"
Grid.Row="0"
Grid.RowSpan="2" />
<TextBlock
Classes="modalHeader"
Grid.Row="0"
Margin="24,24,24,0"
Text="{Binding Title}" />
<!-- Content -->
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Disabled">
<StackPanel Classes="form" Margin="24">
<ItemsControl ItemsSource="{Binding EditorFields}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Spacing="14" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Value}">
<ContentPresenter.DataTemplates>
<DataTemplate DataType="{x:Type system:String}">
<StackPanel Classes="form" DataContext="{Binding $parent[ContentPresenter].Parent.((design:EditorField)DataContext)}">
<TextBlock Text="{Binding PropertyInfo.Name}" />
<TextBlock Text="{Binding Description}" />
<TextBox Text="{Binding Value, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type system:Boolean}">
<StackPanel Classes="form" DataContext="{Binding $parent[ContentPresenter].Parent.((design:EditorField)DataContext)}">
<TextBlock Text="{Binding PropertyInfo.Name}" />
<TextBlock Text="{Binding Description}" />
<CheckBox
Classes="switch"
HorizontalAlignment="Left"
IsChecked="{Binding Value}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type system:Int32}">
<StackPanel Classes="form" DataContext="{Binding $parent[ContentPresenter].Parent.((design:EditorField)DataContext)}">
<TextBlock Text="{Binding PropertyInfo.Name}" />
<TextBlock Text="{Binding Description}" />
<NumericUpDown Value="{Binding Value, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type system:Single}">
<StackPanel Classes="form" DataContext="{Binding $parent[ContentPresenter].Parent.((design:EditorField)DataContext)}">
<TextBlock Text="{Binding PropertyInfo.Name}" />
<TextBlock Text="{Binding Description}" />
<NumericUpDown Value="{Binding Value, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type system:Object}">
<StackPanel Classes="form" DataContext="{Binding $parent[ContentPresenter].Parent.((design:EditorField)DataContext)}">
<TextBlock Text="{Binding PropertyInfo.Name}" />
<TextBlock Text="{Binding Description}" />
<ComboBox ItemsSource="{Binding PossibleValues}" SelectedValue="{Binding Value}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={converters:ToStringConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</DataTemplate>
</ContentPresenter.DataTemplates>
</ContentPresenter>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
<Border Classes="footer" Grid.Row="2">
<Panel>
<Button
Command="{Binding CloseCommand}"
Content="Back"
FontWeight="Bold"
HotKey="Escape" />
<Button
Classes="primary"
Command="{Binding SaveCommand}"
Content="Save"
HorizontalAlignment="Right"
HotKey="Enter" />
</Panel>
</Border>
</Grid>
</Window>

View File

@@ -0,0 +1,11 @@
using Nitrox.Launcher.Views.Abstract;
namespace Nitrox.Launcher.Views;
public partial class ObjectPropertyEditorModal : ModalBase
{
public ObjectPropertyEditorModal()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,218 @@
<UserControl
d:DesignWidth="1000"
mc:Ignorable="d"
x:Class="Nitrox.Launcher.Views.OptionsView"
x:DataType="vm:OptionsViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:converters="clr-namespace:Nitrox.Launcher.Models.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:design="clr-namespace:Nitrox.Launcher.Models.Design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Nitrox.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.DataContext>
<vm:OptionsViewModel>
<vm:OptionsViewModel.SelectedGame>
<design:KnownGame PathToGame="C:\Users\Me\Games\Subnautica" Platform="STEAM" />
</vm:OptionsViewModel.SelectedGame>
</vm:OptionsViewModel>
</Design.DataContext>
<UserControl.Resources>
<StackPanel Orientation="Vertical" x:Key="InfoIconTooltipText">
<TextBlock>
Virtual Reality mode (VR) is disabled by default, but you can edit this line if you want to use it.<LineBreak />
<LineBreak />
Possible values : oculus, openvr, none</TextBlock>
<Border
BorderBrush="Silver"
BorderThickness="0,1,0,0"
Margin="0,8" />
<TextBlock FontStyle="Italic">Contact our support for more information.</TextBlock>
</StackPanel>
</UserControl.Resources>
<ScrollViewer Classes="main">
<StackPanel
Background="{DynamicResource BrandWhite}"
Classes="viewPadding"
Orientation="Vertical"
Spacing="22">
<TextBlock Classes="header" Text="Options" />
<!-- Subnautica Runtime Selection -->
<StackPanel Spacing="12">
<StackPanel Spacing="8">
<TextBlock
FontSize="20"
FontWeight="Bold"
Text="Subnautica Installation" />
<TextBlock FontSize="12" Text="Choose which Subnautica instance to use for multiplayer" />
</StackPanel>
<Border
Background="{DynamicResource BrandPanelBackground}"
CornerRadius="12"
Padding="22,15">
<Grid ColumnDefinitions=" Auto, *, Auto">
<Image
Height="36"
Margin="0,0,22,0"
Source="{Binding SelectedGame.Platform, Converter={converters:PlatformToIconConverter}}"
Stretch="Uniform"
ToolTip.Tip="Welcome aboard, Captain!"
Width="36" />
<StackPanel
Grid.Column="1"
HorizontalAlignment="Left"
Spacing="2"
VerticalAlignment="Center">
<TextBlock
FontSize="16"
FontWeight="Bold"
Text="{Binding SelectedGame.Platform, Converter={converters:ToStringConverter}}" />
<SelectableTextBlock
FontSize="14"
Foreground="#BFFFFFFF"
Text="{Binding SelectedGame.PathToGame, FallbackValue='Unknown path'}" />
</StackPanel>
<Button
Classes="primary"
Command="{Binding SetGamePathCommand}"
Content="Change"
Grid.Column="2"
HorizontalAlignment="Right"
Margin="22,0,0,0"
ToolTip.Tip="Direct the launcher to your game's install location"
Width="120" />
</Grid>
<!-- <ItemsControl ItemsSource="{Binding KnownGames}"> -->
<!-- <ItemsControl.ItemTemplate> -->
<!-- <DataTemplate> -->
<!-- <StackPanel Orientation="Horizontal" Spacing="16" Height="52"> -->
<!-- <RadioButton Margin="0 0 -5 0" /> -->
<!-- <Image Source="{Binding Platform, Converter={converters:PlatformToIconConverter}}" -->
<!-- ToolTip.Tip="Welcome aboard, Captain!" -->
<!-- Stretch="Uniform" -->
<!-- Height="36" -->
<!-- Width="36" /> -->
<!-- <StackPanel Spacing="2" VerticalAlignment="Center"> -->
<!-- <TextBlock FontSize="16" FontWeight="Bold" Text="{Binding Platform, Converter={converters:ToStringConverter}}" /> -->
<!-- <TextBlock FontSize="14" Foreground="#BFFFFFFF" Text="{Binding PathToGame, FallbackValue='Unknown path'}" /> -->
<!-- </StackPanel> -->
<!-- </StackPanel> -->
<!-- </DataTemplate> -->
<!-- </ItemsControl.ItemTemplate> -->
<!-- </ItemsControl> -->
</Border>
<Grid>
<!-- <Button Classes="icon" VerticalAlignment="Bottom" -->
<!-- Content="Manually add game installation" -->
<!-- Command="{Binding AddGameInstallationCommand}"/> -->
<!-- <Button Classes="primary" HorizontalAlignment="Right" Content="Choose destination" /> -->
</Grid>
</StackPanel>
<!-- Launch Arguments -->
<StackPanel Spacing="12">
<StackPanel Spacing="8">
<TextBlock
FontSize="20"
FontWeight="Bold"
Text="Subnautica Launch Arguments" />
<TextBlock FontSize="12" Text="Allows you to override Nitrox default launch arguments" />
</StackPanel>
<Border
Background="{DynamicResource BrandPanelBackground}"
CornerRadius="12"
Padding="22,15">
<Grid ColumnDefinitions="*, Auto, 120">
<Panel>
<TextBox
FontSize="15"
MaxLines="1"
Padding="13,12"
Text="{Binding LaunchArgs}"
x:Name="LaunchArgsInput" />
<Button
Classes="abort"
Command="{Binding ResetArgumentsCommand}"
CommandParameter="{Binding #LaunchArgsInput}"
Content="Reset"
FontSize="12"
Height="28"
HorizontalAlignment="Right"
IsVisible="{Binding ShowResetArgsBtn}"
Margin="0,0,12,0"
Padding="0"
ToolTip.Tip="Reset arguments"
Width="72" />
</Panel>
<Button
Classes="icon"
Grid.Column="1"
Margin="10,0"
ToolTip.Tip="{StaticResource InfoIconTooltipText}">
<Path Data="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,17H13V11H11V17Z" Fill="{DynamicResource BrandBlack}" />
<Button.Styles>
<Style Selector="Button:pointerover">
<Setter Property="Effect">
<!-- TODO: Figure out how to dynamically bind color property to change with active theme -->
<DropShadowDirectionEffect
BlurRadius="20"
Color="White"
ShadowDepth="0" />
</Setter>
</Style>
</Button.Styles>
</Button>
<Button
Classes="primary"
Command="{Binding SetArgumentsCommand}"
Content="Apply"
Grid.Column="2"
HorizontalAlignment="Stretch"
ToolTip.Tip="Save launch arguments" />
</Grid>
</Border>
</StackPanel>
<!-- Saves Folder Location -->
<StackPanel Spacing="12">
<TextBlock
FontSize="20"
FontWeight="Bold"
Text="Save File Location" />
<TextBlock
FontSize="12"
HorizontalAlignment="Left"
Text="This is the location where your Nitrox server save files are stored" />
<Border
Background="{DynamicResource BrandPanelBackground}"
CornerRadius="12"
Padding="22,15">
<Grid ColumnDefinitions="*,Auto">
<TextBlock
FontSize="15"
Foreground="#BFFFFFFF"
Text="{Binding SavesFolderDir}"
VerticalAlignment="Center" />
<Button
Classes="primary"
Command="{Binding OpenSavesFolderCommand}"
Content="Open"
Grid.Column="1"
HorizontalAlignment="Right"
Margin="22,0,0,0"
ToolTip.Tip="Open saves folder"
Width="120" />
</Grid>
</Border>
</StackPanel>
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,12 @@
using Nitrox.Launcher.ViewModels;
using Nitrox.Launcher.Views.Abstract;
namespace Nitrox.Launcher.Views;
public partial class OptionsView : RoutableViewBase<OptionsViewModel>
{
public OptionsView()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,175 @@
<UserControl
x:Class="Nitrox.Launcher.Views.ServersView"
x:DataType="vm:ServersViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:controls="clr-namespace:Nitrox.Launcher.Models.Controls"
xmlns:converters="clr-namespace:Nitrox.Launcher.Models.Converters"
xmlns:vm="clr-namespace:Nitrox.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.DataContext>
<vm:ServersViewModel />
</Design.DataContext>
<Panel Background="{DynamicResource BrandWhite}" Classes="viewPadding">
<Panel IsVisible="{Binding !Servers.Count}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Image
Margin="0,0,0,73"
Source="/Assets/Images/world-manager/server.png"
Stretch="None" />
<TextBlock
FontSize="32"
FontWeight="600"
TextAlignment="Center">
No servers here yet
</TextBlock>
<TextBlock
FontSize="16"
Margin="0,10,0,0"
TextAlignment="Center">
Create a new server to start hosting your Subnautica Multiplayer world
</TextBlock>
<Button
Classes="primary big"
Command="{Binding CreateServerCommand}"
HorizontalAlignment="Center"
HotKey="Enter"
Margin="0,40,0,0"
Width="350">
<StackPanel>
<TextBlock Text="CREATE NEW SERVER" />
<TextBlock Text="MULTIPLAYER" />
</StackPanel>
</Button>
</StackPanel>
</Panel>
<Grid IsVisible="{Binding Servers.Count}" RowDefinitions="Auto,Auto,*,Auto">
<TextBlock
Classes="header"
Grid.Row="0"
Text="Servers" />
<controls:RichTextBlock Grid.Row="1">Welcome to your Subnautica multiplayer server. For additional information and to learn more about hosting a server refer to the [Nitrox Wiki](nitrox.rux.gg/wiki/article/run-and-host-nitrox-subnautica-server)</controls:RichTextBlock>
<ScrollViewer Grid.Row="2" Margin="0,40,0,13">
<ItemsControl ItemsSource="{Binding Servers}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Margin="0,-13" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Classes="serverEntry" Margin="0,13">
<DockPanel VerticalAlignment="Center">
<Border
ClipToBounds="True"
CornerRadius="12"
DockPanel.Dock="Left">
<Grid>
<Image IsVisible="{Binding ServerIcon, Converter={converters:EqualityConverter}}" Source="/Assets/Images/subnautica-icon.png" />
<Border Height="56" Width="56">
<Border.Background>
<ImageBrush Source="{Binding ServerIcon}" Stretch="UniformToFill" />
</Border.Background>
</Border>
<controls:GrayscaleControl Opacity="{Binding !IsOnline}">
<controls:GrayscaleControl.Transitions>
<Transitions>
<DoubleTransition Duration="0:0:0.10" Property="Opacity" />
</Transitions>
</controls:GrayscaleControl.Transitions>
</controls:GrayscaleControl>
</Grid>
</Border>
<StackPanel
DockPanel.Dock="Left"
Margin="16,0"
VerticalAlignment="Center">
<TextBlock
FontSize="16"
FontWeight="600"
Text="{Binding Name}" />
<StackPanel
Classes="description"
HorizontalAlignment="Left"
Orientation="Horizontal">
<Panel>
<StackPanel IsVisible="{Binding IsOnline}" Orientation="Horizontal">
<Ellipse Fill="{DynamicResource BrandOnColor}" Margin="0,0,6,0" />
<TextBlock IsVisible="{Binding IsOnline}" Text="Online" />
</StackPanel>
<StackPanel IsVisible="{Binding !IsOnline}" Orientation="Horizontal">
<Ellipse Fill="{DynamicResource BrandOffColor}" Margin="0,0,6,0" />
<TextBlock IsVisible="{Binding !IsOnline}" Text="Offline" />
</StackPanel>
</Panel>
<TextBlock Text="{Binding GameMode, Converter={converters:ToStringConverter}}" />
<TextBlock>
<Run Text="{Binding Players}" />
<Run Text="/" />
<Run Text="{Binding MaxPlayers}" />
<Run Text=" playing" />
</TextBlock>
</StackPanel>
</StackPanel>
<StackPanel
DockPanel.Dock="Right"
HorizontalAlignment="Right"
Orientation="Horizontal"
Spacing="8">
<Button Command="{Binding OpenSaveFolderCommand}" Content="Open world folder" />
<Button
Content="Manage"
Command="{Binding $parent[ItemsControl].((vm:ServersViewModel)DataContext).ManageServerCommand}"
CommandParameter="{Binding}"
IsVisible="{Binding !IsOnline}" />
<Button
Classes="primary"
Content="Console"
Command="{Binding $parent[ItemsControl].((vm:ServersViewModel)DataContext).ManageServerCommand}"
CommandParameter="{Binding}">
<Button.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="IsOnline" />
<Binding Path="IsEmbedded" />
</MultiBinding>
</Button.IsVisible>
</Button>
<Button
Classes="abort"
Command="{Binding StopCommand}"
Content="Stop"
IsVisible="{Binding IsOnline}" />
<Button
Classes="primary"
Command="{Binding $parent[ItemsControl].((vm:ServersViewModel)DataContext).StartServerCommand}"
CommandParameter="{Binding}"
Content="Start"
IsVisible="{Binding !IsOnline}" />
</StackPanel>
</DockPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<Border
Background="{DynamicResource BrandPanelBackground}"
CornerRadius="12"
Grid.Row="3"
Height="96"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom">
<Button
Classes="primary big"
Command="{Binding CreateServerCommand}"
Content="CREATE NEW SERVER"
HorizontalAlignment="Center"
HotKey="Enter"
VerticalAlignment="Center"
Width="262"
x:Name="CreateServerBtn" />
</Border>
</Grid>
</Panel>
</UserControl>

View File

@@ -0,0 +1,12 @@
using Nitrox.Launcher.ViewModels;
using Nitrox.Launcher.Views.Abstract;
namespace Nitrox.Launcher.Views;
public partial class ServersView : RoutableViewBase<ServersViewModel>
{
public ServersView()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,179 @@
<UserControl
d:DesignWidth="1000"
mc:Ignorable="d"
x:Class="Nitrox.Launcher.Views.UpdatesView"
x:DataType="vm:UpdatesViewModel"
xmlns="https://github.com/avaloniaui"
xmlns:controls="clr-namespace:Nitrox.Launcher.Models.Controls"
xmlns:converters="clr-namespace:Nitrox.Launcher.Models.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:design="clr-namespace:Nitrox.Launcher.Models.Design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Nitrox.Launcher.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.DataContext>
<vm:UpdatesViewModel />
</Design.DataContext>
<ScrollViewer Classes="main">
<StackPanel
Background="{DynamicResource BrandWhite}"
Classes="viewPadding"
Spacing="23">
<TextBlock Classes="header" Text="Updates" />
<Border
Background="{DynamicResource BrandPanelBackground}"
CornerRadius="12"
Padding="22">
<Grid>
<!-- Official Version only text -->
<StackPanel IsVisible="{Binding UsingOfficialVersion, Mode=OneWay}" Spacing="10">
<Image
Height="56"
HorizontalAlignment="Center"
Source="/Assets/Images/nitrox-icon.ico"
ToolTip.Tip="You are the best captain on the planet, I'm not even squiddin'"
Width="56" />
<!-- "No Update Available" text -->
<StackPanel IsVisible="{Binding !NewUpdateAvailable, Mode=OneWay}" Spacing="5">
<TextBlock
FontSize="16"
FontWeight="Bold"
Text="No Update Available"
TextAlignment="Center"
TextWrapping="Wrap" />
<TextBlock
FontSize="14"
FontWeight="Normal"
TextAlignment="Center"
TextWrapping="Wrap">
You are already using the latest version of Nitrox <Run Text="{Binding Version, FallbackValue='.'}" />
</TextBlock>
<TextBlock
FontSize="14"
FontWeight="Normal"
Margin="0,9,0,0"
Text="Missing a feature? Check the changelogs if the feature has already been implemented. If you experience any other issues, please contact support."
TextAlignment="Center"
TextWrapping="Wrap" />
</StackPanel>
<!-- "New Nitrox Update Available" text -->
<StackPanel IsVisible="{Binding NewUpdateAvailable, Mode=OneWay}" Spacing="6">
<TextBlock
FontSize="16"
FontWeight="Bold"
Text="New Nitrox Update Available"
TextAlignment="Center"
TextWrapping="Wrap" />
<TextBlock
FontSize="14"
FontWeight="Normal"
Text="Time for an update! A new version of Nitrox is now available to download. Back up your content and get the latest version to start using all the latest features and improvements."
TextAlignment="Center"
TextWrapping="Wrap" />
<Button
Classes="primary"
Command="{Binding DownloadUpdateCommand}"
HorizontalAlignment="Center"
HorizontalContentAlignment="Center"
Margin="0,4,0,0"
Padding="20,8"
ToolTip.Tip="Download the latest version">
<TextBlock FontSize="12">
Download version <Run Text="{Binding OfficialVersion, FallbackValue='.'}" />
</TextBlock>
</Button>
</StackPanel>
</StackPanel>
<!-- Unofficial Version only text -->
<StackPanel IsVisible="{Binding !UsingOfficialVersion, Mode=OneWay}" Spacing="10">
<!-- TODO: Better Icon here -->
<Image
Height="56"
HorizontalAlignment="Center"
Source="/Assets/Images/store-icons/pirated.png"
ToolTip.Tip=":("
Width="56" />
<StackPanel Spacing="6">
<TextBlock
FontSize="16"
FontWeight="Bold"
Text="Unofficial Version Detected"
TextAlignment="Center"
TextWrapping="Wrap" />
<TextBlock
FontSize="14"
FontWeight="Normal"
TextAlignment="Center"
TextWrapping="Wrap">
It looks like you aren't using an official version of Nitrox. If you are not a developer, please download the official version to ensure that you have the best experience.<LineBreak />
<LineBreak />
Your version: <Run Text="{Binding Version, FallbackValue='.'}" />
<LineBreak />
Official version: <Run Text="{Binding OfficialVersion, FallbackValue='.'}" />
</TextBlock>
<Button
Classes="primary"
Command="{Binding DownloadUpdateCommand}"
HorizontalAlignment="Center"
HorizontalContentAlignment="Center"
Margin="0,4,0,0"
Padding="20,8"
ToolTip.Tip="Download the latest version">
<TextBlock FontSize="12">
Download version <Run Text="{Binding OfficialVersion, FallbackValue='.'}" />
</TextBlock>
</Button>
</StackPanel>
</StackPanel>
</Grid>
</Border>
<ItemsControl ItemsSource="{Binding NitroxChangelogs, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Spacing="18" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="design:NitroxChangelog">
<StackPanel Spacing="5">
<Grid>
<TextBlock
FontSize="20"
FontWeight="Bold"
HorizontalAlignment="Left"
Text="{Binding Version, TargetNullValue='Unknown'}" />
<TextBlock
FontSize="12"
FontWeight="Light"
HorizontalAlignment="Right"
Text="{Binding Released, Converter={converters:DateToRelativeDateConverter}, FallbackValue='Unknown', TargetNullValue='Unknown'}"
ToolTip.Tip="{Binding Released}"
VerticalAlignment="Center" />
</Grid>
<Expander Classes="changelog" Header="View changelog">
<controls:RichTextBlock FontSize="14" Text="{Binding PatchNotes, FallbackValue='Empty', TargetNullValue='Empty'}" />
</Expander>
<Border BorderThickness="0,1,0,0" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,12 @@
using Nitrox.Launcher.ViewModels;
using Nitrox.Launcher.Views.Abstract;
namespace Nitrox.Launcher.Views;
public partial class UpdatesView : RoutableViewBase<UpdatesViewModel>
{
public UpdatesView()
{
InitializeComponent();
}
}