OverviewFilesCommitsIssuesDocsReleasesPeers

ifsrenderer-linux / WpfDisplay / Views / MainWindow.xaml.cs

308 lines · csharp ref:
#nullable enable
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media.Imaging;

using IFSEngine.Model;
using IFSEngine.Rendering;

using WpfDisplay.Helper;
using WpfDisplay.Models;
using WpfDisplay.Properties;
using WpfDisplay.Serialization;
using WpfDisplay.Services;
using WpfDisplay.ViewModels;

namespace WpfDisplay.Views;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    private EditorWindow _editorWindow;
    private GeneratorWindow _generatorWindow;
    private MainViewModel vm => (MainViewModel)DataContext;
    private PaletteManagerWindow _paletteManagerWindow;

    public MainWindow()
    {
        InitializeComponent();

        MaterialDesignThemes.Wpf.ShadowAssist.SetCacheMode(this, null);//disable gpu cache
        animationsPanel.ToggleAutoHide();
        ContentRendered += MainWindow_ContentRendered;
    }

    private async void MainWindow_ContentRendered(object sender, EventArgs e)
    {
        FixAutoDockHeight();
        
        if(mainDisplay.GraphicsContext is null)
            throw new InvalidOperationException("Failed to initialize graphics context.");

        //init workspace
        var renderer = new RendererGL(mainDisplay.GraphicsContext);
        mainDisplay.AttachRenderer(renderer);
        var workspace = new Workspace(renderer);
        await workspace.Initialize();

        var workflow = WelcomeWorkflow.FromScratch;
        if (App.OpenVerbPath is not null)
        {//handle open verb, no welcome screen
            IFS ifs;
            try
            {
                ifs = IfsNodesSerializer.LoadJsonFile(App.OpenVerbPath, workspace.LoadedTransforms, workspace.LoadedEffects, true);
            }
            catch (SerializationException)
            {
                MessageBox.Show(this, $"Failed to load params from '{App.OpenVerbPath}'");
                ifs = new IFS();
            }
            workspace.LoadParams(ifs, App.OpenVerbPath);
        }
        else if (App.OpenVerbPath is null && Settings.Default.IsWelcomeShownOnStartup)
        {
            var welcomeViewModel = new WelcomeViewModel(workspace.IncludeSources, workspace.LoadedTransforms, workspace.LoadedEffects);
            var welcomeWindow = new WelcomeWindow
            {
                Owner = this,
                DataContext = welcomeViewModel
            };
            welcomeWindow.ShowDialog();
            Focus();

            //TODO: refactor this block below
            workflow = welcomeViewModel.SelectedWorkflow;
            if (workflow == WelcomeWorkflow.Explore)
                workspace.LoadParams(welcomeViewModel.ExploreParams, null);
            else if (workflow == WelcomeWorkflow.LoadRecent)
                workspace.LoadParams(welcomeViewModel.ExploreParams, welcomeViewModel.SelectedFilePath);
            else if (workflow == WelcomeWorkflow.LoadFromFile)
            {
                try
                {
                    var ext = Path.GetExtension(welcomeViewModel.SelectedFilePath);
                    if (ext == ".ifsjson")
                        workspace.LoadParamsFileAsync(welcomeViewModel.SelectedFilePath, false).Wait();
                    else if (ext == ".png")
                    {
                        var pngImge = BitmapFrame.Create(new Uri(welcomeViewModel.SelectedFilePath));
                        if (PngMetadataHelper.TryExtractParamsFromImage(pngImge, workspace.LoadedTransforms, workspace.LoadedEffects, out var ifs))
                            workspace.LoadParams(ifs, null);
                    }
                }
                catch
                {
                    workspace.UpdateStatusText($"Failed to load params - {welcomeViewModel.SelectedFilePath}");
                }
            }

        }

        DataContext = new MainViewModel(workspace, workflow);

        if (workflow == WelcomeWorkflow.BrowseRandoms)
            GeneratorButton_Click(null, null);
        if (workflow == WelcomeWorkflow.VisitSettings)
            SettingsButton_Click(null, null);

        vm.AnimationViewModel.Channels.CollectionChanged += (s, e) =>
        {
            if (vm.workspace.Ifs.Dopesheet.Channels.Count > 0)
                ShowAnimationsPanel();
        };
    }

    /// <summary>
    /// ad hoc fix for issue <a href="https://github.com/Dirkster99/AvalonDock/issues/298"/>
    /// </summary>
    private void FixAutoDockHeight()
    {
        tonemappingPane.DockHeight = GridLength.Auto;
        environmentPane.DockHeight = GridLength.Auto;
        performancePane.DockHeight = GridLength.Auto;
    }

    private void GeneratorButton_Click(object sender, RoutedEventArgs e)
    {
        //create window
        if (_generatorWindow == null || !_generatorWindow.IsLoaded)
        {
            _generatorWindow = new GeneratorWindow
            {
                Owner = this
            };
            var generatorViewModel = new GeneratorViewModel(vm);
            _generatorWindow.DataContext = generatorViewModel;
        }

        if (_generatorWindow.ShowActivated)
            _generatorWindow.Show();
        //bring to foreground
        if (!_generatorWindow.IsActive)
            _generatorWindow.Activate();
    }

    private void EditorButton_Click(object sender, RoutedEventArgs e)
    {
        //create window
        if (_editorWindow == null || !_editorWindow.IsLoaded)
        {
            _editorWindow = new EditorWindow
            {
                Owner = this
            };
            _editorWindow.SetBinding(DataContextProperty, new Binding(".") { Source = vm.IFSViewModel, Mode = BindingMode.TwoWay });
        }

        if (_editorWindow.ShowActivated)
            _editorWindow.Show();
        //bring to foreground
        if (!_editorWindow.IsActive)
            _editorWindow.Activate();
    }

    private void PalettesButton_Click(Object sender, RoutedEventArgs e)
    {
        OpenPaletteManagerWindow(false);
    }

    private ColorPalette? OpenPaletteManagerWindow(bool isSelector)
    {
        //create window
        if (_paletteManagerWindow == null || !_paletteManagerWindow.IsLoaded)
        {
            var library = new WpfDisplay.Services.PaletteLibraryService(App.PalettesDirectoryPath);
            _paletteManagerWindow = new PaletteManagerWindow
            {
                Owner = this
            };
            var paletteVm = new WpfDisplay.ViewModels.PaletteManagerViewModel(library, vm);
            _paletteManagerWindow.DataContext = paletteVm;
            _ = paletteVm.InitializeAsync();
        }


        if (isSelector)
        {
            if (_paletteManagerWindow.ShowDialog() == true)
                return ((PaletteManagerViewModel)_paletteManagerWindow.DataContext).BrowserViewModel.SelectedPalette?.Palette;
        }
        else
        {
            if (_paletteManagerWindow.ShowActivated)
                _paletteManagerWindow.Show();
            //bring to foreground
            if (!_paletteManagerWindow.IsActive)
                _paletteManagerWindow.Activate();
        }

        return null;
    }

    private void SettingsButton_Click(object sender, RoutedEventArgs e)
    {
        var settingsWindow = new SettingsWindow
        {
            Owner = this,
            DataContext = new SettingsViewModel(vm)
        };
        if (settingsWindow.ShowDialog() == true)
            vm.StatusBarText = "Settings saved.";
    }

    private void AboutButton_Click(object sender, RoutedEventArgs e)
    {
        var dialog = new AboutDialogWindow
        {
            Owner = this
        };
        dialog.ShowDialog();
    }

    protected override async void OnClosing(CancelEventArgs e)
    {
        if (vm.workspace.HasUnsavedChanges)
        {
            var dialog = new ExitDialogWindow
            {
                Owner = this
            };
            dialog.ShowDialog();
            if (dialog.ExitDialogResult == ExitDialogWindow.ExitChoice.Cancel)
            {
                e.Cancel = true;
                base.OnClosing(e);
                return;
            }
            else if (dialog.ExitDialogResult == ExitDialogWindow.ExitChoice.Save)
            {
                await vm.SaveParamsAsCommand.ExecuteAsync(null);
            }
        }

        _generatorWindow?.Close();
        _editorWindow?.Close();

        await vm.DisposeAsync();
        Application.Current.Shutdown();

        base.OnClosing(e);
    }

    private void Undo_Executed(object sender, ExecutedRoutedEventArgs e) => vm.IFSViewModel.UndoCommand.Execute(null);

    private void Undo_CanExecute(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = vm?.IFSViewModel.UndoCommand.CanExecute(null) ?? false;

    private void Redo_Executed(object sender, ExecutedRoutedEventArgs e) => vm.IFSViewModel.RedoCommand.Execute(null);

    private void Redo_CanExecute(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = vm?.IFSViewModel.RedoCommand.CanExecute(null) ?? false;

    private void Copy_Executed(object sender, ExecutedRoutedEventArgs e) => vm.CopyClipboardParamsCommand.Execute(null);

    private void Copy_CanExecute(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = vm?.CopyClipboardParamsCommand.CanExecute(null) ?? false;

    private void Paste_Executed(object sender, ExecutedRoutedEventArgs e) => vm.PasteClipboardParamsCommand.Execute(null);

    private void Paste_CanExecute(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = vm?.PasteClipboardParamsCommand.CanExecute(null) ?? false;

    private void mainWindow_DragOver(object sender, DragEventArgs e)
    {
        e.Handled = true;
        e.Effects = FileDropHandler.GetDragDropEffect(e.Data);
    }

    private void mainWindow_Drop(object sender, DragEventArgs e)
    {
        e.Handled = true;
        vm.DropFileCommand.Execute(e.Data);
    }

    private void dockManager_DocumentClosing(object sender, AvalonDock.DocumentClosingEventArgs e) => vm.workspace.LoadParams(IFS.Default, null);

    private async void mainDisplay_GamepadConnectionStateChanged(object sender, bool e) => await Dispatcher.InvokeAsync(() => vm.IsGamepadConnected = e);

    private void mainDisplay_DisplayResolutionChanged(object sender, System.EventArgs e) => vm?.QualitySettingsViewModel?.UpdatePreviewRenderSettings();

    private void ShowAnimationsPanel()
    {
        if (animationsPanel.IsAutoHidden)
            animationsPanel.ToggleAutoHide();
    }

    private void animationsPanel_IsActiveChanged(object sender, System.EventArgs e)
    {
        //Avoid leaving the animation panel in a half-open state called "AutoHide" in AvalonDock.
        //This way the user does not need to click the pin button to dock the panel after opening it.
        if (animationsPanel.IsActive)
            ShowAnimationsPanel();
    }
}