diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 00000000..92898fe2 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,33 @@ + + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/IonKiwi.lz4/IonKiwi.lz4.csproj b/IonKiwi.lz4/IonKiwi.lz4.csproj index 312984fb..28bacc25 100644 --- a/IonKiwi.lz4/IonKiwi.lz4.csproj +++ b/IonKiwi.lz4/IonKiwi.lz4.csproj @@ -1,7 +1,7 @@  - net60 + net6.0;net8.0;net10.0-android true IonKiwi.lz4.managed 1.0.7 diff --git a/Knossos.NET.Android/Icon.png b/Knossos.NET.Android/Icon.png new file mode 100644 index 00000000..e0313cfc Binary files /dev/null and b/Knossos.NET.Android/Icon.png differ diff --git a/Knossos.NET.Android/Knossos.NET.Android.csproj b/Knossos.NET.Android/Knossos.NET.Android.csproj new file mode 100644 index 00000000..3ea7a7e6 --- /dev/null +++ b/Knossos.NET.Android/Knossos.NET.Android.csproj @@ -0,0 +1,53 @@ + + + Exe + net10.0-android + 28 + enable + apk + false + enable + true + + com.knossosnet.knossosnet + 1 + 1 + 1.3.1 + true + false + + + + + + + + + + + + + + + + + + + + Resources\drawable\Icon.png + + + + + + + + + + + + + + + + diff --git a/Knossos.NET.Android/MainActivity.cs b/Knossos.NET.Android/MainActivity.cs new file mode 100644 index 00000000..be7a5fa2 --- /dev/null +++ b/Knossos.NET.Android/MainActivity.cs @@ -0,0 +1,23 @@ +using Android.Content.PM; +using Avalonia; +using Avalonia.Android; + +namespace Knossos.NET.Android; + +[Activity( + Label = "Knossos.NET", + Theme = "@style/MyTheme.NoActionBar", + Icon = "@drawable/icon", + MainLauncher = true, + ConfigurationChanges = ConfigChanges.Orientation + | ConfigChanges.ScreenSize + | ConfigChanges.UiMode, + ScreenOrientation = ScreenOrientation.SensorLandscape)] +public class MainActivity : AvaloniaMainActivity +{ + protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) + { + return base.CustomizeAppBuilder(builder) + .WithInterFont(); + } +} diff --git a/Knossos.NET.Android/Properties/AndroidManifest.xml b/Knossos.NET.Android/Properties/AndroidManifest.xml new file mode 100644 index 00000000..606edbb0 --- /dev/null +++ b/Knossos.NET.Android/Properties/AndroidManifest.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Knossos.NET.Android/Resources/AboutResources.txt b/Knossos.NET.Android/Resources/AboutResources.txt new file mode 100644 index 00000000..c2bca974 --- /dev/null +++ b/Knossos.NET.Android/Resources/AboutResources.txt @@ -0,0 +1,44 @@ +Images, layout descriptions, binary blobs and string dictionaries can be included +in your application as resource files. Various Android APIs are designed to +operate on the resource IDs instead of dealing with images, strings or binary blobs +directly. + +For example, a sample Android app that contains a user interface layout (main.axml), +an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) +would keep its resources in the "Resources" directory of the application: + +Resources/ + drawable/ + icon.png + + layout/ + main.axml + + values/ + strings.xml + +In order to get the build system to recognize Android resources, set the build action to +"AndroidResource". The native Android APIs do not operate directly with filenames, but +instead operate on resource IDs. When you compile an Android application that uses resources, +the build system will package the resources for distribution and generate a class called "R" +(this is an Android convention) that contains the tokens for each one of the resources +included. For example, for the above Resources layout, this is what the R class would expose: + +public class R { + public class drawable { + public const int icon = 0x123; + } + + public class layout { + public const int main = 0x456; + } + + public class strings { + public const int first_string = 0xabc; + public const int second_string = 0xbcd; + } +} + +You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main +to reference the layout/main.axml file, or R.strings.first_string to reference the first +string in the dictionary file values/strings.xml. \ No newline at end of file diff --git a/Knossos.NET.Android/Resources/drawable-night-v31/avalonia_anim.xml b/Knossos.NET.Android/Resources/drawable-night-v31/avalonia_anim.xml new file mode 100644 index 00000000..dde4b5a7 --- /dev/null +++ b/Knossos.NET.Android/Resources/drawable-night-v31/avalonia_anim.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Knossos.NET.Android/Resources/drawable-v31/avalonia_anim.xml b/Knossos.NET.Android/Resources/drawable-v31/avalonia_anim.xml new file mode 100644 index 00000000..94f27d9e --- /dev/null +++ b/Knossos.NET.Android/Resources/drawable-v31/avalonia_anim.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Knossos.NET.Android/Resources/drawable/splash_screen.xml b/Knossos.NET.Android/Resources/drawable/splash_screen.xml new file mode 100644 index 00000000..2e920b4b --- /dev/null +++ b/Knossos.NET.Android/Resources/drawable/splash_screen.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/Knossos.NET.Android/Resources/layout/overlay_controls.xml b/Knossos.NET.Android/Resources/layout/overlay_controls.xml new file mode 100644 index 00000000..c2c714c2 --- /dev/null +++ b/Knossos.NET.Android/Resources/layout/overlay_controls.xml @@ -0,0 +1,427 @@ + + + + + + + Library Folder + + Stats diff --git a/Knossos.NET/Views/KnossosWindow.cs b/Knossos.NET/Views/KnossosWindow.cs new file mode 100644 index 00000000..a60826e0 --- /dev/null +++ b/Knossos.NET/Views/KnossosWindow.cs @@ -0,0 +1,244 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.Presenters; +using Avalonia.Media; +using System; +using System.ComponentModel; +using System.Threading.Tasks; + +namespace Knossos.NET.Views +{ + /// + /// Window wrapper system + /// For opening new windows both on desktop OS and single view systems like Android + /// Instead of using Avalonia Window, use this, this will create a new window in runtime for desktop os + /// and display the view on a overlay over the MainView in single view OS. + /// + public partial class KnossosWindow : UserControl + { + public KnossosWindow() + { + } + + //Add some properties from Window missing on Usercontrol + public static readonly StyledProperty TitleProperty = AvaloniaProperty.Register(nameof(Title)); + public string? Title + { + get => GetValue(TitleProperty); + set => SetValue(TitleProperty, value); + } + + public static readonly StyledProperty CanCloseProperty = AvaloniaProperty.Register(nameof(CanClose), defaultValue: true); + public bool CanClose + { + get => GetValue(CanCloseProperty); + set => SetValue(CanCloseProperty, value); + } + + public static readonly StyledProperty CanResizeProperty = AvaloniaProperty.Register(nameof(CanResize), defaultValue: true); + public bool CanResize + { + get => GetValue(CanResizeProperty); + set => SetValue(CanResizeProperty, value); + } + + public static readonly StyledProperty IconProperty = AvaloniaProperty.Register(nameof(Icon)); + public WindowIcon? Icon + { + get => GetValue(IconProperty); + set => SetValue(IconProperty, value); + } + + public static readonly StyledProperty WindowStartupLocationProperty = AvaloniaProperty.Register(nameof(WindowStartupLocation), WindowStartupLocation.CenterOwner); + public WindowStartupLocation WindowStartupLocation + { + get => GetValue(WindowStartupLocationProperty); + set => SetValue(WindowStartupLocationProperty, value); + } + + public static readonly StyledProperty SizeToContentProperty = AvaloniaProperty.Register(nameof(SizeToContent), SizeToContent.Manual); + public SizeToContent SizeToContent + { + get => GetValue(SizeToContentProperty); + set => SetValue(SizeToContentProperty, value); + } + + public static readonly StyledProperty TopmostProperty = AvaloniaProperty.Register(nameof(Topmost), false); + public bool Topmost + { + get => GetValue(TopmostProperty); + set => SetValue(TopmostProperty, value); + } + + // Dialog result + public object? DialogResult { get; set; } + + // Window events + public event EventHandler? Opened; + public event EventHandler? Closing; + public event EventHandler? Closed; + + // Window-like API + public void Show() => _ = ShowInternalAsync(KnUtils.GetTopLevel(), isDialog: false); + public void Show(Window owner) => _ = ShowInternalAsync(owner, isDialog: false); + public Task ShowDialog() => ShowInternalAsync(KnUtils.GetTopLevel(), isDialog: true); + public Task ShowDialog(Window? owner) => ShowInternalAsync(owner, isDialog: true); + protected virtual Task OnClosingAsync() => Task.FromResult(true); + + private Window? _hostWindow; // desktop host + private ContentPresenter? _overlayHost; // android host + private TaskCompletionSource? _tcs; // ShowDialog() + + //Close the window and run OnClosing + public async void Close() + { + var ce = new CancelEventArgs(); + if (!CanClose) ce.Cancel = true; + Closing?.Invoke(this, ce); + if (ce.Cancel) return; + + var ok = await OnClosingAsync().ConfigureAwait(true); + if (!ok) return; + + //desktop + if (_hostWindow != null) + { + _hostWindow.Close(); + return; + } + + //android + if (_overlayHost != null) + { + DialogHost.Hide(this, _overlayHost); + _overlayHost.IsHitTestVisible = false; + _overlayHost = null; + _tcs?.TrySetResult(DialogResult); + _tcs = null; + Closed?.Invoke(this, EventArgs.Empty); + } + } + + // Add Window-like title and close button to the supplied view + private Control BuildOverlayChrome(Control body) + { + var card = new Border + { + Background = new Avalonia.Media.SolidColorBrush(Avalonia.Media.Colors.Black), + CornerRadius = new CornerRadius(10), + Padding = new Thickness(0), + Margin = new Thickness(20), + HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch, + VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch + }; + + var layout = new Grid + { + RowDefinitions = new RowDefinitions("35, *") + }; + + var titleBar = new Grid + { + Background = new Avalonia.Media.SolidColorBrush(Avalonia.Media.Color.FromRgb(240, 240, 240)), + Height = 35 + }; + titleBar.ColumnDefinitions = new ColumnDefinitions("*, Auto"); + + // title + var titleText = new TextBlock + { + Margin = new Thickness(8), + FontWeight = Avalonia.Media.FontWeight.SemiBold, + Foreground = SolidColorBrush.Parse("Black"), + Text = Title + }; + + // close button + var closeBtn = new Button + { + Content = "✕", + Width = 60, + Height = 30, + Background = SolidColorBrush.Parse("Red"), + Margin = new Thickness(0, 3, 6, 6) + }; + closeBtn.Click += (_, __) => Close(); + + titleBar.Children.Add(titleText); + Grid.SetColumn(closeBtn, 1); + titleBar.Children.Add(closeBtn); + + // title bar + Grid.SetRow(titleBar, 0); + layout.Children.Add(titleBar); + + // view + Grid.SetRow(body, 1); + if (MainView.instance != null) + { + body.MaxHeight = MainView.instance.Bounds.Height - 80; + body.MaxWidth = MainView.instance.Bounds.Width - 40; + } + layout.Children.Add(body); + + card.Child = layout; + return card; + } + + // Open the window, creates a window in runtime and attaches the view to it + // or display it on the mainview via dialoghost + private async Task ShowInternalAsync(TopLevel? owner, bool isDialog) + { + if (KnUtils.IsAndroid || KnUtils.IsBrowser) + { + _tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var chrome = BuildOverlayChrome(this); + _overlayHost = DialogHost.Show(chrome, onDismiss: () => Close()); + Opened?.Invoke(this, EventArgs.Empty); + + if (isDialog) + await _tcs.Task.ConfigureAwait(false); + + return; + } + + // Desktop, create a window + var w = new Window + { + Content = this, + Title = Title ?? string.Empty, + SizeToContent = SizeToContent, + Topmost = Topmost, + }; + + if (!double.IsNaN(Width) && Width > 0) w.Width = Width; + if (!double.IsNaN(Height) && Height > 0) w.Height = Height; + if (MinWidth > 0) w.MinWidth = MinWidth; + if (MinHeight > 0) w.MinHeight = MinHeight; + w.WindowStartupLocation = WindowStartupLocation; + + w.Closing += async (_, e) => + { + var ce = new CancelEventArgs(); + if (!CanClose) ce.Cancel = true; + Closing?.Invoke(this, ce); + if (ce.Cancel) { e.Cancel = true; return; } + + var ok = await OnClosingAsync().ConfigureAwait(true); + if (!ok) { e.Cancel = true; return; } + }; + + w.Opened += (_, __) => Opened?.Invoke(this, EventArgs.Empty); + w.Closed += (_, __) => { Closed?.Invoke(this, EventArgs.Empty); _hostWindow = null; }; + + _hostWindow = w; + + if (isDialog && owner is Window ownerWin) + await w.ShowDialog(ownerWin); + else if (isDialog && MainWindow.instance != null) + await w.ShowDialog(MainWindow.instance); + else + w.Show(); + } + } +} diff --git a/Knossos.NET/Views/MainView.axaml b/Knossos.NET/Views/MainView.axaml new file mode 100644 index 00000000..1aaffaa3 --- /dev/null +++ b/Knossos.NET/Views/MainView.axaml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Knossos.NET/Views/MainView.axaml.cs b/Knossos.NET/Views/MainView.axaml.cs new file mode 100644 index 00000000..c1738d38 --- /dev/null +++ b/Knossos.NET/Views/MainView.axaml.cs @@ -0,0 +1,35 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace Knossos.NET.Views; + +public partial class MainView : UserControl +{ + public static MainView? instance; + + public MainView() + { + instance = this; + InitializeComponent(); + } + + + /// + /// For custom mode, for setting the task buttom at the last place in the menu + /// we need to change the margins so it is displayed properly. + /// + public void FixMarginButtomTasks() + { + var tasks = this.FindControl("TaskButtom"); + if (tasks != null) + { + tasks.Margin = new Thickness(9, -45, 0, 0); + } + var list = this.FindControl("ButtomList"); + if (list != null) + { + list.Margin = new Thickness(2, 0, -100, 0); + } + } +} \ No newline at end of file diff --git a/Knossos.NET/Views/MessageBoxView.axaml b/Knossos.NET/Views/MessageBoxView.axaml new file mode 100644 index 00000000..bc66f9b1 --- /dev/null +++ b/Knossos.NET/Views/MessageBoxView.axaml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Knossos.NET/Views/MessageBoxView.axaml.cs b/Knossos.NET/Views/MessageBoxView.axaml.cs new file mode 100644 index 00000000..f350351a --- /dev/null +++ b/Knossos.NET/Views/MessageBoxView.axaml.cs @@ -0,0 +1,60 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using System; +using System.Threading.Tasks; +using static Knossos.NET.Views.MessageBox; + +namespace Knossos.NET.Views +{ + /// + /// Single view version of the Knossos Messagebox system + /// Used to display a message on a overlay rather than a window + /// + public partial class MessageBoxView : UserControl + { + public string Title { get => _title.Text!; set => _title.Text = value; } + public string Text { get => _body.Text!; set => _body.Text = value; } + + private readonly TextBlock _title; + private readonly TextBlock _body; + private readonly StackPanel _buttonsHostPanel; + + public MessageBoxView() + { + AvaloniaXamlLoader.Load(this); + _title = this.FindControl("TitleText")!; + _body = this.FindControl("BodyText")!; + _buttonsHostPanel = this.FindControl("ButtonsHostPanel")!; + } + + /// + /// Displays a message on a overlay over the main view + /// Usefull for single view OS like Android or WASM + /// Normally you dont want to call this directly unless you really want a message + /// on the overlay. Use MessageBox.Show() instead. + /// + /// + /// + /// + /// + public static Task ShowAsync(string text, string title, MessageBoxButtons buttons) + { + var tcs = new TaskCompletionSource(); + var view = new MessageBoxView { Title = title, Text = text }; + + void AddButton(string caption, MessageBoxResult r, bool isDefault = false, string? classes = null, double? width = null) + { + var b = new Button { Content = caption, MinWidth = width ?? 100 }; + if (!string.IsNullOrEmpty(classes)) b.Classes.Add(classes!); + b.Click += (_, __) => { tcs.TrySetResult(r); DialogHost.Hide(view); }; + view._buttonsHostPanel.Children.Add(b); + if (isDefault) view.AttachedToVisualTree += (_, __) => b.Focus(); + }; + + MessageBox.ButtonCreation(AddButton, buttons); + + DialogHost.Show(view, onDismiss: () => tcs.TrySetResult(MessageBoxResult.Cancel)); + return tcs.Task; + } + } +} \ No newline at end of file diff --git a/Knossos.NET/Views/ModListView.axaml.cs b/Knossos.NET/Views/ModListView.axaml.cs index 0f89d28f..dec65c81 100644 --- a/Knossos.NET/Views/ModListView.axaml.cs +++ b/Knossos.NET/Views/ModListView.axaml.cs @@ -67,16 +67,16 @@ private void ApplyFilterButtonsStyle() { try { - if (filterPanel != null && MainWindowViewModel.Instance != null) + if (filterPanel != null && MainViewModel.Instance != null) { - if (MainWindowViewModel.Instance.tagFilter.Any()) + if (MainViewModel.Instance.tagFilter.Any()) { var tags = ModTags.GetListAllFilters(); foreach (var item in filterPanel.Children) { if (item is Button button && button.Tag is int tagIndex) { - if (tags.Count() > tagIndex && MainWindowViewModel.Instance.tagFilter.Contains(tags[tagIndex])) + if (tags.Count() > tagIndex && MainViewModel.Instance.tagFilter.Contains(tags[tagIndex])) { button.Classes.Add("Secondary"); } diff --git a/Knossos.NET/Views/NebulaModListView.axaml.cs b/Knossos.NET/Views/NebulaModListView.axaml.cs index 72bf6fca..952d1d88 100644 --- a/Knossos.NET/Views/NebulaModListView.axaml.cs +++ b/Knossos.NET/Views/NebulaModListView.axaml.cs @@ -66,16 +66,16 @@ private void ApplyFilterButtonsStyle() { try { - if (filterPanel != null && MainWindowViewModel.Instance != null) + if (filterPanel != null && MainViewModel.Instance != null) { - if (MainWindowViewModel.Instance.tagFilter.Any()) + if (MainViewModel.Instance.tagFilter.Any()) { var tags = ModTags.GetListAllFilters(); foreach (var item in filterPanel.Children) { if (item is Button button && button.Tag is int tagIndex) { - if (tags.Count() > tagIndex && MainWindowViewModel.Instance.tagFilter.Contains(tags[tagIndex])) + if (tags.Count() > tagIndex && MainViewModel.Instance.tagFilter.Contains(tags[tagIndex])) { button.Classes.Add("Secondary"); } diff --git a/Knossos.NET/Views/Templates/ModCardView.axaml b/Knossos.NET/Views/Templates/ModCardView.axaml index 5542fb96..9de4e8d5 100644 --- a/Knossos.NET/Views/Templates/ModCardView.axaml +++ b/Knossos.NET/Views/Templates/ModCardView.axaml @@ -33,7 +33,7 @@ - + - + diff --git a/Knossos.NET/Views/Windows/AddUserBuildView.axaml.cs b/Knossos.NET/Views/Windows/AddUserBuildView.axaml.cs index bcfa6177..7628c05a 100644 --- a/Knossos.NET/Views/Windows/AddUserBuildView.axaml.cs +++ b/Knossos.NET/Views/Windows/AddUserBuildView.axaml.cs @@ -1,12 +1,14 @@ using Avalonia.Controls; +using Avalonia.Markup.Xaml; namespace Knossos.NET.Views.Windows { - public partial class AddUserBuildView : Window + public partial class AddUserBuildView : KnossosWindow { public AddUserBuildView() { - InitializeComponent(); + //InitializeComponent(); + AvaloniaXamlLoader.Load(this); } } } diff --git a/Knossos.NET/Views/Windows/CleanupKnossosLibraryView.axaml b/Knossos.NET/Views/Windows/CleanupKnossosLibraryView.axaml index afc666d7..144ae0c2 100644 --- a/Knossos.NET/Views/Windows/CleanupKnossosLibraryView.axaml +++ b/Knossos.NET/Views/Windows/CleanupKnossosLibraryView.axaml @@ -1,4 +1,4 @@ - - + diff --git a/Knossos.NET/Views/Windows/CleanupKnossosLibraryView.axaml.cs b/Knossos.NET/Views/Windows/CleanupKnossosLibraryView.axaml.cs index 6269f655..67b697e8 100644 --- a/Knossos.NET/Views/Windows/CleanupKnossosLibraryView.axaml.cs +++ b/Knossos.NET/Views/Windows/CleanupKnossosLibraryView.axaml.cs @@ -1,19 +1,16 @@ -using System; using Avalonia.Controls; +using Avalonia.Markup.Xaml; using Knossos.NET.ViewModels; +using System; namespace Knossos.NET.Views { - public partial class CleanupKnossosLibraryView : Window + public partial class CleanupKnossosLibraryView : KnossosWindow { public CleanupKnossosLibraryView() { - InitializeComponent(); - } - - protected override void OnOpened(EventArgs e) - { - base.OnOpened(e); + //InitializeComponent(); + AvaloniaXamlLoader.Load(this); ((CleanupKnossosLibraryViewModel)DataContext!).OnRequestClose += (s, ev) => Close(); ((CleanupKnossosLibraryViewModel)DataContext!).LoadRemovableMods(); } diff --git a/Knossos.NET/Views/Windows/DebugFiltersView.axaml b/Knossos.NET/Views/Windows/DebugFiltersView.axaml index 2c3dbbed..32a00106 100644 --- a/Knossos.NET/Views/Windows/DebugFiltersView.axaml +++ b/Knossos.NET/Views/Windows/DebugFiltersView.axaml @@ -1,4 +1,4 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/Knossos.NET/Views/Windows/DebugFiltersView.axaml.cs b/Knossos.NET/Views/Windows/DebugFiltersView.axaml.cs index 833063af..fa7bc8fc 100644 --- a/Knossos.NET/Views/Windows/DebugFiltersView.axaml.cs +++ b/Knossos.NET/Views/Windows/DebugFiltersView.axaml.cs @@ -1,14 +1,16 @@ -using System; using Avalonia.Controls; +using Avalonia.Markup.Xaml; using Knossos.NET.ViewModels; +using System; namespace Knossos.NET.Views { - public partial class DebugFiltersView : Window + public partial class DebugFiltersView : KnossosWindow { public DebugFiltersView() { - InitializeComponent(); + //InitializeComponent(); + AvaloniaXamlLoader.Load(this); } } } \ No newline at end of file diff --git a/Knossos.NET/Views/Windows/DevModAdvancedUploadView.axaml b/Knossos.NET/Views/Windows/DevModAdvancedUploadView.axaml index 59f5361b..b9389d06 100644 --- a/Knossos.NET/Views/Windows/DevModAdvancedUploadView.axaml +++ b/Knossos.NET/Views/Windows/DevModAdvancedUploadView.axaml @@ -1,4 +1,4 @@ -Upload to Nebula - + diff --git a/Knossos.NET/Views/Windows/DevModAdvancedUploadView.axaml.cs b/Knossos.NET/Views/Windows/DevModAdvancedUploadView.axaml.cs index 3b8cd273..72573c62 100644 --- a/Knossos.NET/Views/Windows/DevModAdvancedUploadView.axaml.cs +++ b/Knossos.NET/Views/Windows/DevModAdvancedUploadView.axaml.cs @@ -1,11 +1,13 @@ using Avalonia.Controls; +using Avalonia.Markup.Xaml; namespace Knossos.NET.Views; -public partial class DevModAdvancedUploadView : Window +public partial class DevModAdvancedUploadView : KnossosWindow { public DevModAdvancedUploadView() { - InitializeComponent(); + //InitializeComponent(); + AvaloniaXamlLoader.Load(this); } } \ No newline at end of file diff --git a/Knossos.NET/Views/Windows/DevModCreateNewView.axaml b/Knossos.NET/Views/Windows/DevModCreateNewView.axaml index 0e411773..51e6928f 100644 --- a/Knossos.NET/Views/Windows/DevModCreateNewView.axaml +++ b/Knossos.NET/Views/Windows/DevModCreateNewView.axaml @@ -1,4 +1,4 @@ - - + diff --git a/Knossos.NET/Views/Windows/DevModCreateNewView.axaml.cs b/Knossos.NET/Views/Windows/DevModCreateNewView.axaml.cs index a7235351..d79fdb33 100644 --- a/Knossos.NET/Views/Windows/DevModCreateNewView.axaml.cs +++ b/Knossos.NET/Views/Windows/DevModCreateNewView.axaml.cs @@ -4,10 +4,11 @@ namespace Knossos.NET.Views; -public partial class DevModCreateNewView : Window +public partial class DevModCreateNewView : KnossosWindow { public DevModCreateNewView() { - InitializeComponent(); + //InitializeComponent(); + AvaloniaXamlLoader.Load(this); } } \ No newline at end of file diff --git a/Knossos.NET/Views/Windows/DevModDescriptionEditorView.axaml b/Knossos.NET/Views/Windows/DevModDescriptionEditorView.axaml index 15b54a4a..9af07f92 100644 --- a/Knossos.NET/Views/Windows/DevModDescriptionEditorView.axaml +++ b/Knossos.NET/Views/Windows/DevModDescriptionEditorView.axaml @@ -1,4 +1,4 @@ - - + diff --git a/Knossos.NET/Views/Windows/DevModDescriptionEditorView.axaml.cs b/Knossos.NET/Views/Windows/DevModDescriptionEditorView.axaml.cs index 25c10da2..665d6c4c 100644 --- a/Knossos.NET/Views/Windows/DevModDescriptionEditorView.axaml.cs +++ b/Knossos.NET/Views/Windows/DevModDescriptionEditorView.axaml.cs @@ -6,11 +6,12 @@ namespace Knossos.NET.Views; -public partial class DevModDescriptionEditorView : Window +public partial class DevModDescriptionEditorView : KnossosWindow { public DevModDescriptionEditorView() { - InitializeComponent(); + //InitializeComponent(); + AvaloniaXamlLoader.Load(this); Closing += OnWindowClosing; } @@ -27,7 +28,7 @@ public void BindTextBox() } } - public void OnWindowClosing(object? sender, WindowClosingEventArgs e) + public void OnWindowClosing(object? sender, CancelEventArgs e) { if(DataContext != null) { diff --git a/Knossos.NET/Views/Windows/Fs2InstallerView.axaml b/Knossos.NET/Views/Windows/Fs2InstallerView.axaml index 36ef25fc..e356dbba 100644 --- a/Knossos.NET/Views/Windows/Fs2InstallerView.axaml +++ b/Knossos.NET/Views/Windows/Fs2InstallerView.axaml @@ -1,4 +1,4 @@ - - + diff --git a/Knossos.NET/Views/Windows/Fs2InstallerView.axaml.cs b/Knossos.NET/Views/Windows/Fs2InstallerView.axaml.cs index 185dc2df..6d6d5482 100644 --- a/Knossos.NET/Views/Windows/Fs2InstallerView.axaml.cs +++ b/Knossos.NET/Views/Windows/Fs2InstallerView.axaml.cs @@ -1,12 +1,14 @@ using Avalonia.Controls; +using Avalonia.Markup.Xaml; namespace Knossos.NET.Views { - public partial class Fs2InstallerView : Window + public partial class Fs2InstallerView : KnossosWindow { public Fs2InstallerView() { - InitializeComponent(); + //InitializeComponent(); + AvaloniaXamlLoader.Load(this); } } } diff --git a/Knossos.NET/Views/Windows/MainWindow.axaml b/Knossos.NET/Views/Windows/MainWindow.axaml index 9c2511f1..699300a4 100644 --- a/Knossos.NET/Views/Windows/MainWindow.axaml +++ b/Knossos.NET/Views/Windows/MainWindow.axaml @@ -4,13 +4,13 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:v="using:Knossos.NET.Views" - MinWidth="{Binding MinWindowWidth}" - MinHeight="{Binding MinWindowHeight}" x:DataType="vm:MainWindowViewModel" mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="900" x:Class="Knossos.NET.Views.MainWindow" Icon="/Assets/knossos-icon.ico" WindowStartupLocation="CenterScreen" + MinWidth="{Binding MinWindowWidth}" + MinHeight="{Binding MinWindowHeight}" Background="{StaticResource BackgroundColorPrimary}" Title="{Binding AppTitle}" xmlns:cvt="clr-namespace:Knossos.NET.Converters;assembly=Knossos.NET"> @@ -19,49 +19,6 @@ - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Knossos.NET/Views/Windows/MainWindow.axaml.cs b/Knossos.NET/Views/Windows/MainWindow.axaml.cs index 8ec6360f..0255eb0b 100644 --- a/Knossos.NET/Views/Windows/MainWindow.axaml.cs +++ b/Knossos.NET/Views/Windows/MainWindow.axaml.cs @@ -16,24 +16,6 @@ public MainWindow() InitializeComponent(); } - /// - /// For custom mode, for setting the task buttom at the last place in the menu - /// we need to change the margins so it is displayed properly. - /// - public void FixMarginButtomTasks() - { - var tasks = this.FindControl("TaskButtom"); - if (tasks != null) - { - tasks.Margin = new Thickness(9, -45, 0, 0); - } - var list = this.FindControl("ButtomList"); - if (list != null) - { - list.Margin = new Thickness(2, 0, -100, 0); - } - } - /// /// Change size of the main window /// @@ -56,7 +38,7 @@ protected override async void OnClosing(WindowClosingEventArgs e) await Dispatcher.UIThread.InvokeAsync(() => { Knossos.Tts(string.Empty); - MainWindowViewModel.Instance?.GlobalSettingsView?.CommitPendingChanges(); + MainViewModel.Instance?.GlobalSettingsView?.CommitPendingChanges(); Knossos.globalSettings.SaveSettingsOnAppClose(); canClose = true; }); diff --git a/Knossos.NET/Views/Windows/MessageBox.axaml.cs b/Knossos.NET/Views/Windows/MessageBox.axaml.cs index 9e6c7c4a..bdd25439 100644 --- a/Knossos.NET/Views/Windows/MessageBox.axaml.cs +++ b/Knossos.NET/Views/Windows/MessageBox.axaml.cs @@ -1,6 +1,14 @@ +using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Controls.Presenters; +using Avalonia.Controls.Primitives; using Avalonia.Markup.Xaml; using Avalonia.Threading; +using Avalonia.VisualTree; +using Knossos.NET.ViewModels; +using System; +using System.Linq; using System.Threading.Tasks; namespace Knossos.NET.Views @@ -41,6 +49,17 @@ public MessageBox() //Messagebox is not thread safe! public static Task Show(Window? parent, string text, string title, MessageBoxButtons buttons) + { + if (KnUtils.IsAndroid || KnUtils.IsBrowser) + { + // Redirect to the non-window version + return MessageBoxView.ShowAsync(text, title, buttons).ContinueWith(t => t.Result); + } + // Open a window to show the message + return ShowWindow(parent, text, title, buttons); + } + + private static Task ShowWindow(Window? parent, string text, string title, MessageBoxButtons buttons) { var msgbox = new MessageBox() { @@ -53,20 +72,21 @@ public static Task Show(Window? parent, string text, string ti var result = MessageBoxResult.OK; - void AddButton(string caption, MessageBoxResult r, bool def = false, string classes = "", int buttonWidth = -1) - { - var button = new Button { Content = caption, Width = 100}; - button.Click += (_, __) => { + void AddButton(string caption, MessageBoxResult r, bool def = false, string? classes = null, double? buttonWidth = null) + { + var button = new Button { Content = caption, Width = 100 }; + button.Click += (_, __) => + { result = r; msgbox.Close(); }; - if(classes != "") + if (classes != null) { button.Classes.Add(classes); } - if (buttonWidth != -1) + if (buttonWidth.HasValue) { - button.Width = buttonWidth; + button.Width = buttonWidth.Value; } buttonPanel.Children.Add(button); if (def) @@ -75,56 +95,64 @@ void AddButton(string caption, MessageBoxResult r, bool def = false, string clas } } + ButtonCreation(AddButton, buttons); + + var tcs = new TaskCompletionSource(); + msgbox.Closed += delegate { tcs.TrySetResult(result); }; + + if (parent != null && parent.IsVisible) + { + msgbox.ShowDialog(parent); + } + else + { + msgbox.Show(); + } + + return tcs.Task; + } + + /// + /// Creates buttons for both versions of the messagebox system, do not call directly + /// + internal static void ButtonCreation(Action addButtonMethod, MessageBoxButtons buttons) + { if (buttons == MessageBoxButtons.OK || buttons == MessageBoxButtons.OKCancel || buttons == MessageBoxButtons.DetailsOKCancel) { - AddButton("OK", MessageBoxResult.OK, true, "Accept"); + addButtonMethod("OK", MessageBoxResult.OK, true, "Accept", null); } if (buttons == MessageBoxButtons.YesNo || buttons == MessageBoxButtons.YesNoCancel) { - AddButton("Yes", MessageBoxResult.Yes, false, "Accept"); - AddButton("No", MessageBoxResult.No, true, "Cancel"); + addButtonMethod("Yes", MessageBoxResult.Yes, false, "Accept", null); + addButtonMethod("No", MessageBoxResult.No, true, "Cancel", null); } if (buttons == MessageBoxButtons.Continue || buttons == MessageBoxButtons.ContinueCancel || buttons == MessageBoxButtons.DetailsContinueCancel || buttons == MessageBoxButtons.ContinueCancelSkipVersion) { - AddButton("Continue", MessageBoxResult.Continue, false, "Accept"); + addButtonMethod("Continue", MessageBoxResult.Continue, false, "Accept", null); } if (buttons == MessageBoxButtons.OKCancel || buttons == MessageBoxButtons.YesNoCancel || buttons == MessageBoxButtons.ContinueCancel || buttons == MessageBoxButtons.DetailsOKCancel || buttons == MessageBoxButtons.DetailsContinueCancel || buttons == MessageBoxButtons.ContinueCancelSkipVersion) { - AddButton("Cancel", MessageBoxResult.Cancel, true, "Cancel"); + addButtonMethod("Cancel", MessageBoxResult.Cancel, true, "Cancel", null); } if (buttons == MessageBoxButtons.Details || buttons == MessageBoxButtons.DetailsOKCancel || buttons == MessageBoxButtons.DetailsContinueCancel) { - AddButton("Details", MessageBoxResult.Details, false, "Option"); + addButtonMethod("Details", MessageBoxResult.Details, false, "Option", null); } if (buttons == MessageBoxButtons.DontWarnAgainOK) { - AddButton("OK", MessageBoxResult.OK, true, "Accept"); - AddButton("Don't warn again", MessageBoxResult.DontWarnAgain, false, "Option", 150); + addButtonMethod("OK", MessageBoxResult.OK, true, "Accept", null); + addButtonMethod("Don't warn again", MessageBoxResult.DontWarnAgain, false, "Option", 150); } if (buttons == MessageBoxButtons.ContinueCancelSkipVersion) { - AddButton("Skip this version", MessageBoxResult.SkipVersion, false, "Option", 150); + addButtonMethod("Skip this version", MessageBoxResult.SkipVersion, false, "Option", 150); } - - var tcs = new TaskCompletionSource(); - msgbox.Closed += delegate { tcs.TrySetResult(result); }; - - if (parent != null && parent.IsVisible) - { - msgbox.ShowDialog(parent); - } - else - { - msgbox.Show(); - } - - return tcs.Task; } } -} +} \ No newline at end of file diff --git a/Knossos.NET/Views/Windows/ModDetailsView.axaml b/Knossos.NET/Views/Windows/ModDetailsView.axaml index d5ce51df..a68b561f 100644 --- a/Knossos.NET/Views/Windows/ModDetailsView.axaml +++ b/Knossos.NET/Views/Windows/ModDetailsView.axaml @@ -1,4 +1,4 @@ - - + - + @@ -111,5 +111,5 @@ - + \ No newline at end of file diff --git a/Knossos.NET/Views/Windows/ModDetailsView.axaml.cs b/Knossos.NET/Views/Windows/ModDetailsView.axaml.cs index 0c8f9cbf..0e760545 100644 --- a/Knossos.NET/Views/Windows/ModDetailsView.axaml.cs +++ b/Knossos.NET/Views/Windows/ModDetailsView.axaml.cs @@ -1,11 +1,12 @@ using Avalonia.Controls; using Avalonia.Layout; +using Avalonia.Markup.Xaml; using System; using System.ComponentModel; namespace Knossos.NET.Views { - public partial class ModDetailsView : Window + public partial class ModDetailsView : KnossosWindow { private Carousel _carousel; private Button _left; @@ -13,7 +14,8 @@ public partial class ModDetailsView : Window public ModDetailsView() { - InitializeComponent(); + //InitializeComponent(); + AvaloniaXamlLoader.Load(this); this.Closing += ModDetailsView_StopTTS; _carousel = this.Get("carousel"); diff --git a/Knossos.NET/Views/Windows/ModInstallView.axaml b/Knossos.NET/Views/Windows/ModInstallView.axaml index 3afa519a..773e487c 100644 --- a/Knossos.NET/Views/Windows/ModInstallView.axaml +++ b/Knossos.NET/Views/Windows/ModInstallView.axaml @@ -1,4 +1,4 @@ - - + diff --git a/Knossos.NET/Views/Windows/ModInstallView.axaml.cs b/Knossos.NET/Views/Windows/ModInstallView.axaml.cs index 7f81ad48..65d58411 100644 --- a/Knossos.NET/Views/Windows/ModInstallView.axaml.cs +++ b/Knossos.NET/Views/Windows/ModInstallView.axaml.cs @@ -1,15 +1,17 @@ using Avalonia.Controls; +using Avalonia.Markup.Xaml; using Knossos.NET.ViewModels; using System; using System.ComponentModel; namespace Knossos.NET.Views { - public partial class ModInstallView : Window + public partial class ModInstallView : KnossosWindow { public ModInstallView() { - InitializeComponent(); + //InitializeComponent(); + AvaloniaXamlLoader.Load(this); this.Closing += ForceCollectTrash; } diff --git a/Knossos.NET/Views/Windows/ModSettingsView.axaml b/Knossos.NET/Views/Windows/ModSettingsView.axaml index b072766c..8115a813 100644 --- a/Knossos.NET/Views/Windows/ModSettingsView.axaml +++ b/Knossos.NET/Views/Windows/ModSettingsView.axaml @@ -1,4 +1,4 @@ - - + diff --git a/Knossos.NET/Views/Windows/ModSettingsView.axaml.cs b/Knossos.NET/Views/Windows/ModSettingsView.axaml.cs index 1d019d58..69f81790 100644 --- a/Knossos.NET/Views/Windows/ModSettingsView.axaml.cs +++ b/Knossos.NET/Views/Windows/ModSettingsView.axaml.cs @@ -1,42 +1,35 @@ using Avalonia.Controls; +using Avalonia.Markup.Xaml; using Avalonia.Threading; using Knossos.NET.ViewModels; using System.Threading.Tasks; namespace Knossos.NET.Views { - public partial class ModSettingsView : Window + public partial class ModSettingsView : KnossosWindow { private bool canClose = false; public ModSettingsView() { - InitializeComponent(); + //InitializeComponent(); + AvaloniaXamlLoader.Load(this); } - protected override async void OnClosing(WindowClosingEventArgs e) + protected override async Task OnClosingAsync() { - //Intercept closing, do stuff, then re-call close - if (DataContext != null) + if (DataContext is ModSettingsViewModel vm) { - var vm = DataContext as ModSettingsViewModel; - if (vm != null) + if (!canClose) { - if (!canClose) + await Dispatcher.UIThread.InvokeAsync(() => { - e.Cancel = true; - - await Dispatcher.UIThread.InvokeAsync(() => { - vm.SaveSettingsCommand(); - canClose = true; - }); - - if (canClose) - this.Close(); - } + vm.SaveSettingsCommand(); + canClose = true; + }); } } - base.OnClosing(e); + return canClose; } } } diff --git a/Knossos.NET/Views/Windows/PerformanceHelpView.axaml b/Knossos.NET/Views/Windows/PerformanceHelpView.axaml index 0a9dcebc..c9e4cad1 100644 --- a/Knossos.NET/Views/Windows/PerformanceHelpView.axaml +++ b/Knossos.NET/Views/Windows/PerformanceHelpView.axaml @@ -1,4 +1,4 @@ -If in-game performance is slow, close the game and then disable "Extra Multisample AA" and "Shadows". Note, "Extra Multisample AA" is an added, more expensive anti-aliasing step and if it is disabled then "Post-processing AA" will still be be applied. If performance remains slow the next best options to disable are "Deferred Lighting" and then "Post-processing AA". - + diff --git a/Knossos.NET/Views/Windows/PerformanceHelpView.axaml.cs b/Knossos.NET/Views/Windows/PerformanceHelpView.axaml.cs index 4562f626..c018a68b 100644 --- a/Knossos.NET/Views/Windows/PerformanceHelpView.axaml.cs +++ b/Knossos.NET/Views/Windows/PerformanceHelpView.axaml.cs @@ -1,12 +1,14 @@ using Avalonia.Controls; +using Avalonia.Markup.Xaml; namespace Knossos.NET.Views { - public partial class PerformanceHelpView : Window + public partial class PerformanceHelpView : KnossosWindow { public PerformanceHelpView() { - InitializeComponent(); + //InitializeComponent(); + AvaloniaXamlLoader.Load(this); } } } diff --git a/Knossos.NET/Views/Windows/QuickSetupView.axaml b/Knossos.NET/Views/Windows/QuickSetupView.axaml index bba5ba27..1ac0b6d7 100644 --- a/Knossos.NET/Views/Windows/QuickSetupView.axaml +++ b/Knossos.NET/Views/Windows/QuickSetupView.axaml @@ -1,4 +1,4 @@ - - + diff --git a/Knossos.NET/Views/Windows/QuickSetupView.axaml.cs b/Knossos.NET/Views/Windows/QuickSetupView.axaml.cs index 81e696d1..d4888930 100644 --- a/Knossos.NET/Views/Windows/QuickSetupView.axaml.cs +++ b/Knossos.NET/Views/Windows/QuickSetupView.axaml.cs @@ -1,12 +1,14 @@ using Avalonia.Controls; +using Avalonia.Markup.Xaml; namespace Knossos.NET.Views { - public partial class QuickSetupView : Window + public partial class QuickSetupView : KnossosWindow { public QuickSetupView() { - InitializeComponent(); + //InitializeComponent(); + AvaloniaXamlLoader.Load(this); } } } diff --git a/Knossos.NET/Views/Windows/ReportModView.axaml b/Knossos.NET/Views/Windows/ReportModView.axaml index 3ddcdf8a..6b06bb0d 100644 --- a/Knossos.NET/Views/Windows/ReportModView.axaml +++ b/Knossos.NET/Views/Windows/ReportModView.axaml @@ -1,4 +1,4 @@ - - + diff --git a/Knossos.NET/Views/Windows/ReportModView.axaml.cs b/Knossos.NET/Views/Windows/ReportModView.axaml.cs index 54394176..eb3b3650 100644 --- a/Knossos.NET/Views/Windows/ReportModView.axaml.cs +++ b/Knossos.NET/Views/Windows/ReportModView.axaml.cs @@ -3,10 +3,11 @@ using Avalonia.Markup.Xaml; namespace Knossos.NET.Views; -public partial class ReportModView : Window +public partial class ReportModView : KnossosWindow { public ReportModView() { - InitializeComponent(); + //InitializeComponent(); + AvaloniaXamlLoader.Load(this); } } \ No newline at end of file diff --git a/Knossos.NET/Views/Windows/ServerCreatorView.axaml b/Knossos.NET/Views/Windows/ServerCreatorView.axaml index 0414b3bd..6ff97f9e 100644 --- a/Knossos.NET/Views/Windows/ServerCreatorView.axaml +++ b/Knossos.NET/Views/Windows/ServerCreatorView.axaml @@ -1,4 +1,4 @@ - - + diff --git a/Knossos.NET/Views/Windows/ServerCreatorView.axaml.cs b/Knossos.NET/Views/Windows/ServerCreatorView.axaml.cs index d66b3e9b..f3bdf711 100644 --- a/Knossos.NET/Views/Windows/ServerCreatorView.axaml.cs +++ b/Knossos.NET/Views/Windows/ServerCreatorView.axaml.cs @@ -4,10 +4,11 @@ namespace Knossos.NET.Views.Windows; -public partial class ServerCreatorView : Window +public partial class ServerCreatorView : KnossosWindow { public ServerCreatorView() { - InitializeComponent(); + //InitializeComponent(); + AvaloniaXamlLoader.Load(this); } } \ No newline at end of file diff --git a/VP.NET/VP.NET.csproj b/VP.NET/VP.NET.csproj index 11fa1552..f4826092 100644 --- a/VP.NET/VP.NET.csproj +++ b/VP.NET/VP.NET.csproj @@ -1,7 +1,7 @@ - net6.0 + net6.0;net8.0;net10.0-android enable enable