View Issue Details

IDProjectCategoryView StatusLast Update
0005177NoesisGUIUnitypublic2026-08-20 20:16
Reporterstonstad Assigned To 
PrioritynormalSeveritymajor 
Status newResolutionopen 
Product Version4.0 
Summary0005177: 4.0 RC5. AssetDependency Fails After Subsequent Stop/Play Cycle
Description

I am seeing AssetDependency attribute resolution fail after Unity playback stops and is restarted.

1) Play -> Stop -> Play generates XAML errors.
OR
2) It also happens every time I edit a CS file and the app domain is reloaded.

Note that first time playback works and no XAML errors are generated on Unity import. These are runtime errors that occur on second playback only.

[NOESIS] Xaml not found 'Assets/User Interface/Screens/Title/TitleScreen.xaml'
UnityEngine.StackTraceUtility:ExtractStackTrace ()
Noesis.GUI:LoadComponent (object,string) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC5/Runtime/API/Core/NoesisGUI.cs:373)
StellarConquest.Presentation.Unity.UI.TitleScreen:InitializeComponent () (at Assets/User Interface/Screens/Title/TitleScreen.xaml.cs:62)
StellarConquest.Presentation.Unity.UI.TitleScreen:.ctor () (at Assets/User Interface/Screens/Title/TitleScreen.xaml.cs:57)
StellarConquest.Presentation.Unity.UI.UIStateMachine:<ShowTitleScreen>b__80_0 () (at Assets/User Interface/UIStateMachine.cs:303)
StellarConquest.Presentation.Unity.UI.UIStateMachine:ScreenTransition (System.TimeSpan,System.TimeSpan,System.Action,System.Action) (at Assets/User Interface/UIStateMachine.cs:432)
StellarConquest.Presentation.Unity.UI.UIStateMachine:ScreenTransition (System.Action,System.Action) (at Assets/User Interface/UIStateMachine.cs:423)
StellarConquest.Presentation.Unity.UI.UIStateMachine:ShowTitleScreen (bool) (at Assets/User Interface/UIStateMachine.cs:299)

Unity 6.5.7f1.

Attached Files
TitleScreen.xaml (6,445 bytes)   
<UserControl
    x:Class="StellarConquest.Presentation.Unity.UI.TitleScreen"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
    xmlns:noesis="clr-namespace:NoesisGUIExtensions"
    xmlns:b="http://schemas.microsoft.com/xaml/behaviors" 
    xmlns:local="clr-namespace:StellarConquest.Presentation.Unity.UI"
    d:DesignWidth="1280" d:DesignHeight="720">
    <Grid>
        <Viewbox x:Name="LoginScreenViewBox">
            <local:ViewboxGridControl x:Name="_LayoutRoot">
                <Grid>
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="1*"/>
                            <RowDefinition Height="6*"/>
                            <RowDefinition Height="1*"/>
                        </Grid.RowDefinitions>

                        <StackPanel x:Name="_LoginStackPanel" Orientation="Horizontal" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center">
                            <local:ButtonPanelControl x:Name="_ButtonPanelControl" VerticalAlignment="Center" Margin="0, -35, 20, 0"/>

                            <StackPanel x:Name="_NewsStackPanel1" Orientation="Vertical" VerticalAlignment="Center">
                                <local:NewsControl x:Name="_NewsControl1" Margin="0, 0, 0, 20"/>
                                <local:SocialMediaPanelControl x:Name="_SocialMediaPanelControl"/>
                            </StackPanel>

                            <StackPanel x:Name="_NewsStackPanel2" Orientation="Vertical" VerticalAlignment="Center" Visibility="Collapsed">
                                <local:NewsControl x:Name="_NewsControl2" Margin="0, 0, 0, 20"/>
                                <Grid Height="50"/>
                            </StackPanel>
                        </StackPanel>

                        <TextBlock x:Name="_Handle" Grid.Row="2" HorizontalAlignment="Right" FontFamily="{StaticResource Teko}" FontSize="35" VerticalAlignment="Bottom" Foreground="White" Opacity="0.9" Margin="0, 0, 60, 35"/>
                        <TextBlock x:Name="_VersionTextBlock" Grid.Row="2" HorizontalAlignment="Center" FontFamily="{StaticResource Teko}" FontSize="22" VerticalAlignment="Bottom" Foreground="White" Opacity="0.7" Margin="0, 0, 0, 40"/>
                    </Grid>

                    <StackPanel x:Name="_SecondaryScreenStackPanel" HorizontalAlignment="Center" VerticalAlignment="Center"/>



                    <Grid x:Name="_PreviewGrid" Background="Transparent" Opacity="0" Visibility="Collapsed">
                        <Grid Background="#99000000" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>

                        <Grid HorizontalAlignment="Center" VerticalAlignment="Stretch">
                            <Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                                <b:Interaction.Behaviors>
                                    <noesis:BackgroundEffectBehavior Source="{DynamicResource BaseBlurSource}">
                                        <BlurEffect Radius="{Binding Radius, Source={StaticResource TopBlurRadius}}"/>
                                    </noesis:BackgroundEffectBehavior>
                                </b:Interaction.Behaviors>
                            </Border>

                            <Grid Background="#AA000000" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>

                            <Rectangle Fill="#66777777" Width="2" HorizontalAlignment="Left" VerticalAlignment="Stretch"/>
                            <Rectangle Fill="#66777777" Width="2" HorizontalAlignment="Right" VerticalAlignment="Stretch"/>

                            <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="50, -50, 50, 50">
                                <Viewbox Width="600" Margin="-10, 0, -10, 15">
                                    <Grid>
                                        <Grid.Effect>
                                            <DropShadowEffect BlurRadius="10" ShadowDepth="3" Opacity="1"/>
                                        </Grid.Effect>
                                        <Image x:Name="_LogoEllipse" Source="{StaticResource ellipse}" Width="550" Stretch="Uniform" Margin="0, 0, 0, 100"/>
                                        <TextBlock x:Name="_LogoText" HorizontalAlignment="Center" FontFamily="{StaticResource Agency}" FontSize="95" VerticalAlignment="Bottom" Margin="0, 0, 0, 0" Foreground="White" Opacity="0.9" noesis:Text.CharacterSpacing="350" Text="STELLAR CONQUEST"/>
                                    </Grid>
                                </Viewbox>

                                <StackPanel Orientation="Vertical" MaxWidth="500" Effect="{StaticResource UIDropShadow}">
                                    <TextBlock FontFamily="{StaticResource Teko}" FontSize="40" FontWeight="Normal" HorizontalAlignment="Center" Margin="0, 0, 0, 95">PREVIEW</TextBlock>
                                    <TextBlock FontFamily="{StaticResource DefaultFontFamily}" FontSize="30" FontWeight="Normal" HorizontalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap" Margin="0, 0, 0, 35">This is an unfinished version of the game. You might encounter broken or missing parts.</TextBlock>
                                    <TextBlock FontFamily="{StaticResource DefaultFontFamily}" FontSize="30" FontWeight="Normal" HorizontalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap" Margin="0, 0, 0, 35">Saved galaxies won't work with the final release version.</TextBlock>
                                    <TextBlock FontFamily="{StaticResource HeadingFontFamily}" FontSize="30" FontWeight="Bold" HorizontalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap" Margin="0, 0, 0, 0">Press any key or button to start</TextBlock>
                                </StackPanel>
                            </StackPanel>
                        </Grid>
                    </Grid>

                </Grid>
            </local:ViewboxGridControl>
        </Viewbox>

    </Grid>
</UserControl>
TitleScreen.xaml (6,445 bytes)   
TitleScreen.xaml.cs (15,733 bytes)   
using Noesis;
using StellarConquest.Model.Actions;
using StellarConquest.Model.Authentication;
using StellarConquest.Model.Core;
using StellarConquest.Model.Performance;
using StellarConquest.Model.Platform;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using EventArgs = Noesis.EventArgs;
using Grid = Noesis.Grid;
using GUI = Noesis.GUI;
using Task = System.Threading.Tasks.Task;

namespace StellarConquest.Presentation.Unity.UI
{
    [AssetDependency]
    public partial class TitleScreen : UserControl, IGameScreenPrimary, IGameScreenInputProvider
    {
        private bool _IsPlayerServerError = false;
        private bool _IsGameServerError = false;
        private bool _IsSystemServerError = false;
        private bool _IsServiceAuthenticationError = false;

        private string _GameNews;
        private Statistics _Statistics = new Statistics();

        public List<AuthenticatedPlayer> _Players = null;

        private FrameworkElement _Root;
        private Grid _LayoutRoot;

        private StackPanel _NewsStackPanel1;
        private NewsControl _NewsControl1;

        private StackPanel _NewsStackPanel2;
        private NewsControl _NewsControl2;

        private StackPanel _SecondaryScreenStackPanel;
        private ServerControl _ServerControl;
        private ServerListControl _ServerListControl;
        private LocalizationControl _LocalizationControl;

        private ButtonPanelControl _ButtonPanelControl;
        private TextBlock _VersionTextBlock;
        private TextBlock _Handle;

        public bool IsCursorOverUI { get { return false; } }

        private Grid _PreviewGrid;
        private static bool _PreviewGridShown = false;

        public TitleScreen()
        {
            Initialized += OnInitialized;
            InitializeComponent();
        }

        private void InitializeComponent()
        {
            GUI.LoadComponent(this, "Assets/User Interface/Screens/Title/TitleScreen.xaml");
            _Root = Content as FrameworkElement;
            _LayoutRoot = _Root.FindName(nameof(_LayoutRoot)) as Grid;

            _NewsStackPanel1 = _Root.FindName(nameof(_NewsStackPanel1)) as StackPanel;
            _NewsControl1 = _Root.FindName(nameof(_NewsControl1)) as NewsControl;

            _NewsStackPanel2 = _Root.FindName(nameof(_NewsStackPanel2)) as StackPanel;
            _NewsControl2 = _Root.FindName(nameof(_NewsControl2)) as NewsControl;

            _SecondaryScreenStackPanel = _Root.FindName(nameof(_SecondaryScreenStackPanel)) as StackPanel;

            _ButtonPanelControl = _Root.FindName(nameof(_ButtonPanelControl)) as ButtonPanelControl;
            _VersionTextBlock = _Root.FindName(nameof(_VersionTextBlock)) as TextBlock;
            _Handle = _Root.FindName(nameof(_Handle)) as TextBlock;

            _PreviewGrid = _Root.FindName(nameof(_PreviewGrid)) as Grid;

            _LayoutRoot.Visibility = Visibility.Collapsed;
        }

        private void OnInitialized(object sender, EventArgs args)
        {
            if (!Application.isPlaying)
                return;

            RegisterEvents();

            // TODO: enable and test account id caching feature when Steam is down.
            //var result = PlayerPreferences.Instance.GetPreviousAccount();
            //if (result.Item1 != null && result.Item2.HasValue)
            //{
            //    SessionState.Instance.AuthenticationToken = new AuthenticationToken(result.Item1);
            //    SessionState.Instance.AccountId = result.Item2.Value;
            //}

            _ = Initialize(PlayerPreferences.Instance.ConquestEnvironment, Application.isEditor);

            //https://howtomarketagame.com/2021/06/21/neat-ideas-for-your-games-demo/
            //TutorialControl tutorialControl = new TutorialControl();
            //string text = "Thank you for playing this alpha build of Stellar Conquest. Please join us on our Discord channel for news about the next play test. See you for the next update!" + Environment.NewLine + Environment.NewLine;
            //tutorialControl.SetText("Build Expired", false, text);
            //tutorialControl.Show();

            if (!_PreviewGridShown)
            {
                _PreviewGrid.Visibility = Visibility.Visible;
                _PreviewGrid.FadeInScreen(this);
                _PreviewGridShown = true;
            }
        }

        public async Task Initialize(EnvironmentType cloudEnvironment, bool isEditor)
        {
            UnityDispatcher.Instance.Invoke(() =>
            {
                _Handle.Text = string.Empty;
                ChatBuffer.Instance.Execute(new ChatAction() { Clear = true });
            });

            _IsSystemServerError = false;
            _IsGameServerError = false;

            try
            {
                await NetworkClient.Instance.Initialize(cloudEnvironment, true, isEditor);

                _IsSystemServerError = !await NetworkClient.Instance.TestSystemServerHttpConnection(GameStateMachine.Instance.CancellationTokenSource.Token);

                if (!_IsSystemServerError)
                {
                    var taskA = GameServices.Instance.InitializeGameService(); // initialize Steam or XBL and create Stellar Conquest authorization token
                    var taskB = GetGameServerVersion();
                    var taskC = GetGameNewsAsync();

                    // initialize game services
                    await taskA;
                    _IsServiceAuthenticationError = !taskA.Result;

                    if (!_IsServiceAuthenticationError && !_IsSystemServerError)
                    {
                        var taskD = GetCloudLicenseAsync(); // get license using game service authorization token
                        await taskD;
                    }

                    if (taskB != null)
                        await taskB;
                    if (taskC != null)
                        await taskC;
                }

                UnityDispatcher.Instance.Invoke(() => FadeInUI());
            }
            catch (TaskCanceledException)
            {
            }
            catch (Exception e)
            {
                Debug.LogError(e);
                _IsSystemServerError = true;
            }

#if (UNITY_STANDALONE_WIN)
            if (!Application.isEditor && Constants.System.Build == "Debug")
                UIStateMachine.Instance.UIShowMessage("Build Error", "Warning: DEBUG library detected in RELEASE build.");
#endif

            if (_IsServiceAuthenticationError)
            {
                UnityDispatcher.Instance.Invoke(() =>
                {
#if(UNITY_STANDALONE_WIN)
                    _NewsControl1.ShowError("Please launch Steam before playing Stellar Conquest and ensure you have access to this game.");
#else
                    _NewsControl1.ShowError("A problem occurred while authenticating your account Xbox Live.");
#endif
                    _ButtonPanelControl.SetPlayButtonText("RETRY");
                });
            }
            else if (_IsSystemServerError)
            {
                UnityDispatcher.Instance.Invoke(() =>
                {
                    FadeInUI();

                    _NewsControl1.ShowError($"Stellar Conquest ({PlayerPreferences.Instance.ConquestEnvironment}) " +
                        "is down for maintenance and will be back up shortly. Please retry again.");
                    _ButtonPanelControl.SetPlayButtonText("RETRY");
                });
            }
            else if (!_IsSystemServerError)
            {
                UnityDispatcher.Instance.Invoke(() =>
                {
                    _NewsControl1.Update(_Statistics, _GameNews, null);
                    if (SessionState.Instance.ClientVersion == SessionState.Instance.ServerVersion)
                        _ButtonPanelControl.SetConquestButtonEnabled(SessionState.Instance.HasLicense && !_IsSystemServerError && !_IsGameServerError);
                    else
                        _ButtonPanelControl.SetConquestButtonEnabled(false);
                });
            }
        }

        private async Task Login()
        {
            CheckedResponse<List<AuthenticatedPlayer>> result = null;
            try
            {
                // get players
                result = await NetworkClient.Instance.GetPlayers();

                // get price list
                _ = GetPriceListAsync();
            }
            catch (Exception e)
            {
                Debug.LogError(e);
                result = new CheckedResponse<List<AuthenticatedPlayer>>(null, e.Message);
            }

            UnityDispatcher.Instance.Invoke(() =>
            {
                PlayerPreferences.Instance.FPSLogin = Mathf.RoundToInt(GameState.Instance.FrameRateCounter.FPS);
                PlayerPreferences.Instance.SavePreferences();

                if (result != null && result.Result != null)
                {
                    SessionState.Instance.AuthenticatedPlayers = result.Result;
                    if (result.Result.Count == 0)
                        GameStateMachine.Instance.SetState(GameStateType.CharacterCreation);
                    else
                        GameStateMachine.Instance.SetState(GameStateType.CharacterSelection);

                    RuntimeSettings.Instance.SendTelemetry();
                }
                else if (result == null || result.Result == null || !string.IsNullOrEmpty(result.Error))
                {
                    ShowPlayOptions();
                    if (result != null && !string.IsNullOrEmpty(result.Error))
                        _NewsControl2.ShowError("Login Error: " + result.Error);
                    else
                        _NewsControl2.ShowError("Login Error: unable to access account.");
                }
            });
        }

        private void ShowPlayOptions()
        {
            UIStateMachine.Instance.TransitionLayer.ShowLoadingImage(false, false);

            _ServerControl = null;
            _ServerListControl = null;
            _LocalizationControl = null;

            _SecondaryScreenStackPanel.Children.Clear();
            _ButtonPanelControl.SetPlayButtonEnabled(true);
            _ButtonPanelControl.SetPlayButtonText("PLAY");

            _NewsStackPanel1.Visibility = Visibility.Collapsed;
            _NewsStackPanel2.Visibility = Visibility.Visible;
            _ButtonPanelControl.Visibility = Visibility.Visible;

            _ButtonPanelControl.ShowPlayOptions();
            _Handle.Visibility = Visibility.Collapsed;
            _VersionTextBlock.Visibility = Visibility.Collapsed;
        }

        private void ShowTitleOptions()
        {
            UIStateMachine.Instance.TransitionLayer.ShowLoadingImage(false, false);

            _NewsStackPanel1.Visibility = Visibility.Visible;
            _NewsStackPanel2.Visibility = Visibility.Collapsed;
            _ButtonPanelControl.Visibility = Visibility.Visible;
            if (_ServerControl != null)
                _ServerControl.Visibility = Visibility.Collapsed;
            if (_ServerListControl != null)
                _ServerListControl.Visibility = Visibility.Collapsed;
            _ButtonPanelControl.ShowTitleOptions();
            _Handle.Visibility = Visibility.Visible;
            _VersionTextBlock.Visibility = Visibility.Visible;
        }

        private void FadeInUI()
        {
            if (_LayoutRoot.Visibility == Visibility.Collapsed)
            {
                _LayoutRoot.Visibility = Visibility.Visible;
                _LayoutRoot.FadeInScreen(this);
            }
        }

        private void Shutdown()
        {

            if (!Application.isEditor)
            {
                IsHitTestVisible = false;
                _ = Task.Run(async () =>
                {
                    try
                    {
                        await GameServices.Instance.ShutdownGameService();
                        if (ServerBootstrap.Instance.IsServerRunning && !ServerBootstrap.Instance.IsDedicatedServer)
                            await NetworkClient.Instance.GracefulPlayerServerShutdown();
                        ServerBootstrap.Instance.StopServer(false);
                    }
                    catch (Exception e)
                    {
                        Debug.Log(e);
                    }
                    finally
                    {
                        UnityDispatcher.Instance.Invoke(() => Application.Quit());
                    }
                });
            }
        }

        public void UpdateInput()
        {
            if (_PreviewGrid.Visibility == Visibility.Visible)
                if (Input.anyKeyDown || Input.GetMouseButtonDown(0) || Input.GetButton(InputNames.GamePad.A) || Input.GetButton(InputNames.GamePad.B))
                {
                    if (_PreviewGrid.Visibility == Visibility.Visible)
                    {
                        _PreviewGrid.FadeOutScreen(this, () => _PreviewGrid.Visibility = Visibility.Collapsed);
                        return;
                    }
                }

            if (Input.GetKeyDown(KeyCode.Escape))
            {
                if (GameServerCancellationTokenSource != null)
                    GameServerCancellationTokenSource.Cancel(false);
                else
                {
                    if (_ServerControl != null)
                    {
                        _SecondaryScreenStackPanel.Children.Remove(_ServerControl);
                        _ServerControl = null;
                        ShowPlayOptions();
                    }
                    else if (_ServerListControl != null)
                    {
                        _SecondaryScreenStackPanel.Children.Remove(_ServerListControl);
                        _ServerListControl = null;
                        ShowPlayOptions();
                    }
                    else if (_LocalizationControl != null)
                    {
                        _SecondaryScreenStackPanel.Children.Remove(_LocalizationControl);
                        _LocalizationControl = null;
                        ShowPlayOptions();
                    }
                    else if (_ButtonPanelControl.Visibility == Visibility.Visible && _NewsStackPanel2.Visibility == Visibility.Visible)
                    {
                        ShowTitleOptions();
                    }
                    else
                    {
                        InputControl inputControl = new InputControl("Quit", "Quit Stellar Conquest?", null, InputControlButtonTypes.Yes | InputControlButtonTypes.No);
                        inputControl.Hidden += (sender, e) =>
                        {
                            if (inputControl.Response == InputControlButtonTypes.Yes)
                                Shutdown();
                            else
                                this.FadeInScreen(this);
                        };

                        this.FadeOutScreen(this);
                        inputControl.Show();
                    }
                }
            }

#if (UNITY_GAMECORE_XBOXSERIES)
            if (Input.GetKeyDown(KeyCode.Alpha1))
                _ = Login();
#endif
        }

        public void UpdateUserInterface()
        {
        }

    }
}
TitleScreen.xaml.cs (15,733 bytes)   
PlatformAny

Activities

stonstad

stonstad

2026-08-20 19:43

reporter   ~0012538

It is strange. I get these XAML not found errors for two files (TitleScreen and SettingsControl). I can't seem to determine what causes the errors to persist versus go away. Restarting Unity and/or reloading the assets via Import does not resolve the behavior.

Second Error:
[NOESIS] Xaml not found 'Assets/User Interface/Screens/Game/Overlay/Settings/SettingsControl.xaml'
UnityEngine.StackTraceUtility:ExtractStackTrace ()
Noesis.GUI:LoadComponent (object,string) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC5/Runtime/API/Core/NoesisGUI.cs:373)
StellarConquest.Presentation.Unity.UI.SettingsControl:InitializeComponent () (at Assets/User Interface/Screens/Game/Overlay/Settings/SettingsControl.xaml.cs:192)
StellarConquest.Presentation.Unity.UI.SettingsControl:.ctor () (at Assets/User Interface/Screens/Game/Overlay/Settings/SettingsControl.xaml.cs:185)
StellarConquest.Presentation.Unity.UI.UIStateMachine:OnInitialized (object,Noesis.EventArgs) (at Assets/User Interface/UIStateMachine.cs:170)
Noesis.EventArgs:InvokeHandler (System.Delegate,intptr,intptr) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC5/Runtime/API/Proxies/EventArgs.cs:55)
Noesis.EventManager:InvokeHandler (uint,System.Delegate,intptr,intptr) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC5/Runtime/API/Core/Events.cs:680)
Noesis.EventHandlerStore:RaiseEvent (intptr,intptr,uint,intptr,intptr) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC5/Runtime/API/Core/Events.cs:406)
Noesis.View:CreateCPtr (System.Type,bool&) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC5/Runtime/API/Core/View.cs:583)
Noesis.BaseComponent:.ctor () (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC5/Runtime/API/Proxies/BaseComponentExtend.cs:18)

SettingsControl.xaml (64,271 bytes)   
<UserControl
    x:Class="StellarConquest.Presentation.Unity.UI.SettingsControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"     
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
    xmlns:local="clr-namespace:StellarConquest.Presentation.Unity.UI">
    <UserControl.Resources>
        <Thickness x:Key="GroupMargin">0, 20, 0, 0</Thickness>
        <Thickness x:Key="ScrollViewerMargin">15, 8, 15, 15</Thickness>
        <SolidColorBrush x:Key="HeadingBrush" Color="#AAFFFFFF"/>
    </UserControl.Resources>
    <Grid x:Name="_LayoutRoot">
        <local:BorderControlTitle x:Name="_BorderControl" HorizontalAlignment="Center" VerticalAlignment="Center" TitleText="Settings" BlurLayer="{DynamicResource OverlayBlurSource}">
            <TabControl x:Name="_TabControl" MinWidth="700" Height="600" Background="#00000000" BorderBrush="#00000000" Margin="30, 20, 25, 20">
                <TabItem>
                    <TabItem.Header>
                        <TextBlock Style="{StaticResource Heading2Primary}" Text="GAMEPLAY" FontSize="20" Margin="5"/>
                    </TabItem.Header>
                    <ScrollViewer>
                        <StackPanel Margin="{StaticResource ScrollViewerMargin}" >

                            <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                        Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Gameplay Assistance"/>

                            <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                <CheckBox x:Name="_GameHelpUIEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                    <CheckBox.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </CheckBox.LayoutTransform>
                                </CheckBox>
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Show Game Help"/>
                            </StackPanel>

                            <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                <CheckBox x:Name="_DesignerHelpUIEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                    <CheckBox.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </CheckBox.LayoutTransform>
                                </CheckBox>
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Show Designer Help"/>
                            </StackPanel>

                            <StackPanel Margin="{StaticResource GroupMargin}" Visibility="Collapsed"/>

                            <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Notifications" Visibility="Collapsed"/>

                            <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12" Visibility="Collapsed">
                                <CheckBox x:Name="_WarnSafeParkingEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                    <CheckBox.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </CheckBox.LayoutTransform>
                                </CheckBox>
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Warn if Parking Unsafe"/>
                            </StackPanel>

                            <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12" Visibility="Collapsed">
                                <CheckBox x:Name="_WarnEvilEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                    <CheckBox.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </CheckBox.LayoutTransform>
                                </CheckBox>
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Warn if Committing Evil"/>
                            </StackPanel>

                            <StackPanel Margin="{StaticResource GroupMargin}" Visibility="Visible"/>

                            <StackPanel x:Name="_GenerativeAIStackPanel">

                                <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Generative AI"/>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                    <CheckBox x:Name="_GenerativeTextDialogue" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                        <CheckBox.LayoutTransform>
                                            <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                        </CheckBox.LayoutTransform>
                                    </CheckBox>
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Generative Text Dialogue"/>
                                </StackPanel>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                    <CheckBox x:Name="_GenerativeAudioDialogue" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                        <CheckBox.LayoutTransform>
                                            <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                        </CheckBox.LayoutTransform>
                                    </CheckBox>
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Generative Audio Dialogue"/>
                                </StackPanel>

                            </StackPanel>

                            <StackPanel Margin="{StaticResource GroupMargin}" Visibility="Visible"/>

                            <StackPanel x:Name="_UnitsStackPanel">

                                <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                    Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Measurement Units"/>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="140" Margin="0, 0, 5, 0" Text="Temperature"/>
                                    <ComboBox x:Name="_TemperatureUnits" Width="210">
                                        <ComboBoxItem>Celsius</ComboBoxItem>
                                        <ComboBoxItem>Fahrenheit</ComboBoxItem>
                                    </ComboBox>
                                </StackPanel>

                            </StackPanel>

                        </StackPanel>
                    </ScrollViewer>
                </TabItem>
                <TabItem>
                    <TabItem.Header>
                        <TextBlock Style="{StaticResource Heading2Primary}" Text="INPUT" FontSize="20" Margin="5"/>
                    </TabItem.Header>
                    <ScrollViewer VerticalScrollBarVisibility="Visible">
                        <StackPanel Margin="{StaticResource ScrollViewerMargin}">

                            <StackPanel x:Name="_DevicesStackPanel">
                                <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Devices"/>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                    <CheckBox x:Name="_GamepadEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                        <CheckBox.LayoutTransform>
                                            <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                        </CheckBox.LayoutTransform>
                                    </CheckBox>
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Gamepad Enabled"/>
                                </StackPanel>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                    <CheckBox x:Name="_MouseConfined" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                        <CheckBox.LayoutTransform>
                                            <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                        </CheckBox.LayoutTransform>
                                    </CheckBox>
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Confine Mouse"/>
                                </StackPanel>

                                <StackPanel Margin="{StaticResource GroupMargin}"/>
                            </StackPanel>

                            <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Character"/>

                            <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="160" Margin="0, 0, 5, 0" Text="Camera Control"/>
                                <ComboBox x:Name="_CharacterControl" Width="210">
                                    <ComboBoxItem>Default</ComboBoxItem>
                                    <ComboBoxItem>Pivot</ComboBoxItem>
                                    <ComboBoxItem>Cinematic</ComboBoxItem>
                                </ComboBox>
                            </StackPanel>

                            <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="160" Margin="0, 0, 5, 0" Text="Interior Camera"/>
                                <ComboBox x:Name="_InteriorCamera" Width="210">
                                    <ComboBoxItem>Over the Shoulder</ComboBoxItem>
                                    <ComboBoxItem>First Person</ComboBoxItem>
                                </ComboBox>
                            </StackPanel>

                            <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                <CheckBox x:Name="_CharacterInvertUpEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                    <CheckBox.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </CheckBox.LayoutTransform>
                                </CheckBox>
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Character Invert Up"/>
                            </StackPanel>

                            <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Text="Look Sensitivity"/>
                                <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                    <StackPanel.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </StackPanel.LayoutTransform>
                                    <Slider x:Name="_CharacterLookSensitivity" Minimum="0.2" Maximum="1.8" IsSnapToTickEnabled="False" IsMoveToPointEnabled="True" TickFrequency="0.2" TickPlacement="BottomRight" Width="420" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                </StackPanel>
                            </StackPanel>
                            <StackPanel Margin="{StaticResource GroupMargin}"/>

                            <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Starship"/>

                            <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                <CheckBox x:Name="_ShipInvertUpEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                    <CheckBox.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </CheckBox.LayoutTransform>
                                </CheckBox>
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Starship Invert Up"/>
                            </StackPanel>
                            <StackPanel Margin="{StaticResource GroupMargin}"/>

                        </StackPanel>

                    </ScrollViewer>
                </TabItem>
                <TabItem>
                    <TabItem.Header>
                        <TextBlock Style="{StaticResource Heading2Primary}" Text="DISPLAY" FontSize="20" Margin="5"/>
                    </TabItem.Header>
                    <ScrollViewer>
                        <StackPanel Orientation="Vertical" Margin="{StaticResource ScrollViewerMargin}">

                            <StackPanel x:Name="_ResolutionStackPanel">

                                <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Resolution"/>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="160" Margin="0, 0, 5, 0" Text="Display"/>
                                    <ComboBox x:Name="_Displays" Width="220"/>
                                </StackPanel>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="160" Margin="0, 0, 5, 0" Text="Resolution"/>
                                    <ComboBox x:Name="_WindowMode" Width="220">
                                        <ComboBoxItem>Fullscreen Exclusive</ComboBoxItem>
                                        <ComboBoxItem>Fullscreen Windowed</ComboBoxItem>
                                        <ComboBoxItem Visibility="Collapsed">Windowed Maximized</ComboBoxItem>
                                        <!-- MacOS -->
                                        <ComboBoxItem>Windowed</ComboBoxItem>
                                    </ComboBox>
                                    <StackPanel x:Name="_DisplayResolutionStackPanel" Width="220" Margin="15, 0, 0, 0">
                                        <ComboBox x:Name="_DisplayResolution"/>
                                    </StackPanel>
                                </StackPanel>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="160" Margin="0, 0, 5, 0" Text="Upscaling"/>
                                    <ComboBox x:Name="_ResolutionUpscaler" Width="220">
                                        <ComboBoxItem>Off</ComboBoxItem>
                                        <ComboBoxItem>DLSS</ComboBoxItem>
                                        <ComboBoxItem>FSR 3</ComboBoxItem>
                                        <ComboBoxItem>XeSS</ComboBoxItem>
                                    </ComboBox>

                                    <StackPanel x:Name="_UpscalerQualityStackPanel" Width="220" Margin="15, 0, 0, 0">
                                        <ComboBox x:Name="_ResolutionUpscalerQuality" Width="220">
                                            <ComboBoxItem>Ultra Quality</ComboBoxItem>
                                            <ComboBoxItem>Quality</ComboBoxItem>
                                            <ComboBoxItem>Balanced</ComboBoxItem>
                                            <ComboBoxItem>Performance</ComboBoxItem>
                                            <ComboBoxItem>Ultra Performance</ComboBoxItem>
                                        </ComboBox>
                                    </StackPanel>
                                </StackPanel>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="160" Margin="0, 0, 5, 0" Text="Frame Rate Limit"/>
                                    <ComboBox x:Name="_VSync" Width="220">
                                        <ComboBoxItem>Monitor vSync Off</ComboBoxItem>
                                        <ComboBoxItem>Monitor vSync On</ComboBoxItem>
                                    </ComboBox>

                                    <ComboBox x:Name="_FrameRateLimit" Width="220" Margin="15, 0, 0, 0">
                                        <ComboBoxItem>Unlimited</ComboBoxItem>
                                        <ComboBoxItem>60 FPS</ComboBoxItem>
                                        <ComboBoxItem>120 FPS</ComboBoxItem>
                                    </ComboBox>
                                </StackPanel>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12" Visibility="Collapsed">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="160" Margin="0, 0, 5, 0" Text="Motion Blur"/>
                                    <ComboBox x:Name="_MotionBlur" Width="80">
                                        <ComboBoxItem>Off</ComboBoxItem>
                                        <ComboBoxItem>On</ComboBoxItem>
                                    </ComboBox>
                                </StackPanel>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="160" Margin="0, 0, 5, 0" Text="Show FPS"/>
                                    <ComboBox x:Name="_ShowFPS" Width="80">
                                        <ComboBoxItem>Off</ComboBoxItem>
                                        <ComboBoxItem>On</ComboBoxItem>
                                    </ComboBox>
                                </StackPanel>

                                <StackPanel Margin="{StaticResource GroupMargin}"/>
                            </StackPanel>

                            <StackPanel x:Name="_QualityStackPanel">
                                <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Quality"/>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12" VerticalAlignment="Center">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="160" Margin="0, 0, 5, 0" Text="Preset"/>
                                    <ToggleButton x:Name="_QualityLow" Content="Low"/>
                                    <ToggleButton x:Name="_QualityMedium" Content="Medium"/>
                                    <ToggleButton x:Name="_QualityHigh" Content="High"/>
                                    <ToggleButton x:Name="_QualityEpic" Content="Epic"/>
                                    <ToggleButton x:Name="_QualityAuto" Content="Auto" Visibility="Collapsed"/>
                                </StackPanel>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12" VerticalAlignment="Center">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="145" Margin="15, 0, 5, 0" Text="View Distance"/>
                                    <ToggleButton x:Name="_ViewNear" Content="Near"/>
                                    <ToggleButton x:Name="_ViewMedium" Content="Medium"/>
                                    <ToggleButton x:Name="_ViewFar" Content="Far"/>
                                    <ToggleButton x:Name="_ViewEpic" Content="Epic"/>
                                </StackPanel>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12" VerticalAlignment="Center">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="145" Margin="15, 0, 5, 0" Text="Shadow Quality"/>
                                    <ToggleButton x:Name="_ShadowsLow" Content="Low"/>
                                    <ToggleButton x:Name="_ShadowsMedium" Content="Medium"/>
                                    <ToggleButton x:Name="_ShadowsHigh" Content="High"/>
                                    <ToggleButton x:Name="_ShadowsEpic" Content="Epic"/>
                                </StackPanel>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12" VerticalAlignment="Center">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="145" Margin="15, 0, 5, 0" Text="Shadow Distance"/>
                                    <ToggleButton x:Name="_ShadowDistanceNear" Content="Near"/>
                                    <ToggleButton x:Name="_ShadowDistanceMedium" Content="Medium"/>
                                    <ToggleButton x:Name="_ShadowDistanceFar" Content="Far"/>
                                    <ToggleButton x:Name="_ShadowDistanceEpic" Content="Epic"/>
                                </StackPanel>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12" VerticalAlignment="Center">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="145" Margin="15, 0, 5, 0" Text="Textures"/>
                                    <ToggleButton x:Name="_TexturesLow" Content="Low"/>
                                    <ToggleButton x:Name="_TexturesMedium" Content="Medium"/>
                                    <ToggleButton x:Name="_TexturesHigh" Content="High"/>
                                    <ToggleButton x:Name="_TexturesEpic" Content="Epic"/>
                                </StackPanel>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12" VerticalAlignment="Center">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="145" Margin="15, 0, 5, 0" Text="Reflections"/>
                                    <ToggleButton x:Name="_ReflectionsOff" Content="Off"/>
                                    <ToggleButton x:Name="_ReflectionsMedium" Content="Medium"/>
                                    <ToggleButton x:Name="_ReflectionsHigh" Content="High"/>
                                    <ToggleButton x:Name="_ReflectionsEpic" Content="Epic"/>
                                </StackPanel>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12" VerticalAlignment="Center">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="145" Margin="15, 0, 5, 0" Text="Detail"/>
                                    <ToggleButton x:Name="_DetailLow" Content="Low"/>
                                    <ToggleButton x:Name="_DetailMedium" Content="Medium"/>
                                    <ToggleButton x:Name="_DetailHigh" Content="High"/>
                                    <ToggleButton x:Name="_DetailEpic" Content="Epic"/>
                                </StackPanel>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12" VerticalAlignment="Center">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="145" Margin="15, 0, 5, 0" Text="Post Processing"/>
                                    <ToggleButton x:Name="_PostProcessingLow" Content="Low"/>
                                    <ToggleButton x:Name="_PostProcessingMedium" Content="Medium"/>
                                    <ToggleButton x:Name="_PostProcessingHigh" Content="High"/>
                                    <ToggleButton x:Name="_PostProcessingEpic" Content="Epic"/>
                                </StackPanel>

                                <StackPanel Margin="{StaticResource GroupMargin}"/>
                            </StackPanel>

                            <StackPanel x:Name="_FieldOfViewStackPanel">

                                <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Field of View"/>

                                <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Text="Character"/>
                                    <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                        <StackPanel.LayoutTransform>
                                            <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                        </StackPanel.LayoutTransform>
                                        <Slider x:Name="_CharacterFieldOfView" Minimum="40" Maximum="120" IsSnapToTickEnabled="False" IsMoveToPointEnabled="True" TickFrequency="10" TickPlacement="BottomRight" Width="420" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                    </StackPanel>
                                </StackPanel>

                                <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Text="Starship Cockpit"/>
                                    <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                        <StackPanel.LayoutTransform>
                                            <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                        </StackPanel.LayoutTransform>
                                        <Slider x:Name="_CockpitFieldOfView" Minimum="40" Maximum="120" IsSnapToTickEnabled="False" IsMoveToPointEnabled="True" TickFrequency="10" TickPlacement="BottomRight" Width="420" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                    </StackPanel>
                                </StackPanel>

                                <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Text="Starship External"/>
                                    <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                        <StackPanel.LayoutTransform>
                                            <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                        </StackPanel.LayoutTransform>
                                        <Slider x:Name="_ExternalFieldOfView" Minimum="40" Maximum="120" IsSnapToTickEnabled="False" IsMoveToPointEnabled="True" TickFrequency="10" TickPlacement="BottomRight" Width="420" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                    </StackPanel>
                                </StackPanel>

                                <StackPanel Orientation="Vertical" Margin="5, 0, 0, 12">
                                    <Button x:Name="_ResetFieldOfViewButton" HorizontalAlignment="Left">
                                        <TextBlock Style="{StaticResource Heading3Primary}">Reset</TextBlock>
                                    </Button>
                                </StackPanel>
                                <StackPanel Margin="{StaticResource GroupMargin}"/>
                            </StackPanel>

                            <StackPanel x:Name="_ColorCorrectionStackPanel">

                                <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                    Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Color Correction"/>

                                <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Text="Brightness"/>
                                    <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                        <StackPanel.LayoutTransform>
                                            <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                        </StackPanel.LayoutTransform>
                                        <Slider x:Name="_BrightnessSlider" Minimum="-50" Maximum="50" IsSnapToTickEnabled="False" IsMoveToPointEnabled="True" TickFrequency="20" TickPlacement="BottomRight" Width="420" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                    </StackPanel>
                                </StackPanel>

                                <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Text="Contrast"/>
                                    <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                        <StackPanel.LayoutTransform>
                                            <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                        </StackPanel.LayoutTransform>
                                        <Slider x:Name="_ContrastSlider" Minimum="-50" Maximum="50" IsSnapToTickEnabled="False" IsMoveToPointEnabled="True" TickFrequency="20" TickPlacement="BottomRight" Width="420" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                    </StackPanel>
                                </StackPanel>

                                <StackPanel Orientation="Vertical" Margin="5, 0, 0, 12">
                                    <Button x:Name="_ResetColorCorrectionButton" HorizontalAlignment="Left">
                                        <TextBlock Style="{StaticResource Heading3Primary}">Reset</TextBlock>
                                    </Button>
                                </StackPanel>
                            </StackPanel>
                        </StackPanel>
                    </ScrollViewer>
                </TabItem>
                <TabItem>
                    <TabItem.Header>
                        <TextBlock Style="{StaticResource Heading2Primary}" Text="UI" FontSize="20" Margin="5"/>
                    </TabItem.Header>
                    <ScrollViewer>
                        <StackPanel Orientation="Vertical" Margin="{StaticResource ScrollViewerMargin}">

                            <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Configuration"/>

                            <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                <CheckBox x:Name="_UIEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                    <CheckBox.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </CheckBox.LayoutTransform>
                                </CheckBox>
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Show User Interface"/>
                            </StackPanel>

                            <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                <CheckBox x:Name="_GameMenuEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                    <CheckBox.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </CheckBox.LayoutTransform>
                                </CheckBox>
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Show Game Menu"/>
                            </StackPanel>

                            <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                <CheckBox x:Name="_WindowDragEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                    <CheckBox.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </CheckBox.LayoutTransform>
                                </CheckBox>
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Allow Window Dragging"/>
                            </StackPanel>

                            <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                <CheckBox x:Name="_TargetingEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                    <CheckBox.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </CheckBox.LayoutTransform>
                                </CheckBox>
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Show Targeting"/>
                            </StackPanel>

                            <StackPanel Margin="{StaticResource GroupMargin}"/>

                            <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Scale"/>

                            <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12" VerticalAlignment="Center">
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="160" Margin="0, 0, 5, 0" Text="User Interface"/>
                                <ToggleButton x:Name="_UISmallest" Content="Smallest"/>
                                <ToggleButton x:Name="_UISmaller" Content="Smaller"/>
                                <ToggleButton x:Name="_UINormal" Content="Normal"/>
                                <ToggleButton x:Name="_UILarger" Content="Larger"/>
                                <ToggleButton x:Name="_UILargest" Content="Largest"/>
                            </StackPanel>

                            <StackPanel Margin="{StaticResource GroupMargin}"/>
                            <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Effects"/>

                            <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Text="Blur Effects"/>
                                <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                    <StackPanel.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </StackPanel.LayoutTransform>
                                    <CheckBox x:Name="_BlurEffectsEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center"/>
                                </StackPanel>
                            </StackPanel>

                            <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Text="Top Blur"/>
                                <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                    <StackPanel.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </StackPanel.LayoutTransform>
                                    <Slider x:Name="_TopBlurSlider" Minimum="0" Maximum="100" IsSnapToTickEnabled="False" IsMoveToPointEnabled="True" TickFrequency="20" TickPlacement="BottomRight" Width="420" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                </StackPanel>
                            </StackPanel>

                            <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Text="Bottom Blur"/>
                                <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                    <StackPanel.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </StackPanel.LayoutTransform>
                                    <Slider x:Name="_BottomBlurSlider" Minimum="0" Maximum="100" IsSnapToTickEnabled="False" IsMoveToPointEnabled="True" TickFrequency="20" TickPlacement="BottomRight" Width="420" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                </StackPanel>
                            </StackPanel>

                        </StackPanel>
                    </ScrollViewer>
                </TabItem>
                <TabItem>
                    <TabItem.Header>
                        <TextBlock Style="{StaticResource Heading2Primary}" Text="AUDIO" FontSize="20" Margin="5"/>
                    </TabItem.Header>
                    <ScrollViewer>
                        <StackPanel Orientation="Vertical" Margin="{StaticResource ScrollViewerMargin}">
                            <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Master"/>

                            <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Text="Volume"/>
                                <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                    <StackPanel.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </StackPanel.LayoutTransform>
                                    <CheckBox x:Name="_MasterVolumeEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center" Width="35"/>
                                    <Slider x:Name="_MasterVolumeSlider" Minimum="0" Maximum="100" IsSnapToTickEnabled="False" IsMoveToPointEnabled="True" TickFrequency="25" TickPlacement="BottomRight" Width="380" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                </StackPanel>
                            </StackPanel>

                            <StackPanel Margin="{StaticResource GroupMargin}"/>
                            <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Mixer"/>

                            <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Text="Music"/>
                                <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                    <StackPanel.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </StackPanel.LayoutTransform>
                                    <CheckBox x:Name="_MusicVolumeEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center" Width="35"/>
                                    <Slider x:Name="_MusicVolumeSlider" Minimum="0" Maximum="100" IsSnapToTickEnabled="False" IsMoveToPointEnabled="True" TickFrequency="25" TickPlacement="BottomRight" Width="380" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                </StackPanel>
                            </StackPanel>

                            <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Text="Effects"/>
                                <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                    <StackPanel.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </StackPanel.LayoutTransform>
                                    <CheckBox x:Name="_EffectsVolumeEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center" Width="35"/>
                                    <Slider x:Name="_EffectsVolumeSlider" Minimum="0" Maximum="100" IsSnapToTickEnabled="False" IsMoveToPointEnabled="True" TickFrequency="25" TickPlacement="BottomRight" Width="380" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                </StackPanel>
                            </StackPanel>

                            <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Text="Voice"/>
                                <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                    <StackPanel.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </StackPanel.LayoutTransform>
                                    <CheckBox x:Name="_VoiceVolumeEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center" Width="35"/>
                                    <Slider x:Name="_VoiceVolumeSlider" Minimum="0" Maximum="100" IsSnapToTickEnabled="False" IsMoveToPointEnabled="True" TickFrequency="25" TickPlacement="BottomRight" Width="380" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                </StackPanel>
                            </StackPanel>

                            <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Text="User Interface"/>
                                <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                    <StackPanel.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </StackPanel.LayoutTransform>
                                    <CheckBox x:Name="_UIVolumeEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center" Width="35"/>
                                    <Slider x:Name="_UIVolumeSlider" Minimum="0" Maximum="100" IsSnapToTickEnabled="False" IsMoveToPointEnabled="True" TickFrequency="25" TickPlacement="BottomRight" Width="380" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                </StackPanel>
                            </StackPanel>

                            <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Text="Ambient"/>
                                <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                    <StackPanel.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </StackPanel.LayoutTransform>

                                    <CheckBox x:Name="_AmbientVolumeEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center" Width="35"/>
                                    <Slider x:Name="_AmbientVolumeSlider" Minimum="0" Maximum="100" IsSnapToTickEnabled="False" IsMoveToPointEnabled="True" TickFrequency="25" TickPlacement="BottomRight" Width="380" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                </StackPanel>
                            </StackPanel>

                        </StackPanel>
                    </ScrollViewer>
                </TabItem>
                <TabItem x:Name="_NetworkTab">
                    <TabItem.Header>
                        <TextBlock Style="{StaticResource Heading2Primary}" Text="NETWORK" FontSize="20" Margin="5"/>
                    </TabItem.Header>
                    <ScrollViewer>
                        <StackPanel Orientation="Vertical" Margin="{StaticResource ScrollViewerMargin}">

                            <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Environment"/>

                            <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Right" VerticalAlignment="Center" Width="160" Margin="0, 0, 5, 0" Text="Conquest Server"/>
                                <ComboBox x:Name="_Environment">
                                    <ComboBoxItem>Developer</ComboBoxItem>
                                    <ComboBoxItem>Preview</ComboBoxItem>
                                    <ComboBoxItem>Release</ComboBoxItem>
                                </ComboBox>
                            </StackPanel>

                            <StackPanel Margin="{StaticResource GroupMargin}"/>
                            <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Update Rate"/>

                            <StackPanel>
                                <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                    <TextBlock x:Name="_ClientToServerRateTextBlock" Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Margin="0, 0, 0, 2" Text="Client"/>
                                    <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                        <StackPanel.LayoutTransform>
                                            <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                        </StackPanel.LayoutTransform>
                                        <Slider x:Name="_ClientToServerSlider" Minimum="0.0" Maximum="1.0" IsSnapToTickEnabled="true" IsMoveToPointEnabled="True" TickFrequency="0.05" TickPlacement="BottomRight" Width="420" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                    </StackPanel>
                                </StackPanel>

                                <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                    <TextBlock x:Name="_ServerToClientRateTextBlock" Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Margin="0, 0, 0, 2" Text="Server"/>
                                    <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                        <StackPanel.LayoutTransform>
                                            <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                        </StackPanel.LayoutTransform>
                                        <Slider x:Name="_ServerToClientSlider" Minimum="0.0" Maximum="1.0" IsSnapToTickEnabled="true" IsMoveToPointEnabled="True" TickFrequency="0.05" TickPlacement="BottomRight" Width="420" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                    </StackPanel>
                                </StackPanel>
                            </StackPanel>

                        </StackPanel>
                    </ScrollViewer>
                </TabItem>
                <TabItem x:Name="_CorsairTab">
                    <TabItem.Header>
                        <TextBlock Style="{StaticResource Heading2Primary}" Text="CORSAIR" FontSize="20" Margin="5"/>
                    </TabItem.Header>
                    <ScrollViewer>
                        <StackPanel Orientation="Vertical" Margin="{StaticResource ScrollViewerMargin}">

                            <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Corsair Lighting"/>

                            <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                <CheckBox x:Name="_RGBLightingEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                    <CheckBox.LayoutTransform>
                                        <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                    </CheckBox.LayoutTransform>
                                </CheckBox>
                                <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="RGB Lighting Enabled"/>
                            </StackPanel>

                            <TextBlock x:Name="_CorsairNotFoundTextBlock" Margin="0, 16, 0, 0" Style="{StaticResource Heading3Primary}"
                                       Visibility="Collapsed" HorizontalAlignment="Left" Width="625" TextWrapping="Wrap">
                                Corsair devices could not be detected. Please ensure that iCUE is running and that both iCUE and Game SDK control is enabled.
                            </TextBlock>

                            <StackPanel x:Name="_RGBStackPanel">
                                <StackPanel Margin="{StaticResource GroupMargin}"/>
                                <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Keyboard"/>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                    <CheckBox x:Name="_TutorialKeysEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                        <CheckBox.LayoutTransform>
                                            <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                        </CheckBox.LayoutTransform>
                                    </CheckBox>
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Show Tutorial Keys"/>
                                </StackPanel>

                                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 12">
                                    <CheckBox x:Name="_InputKeysEnabled" Margin="0, 0, 5, 0" VerticalAlignment="Center">
                                        <CheckBox.LayoutTransform>
                                            <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                        </CheckBox.LayoutTransform>
                                    </CheckBox>
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Show Input Keys"/>
                                </StackPanel>

                                <StackPanel Margin="{StaticResource GroupMargin}"/>
                                <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Assignment"/>
                                <StackPanel x:Name="_CorsairStackPanel" Orientation="Vertical" Margin="0, 0, 0, 0"/>

                                <StackPanel Margin="{StaticResource GroupMargin}"/>
                                <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                                Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Color Correction"/>

                                <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Margin="0, 0, 0, 2" Text="Hue"/>
                                    <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                        <StackPanel.LayoutTransform>
                                            <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                        </StackPanel.LayoutTransform>
                                        <Slider x:Name="_RGBHueSlider" Minimum="-180" Maximum="180" IsSnapToTickEnabled="false" IsMoveToPointEnabled="True" TickFrequency="20" TickPlacement="BottomRight" Width="420" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                    </StackPanel>
                                </StackPanel>

                                <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Margin="0, 0, 0, 2" Text="Saturation"/>
                                    <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                        <StackPanel.LayoutTransform>
                                            <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                        </StackPanel.LayoutTransform>
                                        <Slider x:Name="_RGBSaturationSlider" Minimum="-100" Maximum="100" IsSnapToTickEnabled="false" IsMoveToPointEnabled="True" TickFrequency="20" TickPlacement="BottomRight" Width="420" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                    </StackPanel>
                                </StackPanel>

                                <StackPanel Orientation="Vertical" Margin="0, 0, 0, 12">
                                    <TextBlock Style="{StaticResource Heading4Primary}" HorizontalAlignment="Left" Margin="0, 0, 0, 2" Text="Brightness"/>
                                    <StackPanel Orientation="Horizontal" Margin="0, 0, 10, 0">
                                        <StackPanel.LayoutTransform>
                                            <ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
                                        </StackPanel.LayoutTransform>
                                        <Slider x:Name="_RGBBrightnessSlider" Minimum="-100" Maximum="100" IsSnapToTickEnabled="false" IsMoveToPointEnabled="True" TickFrequency="20" TickPlacement="BottomRight" Width="420" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0, 0, 0, 0"/>
                                    </StackPanel>
                                </StackPanel>

                                <StackPanel Orientation="Vertical" Margin="5, 0, 0, 12">
                                    <Button x:Name="_ResetCorsairColorCorrectionButton" HorizontalAlignment="Left">
                                        <TextBlock Style="{StaticResource Heading3Primary}">Reset</TextBlock>
                                    </Button>
                                </StackPanel>

                            </StackPanel>

                        </StackPanel>
                    </ScrollViewer>
                </TabItem>
                <TabItem x:Name="AboutTab">
                    <TabItem.Header>
                        <TextBlock Style="{StaticResource Heading2Primary}" Text="ABOUT" FontSize="20" Margin="5"/>
                    </TabItem.Header>
                    <StackPanel Orientation="Vertical" Margin="{StaticResource ScrollViewerMargin}">
                        <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                            Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Version"/>

                        <TextBlock x:Name="_VersionTextBlock" Margin="0, 0, 0, 10" Style="{StaticResource Heading3Primary}"/>

                        <StackPanel Margin="{StaticResource GroupMargin}"/>

                        <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                            Foreground="Black" Background="{StaticResource HeadingBrush}" Text="AI Notice"/>

                        <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" Width="625" TextWrapping="Wrap">
                            <Run>Stellar Conquest</Run>
                            <Run FontWeight="Bold">does not</Run>
                            <Run>use AI for art and music content. If enabled, generative AI is used to create NPC memories and dialogue.</Run>
                        </TextBlock>

                        <StackPanel Margin="{StaticResource GroupMargin}"/>

                        <TextBlock Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="3" Margin="0, 0, 0, 12" MinWidth="625"
                            Foreground="Black" Background="{StaticResource HeadingBrush}" Text="Legal Notice"/>

                        <TextBlock Style="{StaticResource Heading3Primary}">
                            <Run FontWeight="Bold">Stellar Conquest</Run>
                            <Run>&#xA9; 2026 Stellar Studio</Run></TextBlock>

                        <TextBlock Margin="0, 16, 0, 0" Style="{StaticResource Heading3Primary}" HorizontalAlignment="Left" Width="625" TextWrapping="Wrap">
                            <Run>All trademarked names and copyrights are protected by law. The Stellar Conquest&#x00AE;</Run>
                            <Run>name and the Stellar Conquest Logo are registered trademarks and/or service brand names, slogans, logos and emblems of Shaun C Tonstad. All rights reserved.</Run></TextBlock>

                        <TextBlock Margin="0, 16, 0, 0">
                            <Hyperlink x:Name="_HyperLinkButton" FontSize="{StaticResource Heading3FontSize}">Third-Party Notices</Hyperlink>
                        </TextBlock>

                    </StackPanel>
                </TabItem>
            </TabControl>
        </local:BorderControlTitle>
    </Grid>
</UserControl>
SettingsControl.xaml (64,271 bytes)   
SettingsControl.xaml.cs (69,999 bytes)   
using Noesis;
using StellarConquest.Model.Core;
using StellarConquest.Model.Platform;
using StellarConquest.RGB;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
using GUI = Noesis.GUI;
using NoesisEventArgs = Noesis.EventArgs;

namespace StellarConquest.Presentation.Unity.UI
{
    [AssetDependency]
    public partial class SettingsControl : OverlayControl
    {
        private FrameworkElement _Root;

        private BorderControlTitle _BorderControl;
        private TabControl _TabControl;

        // gameplay
        private CheckBox _WarnSafeParkingEnabled;
        private CheckBox _WarnEvilEnabled;
        private CheckBox _GameMenuEnabled;
        private CheckBox _TargetingEnabled;
        private CheckBox _WindowDragEnabled;
        private CheckBox _UIEnabled;

        private ComboBox _TemperatureUnits;

        // control
        private StackPanel _DevicesStackPanel;
        private CheckBox _MouseConfined;
        private CheckBox _GamepadEnabled;
        private TextBlock _CockpitHeadingTextBlock;
        private CheckBox _CharacterInvertUpEnabled;
        private Slider _CharacterLookSensitivity;
        private CheckBox _ShipInvertUpEnabled;
        private ComboBox _CharacterControl;
        private ComboBox _InteriorCamera;

        // display
        private bool _DisplayInitialized = false;

        private StackPanel _ResolutionStackPanel;
        private ComboBox _WindowMode;
        private StackPanel _DisplayResolutionStackPanel;
        private ComboBox _Displays;
        private ComboBox _DisplayResolution;
        private ComboBox _ResolutionUpscaler;
        private ComboBox _ResolutionUpscalerQuality;
        private ComboBox _FrameRateLimit;
        private ComboBox _VSync;
        private ComboBox _MotionBlur;
        private ComboBox _ShowFPS;

        private StackPanel _QualityStackPanel;
        private ToggleButton _QualityLow;
        private ToggleButton _QualityMedium;
        private ToggleButton _QualityHigh;
        private ToggleButton _QualityEpic;
        private ToggleButton _QualityAuto;

        private ToggleButton _ViewNear;
        private ToggleButton _ViewMedium;
        private ToggleButton _ViewFar;
        private ToggleButton _ViewEpic;

        private ToggleButton _ShadowsLow;
        private ToggleButton _ShadowsMedium;
        private ToggleButton _ShadowsHigh;
        private ToggleButton _ShadowsEpic;

        private ToggleButton _ShadowDistanceNear;
        private ToggleButton _ShadowDistanceMedium;
        private ToggleButton _ShadowDistanceFar;
        private ToggleButton _ShadowDistanceEpic;

        private ToggleButton _TexturesLow;
        private ToggleButton _TexturesMedium;
        private ToggleButton _TexturesHigh;
        private ToggleButton _TexturesEpic;

        private ToggleButton _ReflectionsOff;
        private ToggleButton _ReflectionsMedium;
        private ToggleButton _ReflectionsHigh;
        private ToggleButton _ReflectionsEpic;

        private ToggleButton _PostProcessingLow;
        private ToggleButton _PostProcessingMedium;
        private ToggleButton _PostProcessingHigh;
        private ToggleButton _PostProcessingEpic;

        private ToggleButton _DetailLow;
        private ToggleButton _DetailMedium;
        private ToggleButton _DetailHigh;
        private ToggleButton _DetailEpic;

        private StackPanel _FieldOfViewStackPanel;
        private Slider _CharacterFieldOfView;
        private Slider _CockpitFieldOfView;
        private Slider _ExternalFieldOfView;
        private Button _ResetFieldOfViewButton;

        private Slider _BrightnessSlider;
        private Slider _ContrastSlider;
        private Button _ResetColorCorrectionButton;

        // ui
        private ToggleButton _UISmallest;
        private ToggleButton _UISmaller;
        private ToggleButton _UINormal;
        private ToggleButton _UILarger;
        private ToggleButton _UILargest;

        private CheckBox _BlurEffectsEnabled;
        private Slider _TopBlurSlider;
        private Slider _BottomBlurSlider;

        private CheckBox _DesignerHelpUIEnabled;
        private CheckBox _GameHelpUIEnabled;

        // sound
        private CheckBox _MasterVolumeEnabled;
        private Slider _MasterVolumeSlider;

        private CheckBox _MusicVolumeEnabled;
        private Slider _MusicVolumeSlider;

        private CheckBox _EffectsVolumeEnabled;
        private Slider _EffectsVolumeSlider;

        private CheckBox _VoiceVolumeEnabled;
        private Slider _VoiceVolumeSlider;

        private CheckBox _UIVolumeEnabled;
        private Slider _UIVolumeSlider;

        private CheckBox _AmbientVolumeEnabled;
        private Slider _AmbientVolumeSlider;

        // network
        private TabItem _NetworkTab;
        private ComboBox _Environment;
        private Slider _ClientToServerSlider;
        private Slider _ServerToClientSlider;
        private TextBlock _ClientToServerRateTextBlock;
        private TextBlock _ServerToClientRateTextBlock;

        // generative AI
        private StackPanel _GenerativeAIStackPanel;
        private CheckBox _GenerativeTextDialogue;
        private CheckBox _GenerativeAudioDialogue;

        // corsair
        private TabItem _CorsairTab;
        private StackPanel _RGBStackPanel;
        private CheckBox _RGBLightingEnabled;
        private CheckBox _TutorialKeysEnabled;
        private CheckBox _InputKeysEnabled;
        private Slider _RGBHueSlider;
        private Slider _RGBSaturationSlider;
        private Slider _RGBBrightnessSlider;
        private Button _ResetCorsairColorCorrectionButton;
        private StackPanel _CorsairStackPanel;
        private TextBlock _CorsairNotFoundTextBlock;

        // about
        private Hyperlink _HyperlinkButton;
        private TextBlock _VersionTextBlock;

        private bool _ApplyEnabled = true;
        private List<Resolution> _SupportedResolutions;

        public SettingsControl()
        {
            Name = nameof(SettingsControl);

            IsBlurEnabled = false;
            IsDragAllowed = true;

            Initialized += OnInitialized;
            InitializeComponent();
        }

        protected override void InitializeComponent()
        {
            base.InitializeComponent();

            GUI.LoadComponent(this, "Assets/User Interface/Screens/Game/Overlay/Settings/SettingsControl.xaml");
            _Root = Content as FrameworkElement;

            _TabControl = _Root.FindName("_TabControl") as TabControl;
            _BorderControl = _Root.FindName("_BorderControl") as BorderControlTitle;

            // bind controls: gameplay
            _WarnSafeParkingEnabled = _Root.FindName("_WarnSafeParkingEnabled") as CheckBox;
            _WarnEvilEnabled = _Root.FindName("_WarnEvilEnabled") as CheckBox;
            _GameMenuEnabled = _Root.FindName("_GameMenuEnabled") as CheckBox;
            _TargetingEnabled = _Root.FindName("_TargetingEnabled") as CheckBox;
            _WindowDragEnabled = _Root.FindName("_WindowDragEnabled") as CheckBox;
            _UIEnabled = _Root.FindName("_UIEnabled") as CheckBox;

            _TemperatureUnits = _Root.FindName(nameof(_TemperatureUnits)) as ComboBox;

            // bind controls: input
            _DevicesStackPanel = _Root.FindName(nameof(_DevicesStackPanel)) as StackPanel;
            _MouseConfined = _Root.FindName("_MouseConfined") as CheckBox;
            _GamepadEnabled = _Root.FindName("_GamepadEnabled") as CheckBox;
            _CockpitHeadingTextBlock = _Root.FindName("_CockpitHeadingTextBlock") as TextBlock;
            _CharacterInvertUpEnabled = _Root.FindName("_CharacterInvertUpEnabled") as CheckBox;
            _ShipInvertUpEnabled = _Root.FindName("_ShipInvertUpEnabled") as CheckBox;
            _CharacterControl = _Root.FindName("_CharacterControl") as ComboBox;
            _InteriorCamera = _Root.FindName("_InteriorCamera") as ComboBox;
            _CharacterLookSensitivity = _Root.FindName("_CharacterLookSensitivity") as Slider;

            // bind controls: video
            _ResolutionStackPanel = _Root.FindName(nameof(_ResolutionStackPanel)) as StackPanel;
            _WindowMode = _Root.FindName("_WindowMode") as ComboBox;
            _DisplayResolutionStackPanel = _Root.FindName("_DisplayResolutionStackPanel") as StackPanel;
            _Displays = _Root.FindName("_Displays") as ComboBox;
            _DisplayResolution = _Root.FindName("_DisplayResolution") as ComboBox;
            _ResolutionUpscaler = _Root.FindName(nameof(_ResolutionUpscaler)) as ComboBox;
            _ResolutionUpscalerQuality = _Root.FindName(nameof(_ResolutionUpscalerQuality)) as ComboBox;
            _FrameRateLimit = _Root.FindName("_FrameRateLimit") as ComboBox;
            _VSync = _Root.FindName("_VSync") as ComboBox;
            _MotionBlur = _Root.FindName("_MotionBlur") as ComboBox;
            _ShowFPS = _Root.FindName("_ShowFPS") as ComboBox;

            _QualityStackPanel = _Root.FindName(nameof(_QualityStackPanel)) as StackPanel;
            _QualityLow = _Root.FindName("_QualityLow") as ToggleButton;
            _QualityMedium = _Root.FindName("_QualityMedium") as ToggleButton;
            _QualityHigh = _Root.FindName("_QualityHigh") as ToggleButton;
            _QualityEpic = _Root.FindName("_QualityEpic") as ToggleButton;
            _QualityAuto = _Root.FindName("_QualityAuto") as ToggleButton;

            _ViewNear = _Root.FindName("_ViewNear") as ToggleButton;
            _ViewMedium = _Root.FindName("_ViewMedium") as ToggleButton;
            _ViewFar = _Root.FindName("_ViewFar") as ToggleButton;
            _ViewEpic = _Root.FindName("_ViewEpic") as ToggleButton;

            _ShadowsLow = _Root.FindName("_ShadowsLow") as ToggleButton;
            _ShadowsMedium = _Root.FindName("_ShadowsMedium") as ToggleButton;
            _ShadowsHigh = _Root.FindName("_ShadowsHigh") as ToggleButton;
            _ShadowsEpic = _Root.FindName("_ShadowsEpic") as ToggleButton;

            _ShadowDistanceNear = _Root.FindName("_ShadowDistanceNear") as ToggleButton;
            _ShadowDistanceMedium = _Root.FindName("_ShadowDistanceMedium") as ToggleButton;
            _ShadowDistanceFar = _Root.FindName("_ShadowDistanceFar") as ToggleButton;
            _ShadowDistanceEpic = _Root.FindName("_ShadowDistanceEpic") as ToggleButton;

            _TexturesLow = _Root.FindName("_TexturesLow") as ToggleButton;
            _TexturesMedium = _Root.FindName("_TexturesMedium") as ToggleButton;
            _TexturesHigh = _Root.FindName("_TexturesHigh") as ToggleButton;
            _TexturesEpic = _Root.FindName("_TexturesEpic") as ToggleButton;

            _ReflectionsOff = _Root.FindName("_ReflectionsOff") as ToggleButton;
            _ReflectionsMedium = _Root.FindName("_ReflectionsMedium") as ToggleButton;
            _ReflectionsHigh = _Root.FindName("_ReflectionsHigh") as ToggleButton;
            _ReflectionsEpic = _Root.FindName("_ReflectionsEpic") as ToggleButton;

            _PostProcessingLow = _Root.FindName("_PostProcessingLow") as ToggleButton;
            _PostProcessingMedium = _Root.FindName("_PostProcessingMedium") as ToggleButton;
            _PostProcessingHigh = _Root.FindName("_PostProcessingHigh") as ToggleButton;
            _PostProcessingEpic = _Root.FindName("_PostProcessingEpic") as ToggleButton;

            _DetailLow = _Root.FindName("_DetailLow") as ToggleButton;
            _DetailMedium = _Root.FindName("_DetailMedium") as ToggleButton;
            _DetailHigh = _Root.FindName("_DetailHigh") as ToggleButton;
            _DetailEpic = _Root.FindName("_DetailEpic") as ToggleButton;

            _FieldOfViewStackPanel = _Root.FindName(nameof(_FieldOfViewStackPanel)) as StackPanel;
            _CharacterFieldOfView = _Root.FindName("_CharacterFieldOfView") as Slider;
            _CockpitFieldOfView = _Root.FindName("_CockpitFieldOfView") as Slider;
            _ExternalFieldOfView = _Root.FindName("_ExternalFieldOfView") as Slider;
            _ResetFieldOfViewButton = _Root.FindName("_ResetFieldOfViewButton") as Button;

            _BrightnessSlider = _Root.FindName("_BrightnessSlider") as Slider;
            _ContrastSlider = _Root.FindName("_ContrastSlider") as Slider;
            _ResetColorCorrectionButton = _Root.FindName("_ResetColorCorrectionButton") as Button;

            // UI
            _UISmallest = _Root.FindName("_UISmallest") as ToggleButton;
            _UISmaller = _Root.FindName("_UISmaller") as ToggleButton;
            _UINormal = _Root.FindName("_UINormal") as ToggleButton;
            _UILarger = _Root.FindName("_UILarger") as ToggleButton;
            _UILargest = _Root.FindName("_UILargest") as ToggleButton;

            _BlurEffectsEnabled = _Root.FindName("_BlurEffectsEnabled") as CheckBox;
            _TopBlurSlider = _Root.FindName("_TopBlurSlider") as Slider;
            _BottomBlurSlider = _Root.FindName("_BottomBlurSlider") as Slider;
            _DesignerHelpUIEnabled = _Root.FindName("_DesignerHelpUIEnabled") as CheckBox;
            _GameHelpUIEnabled = _Root.FindName("_GameHelpUIEnabled") as CheckBox;

            // bind controls: audio
            _MasterVolumeEnabled = _Root.FindName("_MasterVolumeEnabled") as CheckBox;
            _MasterVolumeSlider = _Root.FindName("_MasterVolumeSlider") as Slider;

            _MusicVolumeEnabled = _Root.FindName("_MusicVolumeEnabled") as CheckBox;
            _MusicVolumeSlider = _Root.FindName("_MusicVolumeSlider") as Slider;

            _EffectsVolumeEnabled = _Root.FindName("_EffectsVolumeEnabled") as CheckBox;
            _EffectsVolumeSlider = _Root.FindName("_EffectsVolumeSlider") as Slider;

            _VoiceVolumeEnabled = _Root.FindName("_VoiceVolumeEnabled") as CheckBox;
            _VoiceVolumeSlider = _Root.FindName("_VoiceVolumeSlider") as Slider;

            _UIVolumeEnabled = _Root.FindName("_UIVolumeEnabled") as CheckBox;
            _UIVolumeSlider = _Root.FindName("_UIVolumeSlider") as Slider;

            _AmbientVolumeEnabled = _Root.FindName("_AmbientVolumeEnabled") as CheckBox;
            _AmbientVolumeSlider = _Root.FindName("_AmbientVolumeSlider") as Slider;

            // network
            _NetworkTab = _Root.FindName("_NetworkTab") as TabItem;
            _Environment = _Root.FindName("_Environment") as ComboBox;
            _ClientToServerSlider = _Root.FindName("_ClientToServerSlider") as Slider;
            _ServerToClientSlider = _Root.FindName("_ServerToClientSlider") as Slider;
            _ClientToServerRateTextBlock = _Root.FindName(nameof(_ClientToServerRateTextBlock)) as TextBlock;
            _ServerToClientRateTextBlock = _Root.FindName(nameof(_ServerToClientRateTextBlock)) as TextBlock;

            // generative AI
            _GenerativeAIStackPanel = _Root.FindName(nameof(_GenerativeAIStackPanel)) as StackPanel;
            _GenerativeTextDialogue = _Root.FindName(nameof(_GenerativeTextDialogue)) as CheckBox;
            _GenerativeAudioDialogue = _Root.FindName(nameof(_GenerativeAudioDialogue)) as CheckBox;

            // corsair
            _CorsairTab = _Root.FindName(nameof(_CorsairTab)) as TabItem;
            _RGBStackPanel = _Root.FindName(nameof(_RGBStackPanel)) as StackPanel;
            _RGBLightingEnabled = _Root.FindName(nameof(_RGBLightingEnabled)) as CheckBox;
            _TutorialKeysEnabled = _Root.FindName("_TutorialKeysEnabled") as CheckBox;
            _InputKeysEnabled = _Root.FindName("_InputKeysEnabled") as CheckBox;
            _RGBHueSlider = _Root.FindName("_RGBHueSlider") as Slider;
            _RGBSaturationSlider = _Root.FindName("_RGBSaturationSlider") as Slider;
            _RGBBrightnessSlider = _Root.FindName("_RGBBrightnessSlider") as Slider;
            _ResetCorsairColorCorrectionButton = _Root.FindName("_ResetCorsairColorCorrectionButton") as Button;
            _CorsairStackPanel = _Root.FindName("_CorsairStackPanel") as StackPanel;
            _CorsairNotFoundTextBlock = _Root.FindName("_CorsairNotFoundTextBlock") as TextBlock;

            // about
            _HyperlinkButton = _Root.FindName("_HyperLinkButton") as Hyperlink;
            _VersionTextBlock = _Root.FindName("_VersionTextBlock") as TextBlock;
        }

        private void WireEvents(Action<int> action, int defaultIndex, params ToggleButton[] buttons)
        {
            buttons[defaultIndex].IsChecked = true;

            foreach (ToggleButton button in buttons)
            {
                button.Checked += (sender, e) =>
                {
                    int index = 0;
                    for (int i = 0; i < buttons.Length; i++)
                    {
                        if (buttons[i].Equals(sender))
                            index = i;
                        else if (!buttons[i].Equals(sender))
                            buttons[i].IsChecked = false;
                    }
                    action?.Invoke(index);
                };
            }
        }

        protected override void OnInitialized(object sender, NoesisEventArgs args)
        {
            base.OnInitialized(sender, args);
            if (!Application.isPlaying)
                return;

            if (PlayerPreferences.Instance == null)
                return;

            Update(true);
        }

        public void Update(bool wireEvents)
        {
            _ApplyEnabled = false;

            // initialize values: gameplay
            _WarnSafeParkingEnabled.IsChecked = PlayerPreferences.Instance.WarnSafeParkingEnabled;
            _WarnEvilEnabled.IsChecked = PlayerPreferences.Instance.WarnEvilEnabled;

            // initialize values: controls
            _MouseConfined.IsChecked = PlayerPreferences.Instance.MouseConfined;
            _GamepadEnabled.IsChecked = PlayerPreferences.Instance.GamepadEnabled;
            _CharacterInvertUpEnabled.IsChecked = PlayerPreferences.Instance.CharacterInvertUpEnabled;
            _ShipInvertUpEnabled.IsChecked = PlayerPreferences.Instance.ShipInvertUpEnabled;
            _CharacterControl.SelectedIndex = (int)PlayerPreferences.Instance.CharacterControl;
            _InteriorCamera.SelectedIndex = (int)PlayerPreferences.Instance.DefaultInteriorCamera;
            _CharacterLookSensitivity.Value = PlayerPreferences.Instance.CharacterLookSensitivity;

            // initialize values: display
            _WindowMode.SelectedIndex = (int)PlayerPreferences.Instance.FullScreenMode;
            _FrameRateLimit.SelectedIndex = (int)PlayerPreferences.Instance.FrameRateLimit;
            _VSync.SelectedIndex = (int)PlayerPreferences.Instance.VSync;
            _MotionBlur.SelectedIndex = (int)PlayerPreferences.Instance.MotionBlur;
            _ShowFPS.SelectedIndex = PlayerPreferences.Instance.ShowFPS ? 1 : 0;

            // diplays
            List<DisplayInfo> displays = new List<DisplayInfo>();
            Screen.GetDisplayLayout(displays);

            string FilterName(string displayName)
            {
                if (string.IsNullOrEmpty(displayName))
                    return string.Empty;
                return displayName.Replace("\\", string.Empty);
            }

            for (int i = 0; i < displays.Count; i++)
            {
                DisplayInfo displayInfo = displays[i];
                ComboBoxItem comboBoxItem = new ComboBoxItem();
                comboBoxItem.Content = $"{i + 1}: {FilterName(displayInfo.name)}";
                comboBoxItem.Tag = displayInfo;
                _Displays.Items.Add(comboBoxItem);
            }
            _Displays.SelectedIndex = PlayerPreferences.Instance.SelectedDisplayIndex;

            UpdateScreeenResolutions(false);

            switch (PlayerPreferences.Instance.ResolutionUpscaler)
            {
                case ResolutionUpscalerType.DLSS:
                    _ResolutionUpscaler.SelectedIndex = 1;
                    break;
                case ResolutionUpscalerType.FSR:
                    _ResolutionUpscaler.SelectedIndex = 2;
                    break;
                case ResolutionUpscalerType.XeSS:
                    _ResolutionUpscaler.SelectedIndex = 3;
                    break;
                case ResolutionUpscalerType.TAA:
                case ResolutionUpscalerType.None:
                default:
                    _ResolutionUpscaler.SelectedIndex = 0;
                    break;
            }
            _ResolutionUpscalerQuality.SelectedIndex = (int)(PlayerPreferences.Instance.ResolutionUpscalerQuality - 2);

            _CharacterFieldOfView.Value = PlayerPreferences.Instance.CharacterFieldOfView;
            _CockpitFieldOfView.Value = PlayerPreferences.Instance.CockpitFieldOfView;
            _ExternalFieldOfView.Value = PlayerPreferences.Instance.ExternalFieldOfView;

            _BrightnessSlider.Value = PlayerPreferences.Instance.Brightness;
            _ContrastSlider.Value = PlayerPreferences.Instance.Contrast;

            // initialize values: ui
            _BlurEffectsEnabled.IsChecked = PlayerPreferences.Instance.BlurEffectsEnabled;
            _TopBlurSlider.Value = PlayerPreferences.Instance.TopBlurRadius;
            _BottomBlurSlider.Value = PlayerPreferences.Instance.BottomBlurRadius;
            _GameMenuEnabled.IsChecked = PlayerPreferences.Instance.GameMenuEnabled;
            _TargetingEnabled.IsChecked = PlayerPreferences.Instance.TargetingEnabled;
            _WindowDragEnabled.IsChecked = PlayerPreferences.Instance.WindowDragEnabled;
            _UIEnabled.IsChecked = PlayerPreferences.Instance.UIEnabled;
            _DesignerHelpUIEnabled.IsChecked = PlayerPreferences.Instance.DesignerHelpUIEnabled;
            _GameHelpUIEnabled.IsChecked = PlayerPreferences.Instance.GameHelpUIEnabled;

            _TemperatureUnits.SelectedIndex = (int)PlayerPreferences.Instance.TemperatureUnits;

            // initialize values: sound
            _MasterVolumeEnabled.IsChecked = PlayerPreferences.Instance.MasterVolumeEnabled;
            _MasterVolumeSlider.Value = PlayerPreferences.Instance.MasterVolume * 100.0f;

            _MusicVolumeEnabled.IsChecked = PlayerPreferences.Instance.MusicVolumeEnabled;
            _MusicVolumeSlider.Value = PlayerPreferences.Instance.MusicVolume * 100.0f;

            _EffectsVolumeEnabled.IsChecked = PlayerPreferences.Instance.EffectsVolumeEnabled;
            _EffectsVolumeSlider.Value = PlayerPreferences.Instance.EffectsVolume * 100.0f;

            _VoiceVolumeEnabled.IsChecked = PlayerPreferences.Instance.VoiceVolumeEnabled;
            _VoiceVolumeSlider.Value = PlayerPreferences.Instance.VoiceVolume * 100.0f;

            _UIVolumeEnabled.IsChecked = PlayerPreferences.Instance.UIVolumeEnabled;
            _UIVolumeSlider.Value = PlayerPreferences.Instance.UIVolume * 100.0f;

            _AmbientVolumeEnabled.IsChecked = PlayerPreferences.Instance.AmbientVolumeEnabled;
            _AmbientVolumeSlider.Value = PlayerPreferences.Instance.AmbientVolume * 100.0f;

            // generative ai
            _GenerativeTextDialogue.IsChecked = PlayerPreferences.Instance.GenerativeTextDialogue;
            _GenerativeAudioDialogue.IsChecked = PlayerPreferences.Instance.GenerativeAudioDialogue;

            // initialize values: rgb
            _RGBLightingEnabled.IsChecked = PlayerPreferences.Instance.RGBLightingEnabled;
            _TutorialKeysEnabled.IsChecked = PlayerPreferences.Instance.RGBConfiguration.TutorialKeysEnabled;
            _InputKeysEnabled.IsChecked = PlayerPreferences.Instance.RGBConfiguration.InputKeysEnabled;

            _Environment.SelectedIndex = (int)PlayerPreferences.Instance.ConquestEnvironment;
            _ClientToServerSlider.Value = PlayerPreferences.Instance.ClientToServerNetworkUpdateRate;
            _ServerToClientSlider.Value = PlayerPreferences.Instance.ServerToClientNetworkUpdateRate;
            _RGBHueSlider.Value = PlayerPreferences.Instance.RGBConfiguration.Hue * 100.0f;
            _RGBSaturationSlider.Value = PlayerPreferences.Instance.RGBConfiguration.Saturation * 100.0f;
            _RGBBrightnessSlider.Value = PlayerPreferences.Instance.RGBConfiguration.Brightness * 100.0f;

            // about
            _VersionTextBlock.Text = $"Version {VersionUtility.GetVersion()}{Environment.NewLine}{VersionUtility.GetBuildDate()}";

            if (!wireEvents)
            {
                _ApplyEnabled = true;
                return;
            }

            // events: gameplay
            _WarnSafeParkingEnabled.Checked += (sender2, e2) => PlayerPreferences.Instance.WarnSafeParkingEnabled = true;
            _WarnSafeParkingEnabled.Unchecked += (sender2, e2) => PlayerPreferences.Instance.WarnSafeParkingEnabled = false;
            _WarnEvilEnabled.Checked += (sender2, e2) => PlayerPreferences.Instance.WarnEvilEnabled = true;
            _WarnEvilEnabled.Unchecked += (sender2, e2) => PlayerPreferences.Instance.WarnEvilEnabled = false;
            _GameMenuEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.GameMenuEnabled = true;
                Apply();
            };

            _GameMenuEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.GameMenuEnabled = false;
                Apply();
            };
            _TargetingEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.TargetingEnabled = true;
                Apply();
            };
            _TargetingEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.TargetingEnabled = false;
                Apply();
            };

            // UI
            _WindowDragEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.WindowDragEnabled = true;
                Apply();
                UpdateWindowDragBehavior();
            };
            _WindowDragEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.WindowDragEnabled = false;
                Apply();
                UpdateWindowDragBehavior();
            };
            _TemperatureUnits.SelectionChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.TemperatureUnits = (TemperatureUnitType)_TemperatureUnits.SelectedIndex;
            };
            _UIEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.UIEnabled = true;
                Apply();
            };
            _UIEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.UIEnabled = false;
                Apply();
            };
            _DesignerHelpUIEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.DesignerHelpUIEnabled = true;
                Apply();
            };
            _DesignerHelpUIEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.DesignerHelpUIEnabled = false;
                Apply();
            };
            _GameHelpUIEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.GameHelpUIEnabled = true;
                Apply();
            };
            _GameHelpUIEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.GameHelpUIEnabled = false;
                Apply();
            };

            // events: input
            _MouseConfined.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.MouseConfined = true;
            };
            _MouseConfined.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.MouseConfined = false;
            };
            _GamepadEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.GamepadEnabled = true;
            };
            _GamepadEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.GamepadEnabled = false;
            };
            _CharacterInvertUpEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.CharacterInvertUpEnabled = true;
            };
            _CharacterInvertUpEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.CharacterInvertUpEnabled = false;
            };
            _ShipInvertUpEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.ShipInvertUpEnabled = true;
            };
            _ShipInvertUpEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.ShipInvertUpEnabled = false;
            };
            _CharacterControl.SelectionChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.CharacterControl = (CharacterControlType)_CharacterControl.SelectedIndex;
            };
            _InteriorCamera.SelectionChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.DefaultInteriorCamera = (DefaultInteriorCameraType)_InteriorCamera.SelectedIndex;
            };
            _CharacterLookSensitivity.ValueChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.CharacterLookSensitivity = e2.NewValue;
            };

            // events: sound

            // master
            _MasterVolumeEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.MasterVolumeEnabled = true;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
                UpdateButtonVisibility();
            };

            _MasterVolumeEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.MasterVolumeEnabled = false;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
                UpdateButtonVisibility();
            };

            _MasterVolumeSlider.ValueChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.MasterVolume = e2.NewValue / 100.0f;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
            };

            // music
            _MusicVolumeEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.MusicVolumeEnabled = true;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
                UpdateButtonVisibility();
            };
            _MusicVolumeEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.MusicVolumeEnabled = false;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
                UpdateButtonVisibility();
            };
            _MusicVolumeSlider.ValueChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.MusicVolume = e2.NewValue / 100.0f;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
            };

            // effects
            _EffectsVolumeEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.EffectsVolumeEnabled = true;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
                UpdateButtonVisibility();
            };
            _EffectsVolumeEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.EffectsVolumeEnabled = false;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
                UpdateButtonVisibility();
            };
            _EffectsVolumeSlider.ValueChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.EffectsVolume = e2.NewValue / 100.0f;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
            };

            // ambient
            _AmbientVolumeEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.AmbientVolumeEnabled = true;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
                UpdateButtonVisibility();
            };
            _AmbientVolumeEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.AmbientVolumeEnabled = false;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
                UpdateButtonVisibility();
            };
            _AmbientVolumeSlider.ValueChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.AmbientVolume = e2.NewValue / 100.0f;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
            };

            // voice
            _VoiceVolumeEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.VoiceVolumeEnabled = true;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
                UpdateButtonVisibility();
            };
            _VoiceVolumeEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.VoiceVolumeEnabled = false;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
                UpdateButtonVisibility();
            };
            _VoiceVolumeSlider.ValueChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.VoiceVolume = e2.NewValue / 100.0f;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
            };

            // ui
            _UIVolumeEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.UIVolumeEnabled = true;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
                UpdateButtonVisibility();
            };
            _UIVolumeEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.UIVolumeEnabled = false;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
                UpdateButtonVisibility();
            };
            _UIVolumeSlider.ValueChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.UIVolume = e2.NewValue / 100.0f;
                Apply(updateUI: false, updateAudio: true, updateDisplay: false);
            };

            void OnResolutionChanged()
            {
                if (!_ApplyEnabled)
                    return;

                if (_DisplayResolution.SelectedIndex == -1)
                    return;

                Resolution selectedResolution = _SupportedResolutions[_DisplayResolution.SelectedIndex];

                if (PlayerPreferences.Instance.FullScreenMode == FullScreenMode.Windowed)
                {
                    // use system (window) resolution and system refresh rate
                    PlayerPreferences.Instance.DisplayResolution = new Vector2Int(Screen.width, Screen.height); // causes maximized screen to reset to screen width and height?
                    //PlayerPreferences.Instance.DisplayResolution = new Vector2Int(selectedResolution.width, selectedResolution.height);
                    PlayerPreferences.Instance.DisplayRefreshRate = (float)Screen.currentResolution.refreshRateRatio.value; // ignore selected refreshrate
                }
                else if (PlayerPreferences.Instance.FullScreenMode == FullScreenMode.FullScreenWindow)
                {
                    // use selected resolution and system refresh rate
                    PlayerPreferences.Instance.DisplayResolution = new Vector2Int(selectedResolution.width, selectedResolution.height);
                    PlayerPreferences.Instance.DisplayRefreshRate = (float)Screen.currentResolution.refreshRateRatio.value; // ignore selected refreshrate
                }
                else if (PlayerPreferences.Instance.FullScreenMode == FullScreenMode.ExclusiveFullScreen)
                {
                    // use selected resolution and selected refresh rate
                    PlayerPreferences.Instance.DisplayResolution = new Vector2Int(selectedResolution.width, selectedResolution.height);
                    PlayerPreferences.Instance.DisplayRefreshRate = (float)Math.Round(selectedResolution.refreshRateRatio.value, 2);
                }

                Apply(updateDisplay: true);
            }

            void OnFullScreenModeChanged()
            {
                PlayerPreferences.Instance.FullScreenMode = (FullScreenMode)_WindowMode.SelectedIndex;
                UpdateScreeenResolutions(true);

                if (_ApplyEnabled)
                {
                    UpdateButtonVisibility();
                    if (PlayerPreferences.Instance.FullScreenMode == FullScreenMode.FullScreenWindow || PlayerPreferences.Instance.FullScreenMode == FullScreenMode.ExclusiveFullScreen)
                    {
                        _DisplayResolution.SelectedIndex = 0;
                        OnResolutionChanged(); // will invoke Apply()
                    }
                    else
                        Apply(updateDisplay: true);
                }
            }

            void OnDisplayChanged()
            {
                PlayerPreferences.Instance.SelectedDisplayIndex = _Displays.SelectedIndex;
                if (_ApplyEnabled)
                {
                    Apply(updateDisplay: true, completed: () =>
                    {
                        UpdateScreeenResolutions(resetSelection: true); // updates display and resets Resolutions combobox
                        Apply(updateDisplay: true); // applies resolution ^1
                    });
                }
            }

            _Displays.SelectionChanged += (sender, e) => OnDisplayChanged();
            _WindowMode.SelectionChanged += (sender, e) => OnFullScreenModeChanged();
            _DisplayResolution.SelectionChanged += (sender2, e2) => OnResolutionChanged();

            _ResolutionUpscaler.SelectionChanged += (sender2, e2) =>
            {
                if (_ResolutionUpscaler.SelectedIndex == 0)
                    PlayerPreferences.Instance.ResolutionUpscaler = ResolutionUpscalerType.TAA;
                else if (_ResolutionUpscaler.SelectedIndex == 1)
                    PlayerPreferences.Instance.ResolutionUpscaler = ResolutionUpscalerType.DLSS;
                else if (_ResolutionUpscaler.SelectedIndex == 2)
                    PlayerPreferences.Instance.ResolutionUpscaler = ResolutionUpscalerType.FSR;
                else if (_ResolutionUpscaler.SelectedIndex == 3)
                    PlayerPreferences.Instance.ResolutionUpscaler = ResolutionUpscalerType.XeSS;
                else
                    throw new ArgumentOutOfRangeException();
                Apply(updateDisplay: true);
            };

            _ResolutionUpscalerQuality.SelectionChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.ResolutionUpscalerQuality = (ResolutionUpscalerQualityType)_ResolutionUpscalerQuality.SelectedIndex + 2;
                Apply(updateDisplay: true);
            };

            _VSync.SelectionChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.VSync = (VSyncType)_VSync.SelectedIndex;
                
                if (PlayerPreferences.Instance.VSync == VSyncType.On)
                    _FrameRateLimit.Visibility = Visibility.Collapsed;
                else
                    _FrameRateLimit.Visibility = Visibility.Visible;

                Apply();
            };

            if (PlayerPreferences.Instance.VSync == VSyncType.On)
                _FrameRateLimit.Visibility = Visibility.Collapsed;
            else
                _FrameRateLimit.Visibility = Visibility.Visible;

            _FrameRateLimit.SelectionChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.FrameRateLimit = (FrameRateLimitType)_FrameRateLimit.SelectedIndex;
                Apply();
            };

            // motion blur
            _MotionBlur.SelectionChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.MotionBlur = (MotionBlurType)_MotionBlur.SelectedIndex;
                Apply();
            };

            // show FPS
            _ShowFPS.SelectionChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.ShowFPS = _ShowFPS.SelectedIndex == 1;
                Apply();
            };

            // quality
            Action<int> updateAction = null;
            updateAction = new Action<int>((value) =>
            {
                PlayerPreferences.Instance.Quality = (QualityType)value;
                if (_DisplayInitialized)
                    ApplyQualitySetting(PlayerPreferences.Instance.Quality);
            });
            WireEvents(updateAction, (int)PlayerPreferences.Instance.Quality, _QualityLow, _QualityMedium, _QualityHigh, _QualityEpic, _QualityAuto);

            // view distance
            updateAction = new Action<int>((value) =>
            {
                PlayerPreferences.Instance.ViewDistance = (ViewDistanceType)value;
                Apply();
            });
            WireEvents(updateAction, (int)PlayerPreferences.Instance.ViewDistance, _ViewNear, _ViewMedium, _ViewFar, _ViewEpic);

            // shadows
            updateAction = new Action<int>((value) =>
            {
                PlayerPreferences.Instance.Shadows = (ShadowsQualityType)value;
                Apply();
            });
            WireEvents(updateAction, (int)PlayerPreferences.Instance.Shadows, _ShadowsLow, _ShadowsMedium, _ShadowsHigh, _ShadowsEpic);

            // shadow distance
            updateAction = new Action<int>((value) =>
            {
                PlayerPreferences.Instance.ShadowDistance = (ShadowDistanceType)value;
                Apply();
            });
            WireEvents(updateAction, (int)PlayerPreferences.Instance.ShadowDistance, _ShadowDistanceNear, _ShadowDistanceMedium, _ShadowDistanceFar, _ShadowDistanceEpic);

            // textures
            updateAction = new Action<int>((value) =>
            {
                PlayerPreferences.Instance.Textures = (TexturesType)value;
                Apply();
            });
            WireEvents(updateAction, (int)PlayerPreferences.Instance.Textures, _TexturesLow, _TexturesMedium, _TexturesHigh, _TexturesEpic);

            // reflections
            updateAction = new Action<int>((value) =>
            {
                PlayerPreferences.Instance.Reflections = (ReflectionsType)value;
                Apply();
            });
            WireEvents(updateAction, (int)PlayerPreferences.Instance.Reflections, _ReflectionsOff, _ReflectionsMedium, _ReflectionsHigh, _ReflectionsEpic);

            // post processing
            updateAction = new Action<int>((value) =>
            {
                PlayerPreferences.Instance.PostProcessing = (PostProcessingType)value;
                Apply();
            });
            WireEvents(updateAction, (int)PlayerPreferences.Instance.PostProcessing, _PostProcessingLow, _PostProcessingMedium, _PostProcessingHigh, _PostProcessingEpic);

            // detail
            updateAction = new Action<int>((value) =>
            {
                PlayerPreferences.Instance.Detail = (DetailType)value;
                Apply();
            });
            WireEvents(updateAction, (int)PlayerPreferences.Instance.Detail, _DetailLow, _DetailMedium, _DetailHigh, _DetailEpic);

            // field of view
            _CharacterFieldOfView.ValueChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.CharacterFieldOfView = (int)e2.NewValue;
                Apply();
            };

            _CockpitFieldOfView.ValueChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.CockpitFieldOfView = (int)e2.NewValue;
                Apply();
            };

            _ExternalFieldOfView.ValueChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.ExternalFieldOfView = (int)e2.NewValue;
                Apply();
            };

            _ResetFieldOfViewButton.Click += (sender2, e2) =>
            {
                _CockpitFieldOfView.Value = 60;
                _CharacterFieldOfView.Value = 60;
                _ExternalFieldOfView.Value = 40;
            };

            _BrightnessSlider.ValueChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.Brightness = (int)e2.NewValue;
                Apply();
            };

            _ContrastSlider.ValueChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.Contrast = (int)e2.NewValue;
                Apply();
            };

            _ResetColorCorrectionButton.Click += (sender2, e2) =>
            {
                _BrightnessSlider.Value = 0;
                _ContrastSlider.Value = 0;
            };


            // ui

            // UI size
            updateAction = new Action<int>((value) =>
            {
                PlayerPreferences.Instance.UserInterfaceSize = AspectRatioUtility.DefaultAspectRatioIndex - 2 + value;
                Apply();
            });
            WireEvents(updateAction, PlayerPreferences.Instance.UserInterfaceSize, _UISmallest, _UISmaller, _UINormal, _UILarger, _UILargest);

            _BlurEffectsEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.BlurEffectsEnabled = _BlurEffectsEnabled.IsChecked.Value;
                Apply();
            };

            _BlurEffectsEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.BlurEffectsEnabled = _BlurEffectsEnabled.IsChecked.Value;
                Apply();
            };

            _TopBlurSlider.ValueChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.TopBlurRadius = (int)e2.NewValue;
                Apply();
            };

            _BottomBlurSlider.ValueChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.BottomBlurRadius = (int)e2.NewValue;
                Apply();
            };

            // generative AI
            _GenerativeTextDialogue.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.GenerativeTextDialogue = true;
            };

            _GenerativeTextDialogue.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.GenerativeTextDialogue = false;
            };

            _GenerativeAudioDialogue.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.GenerativeAudioDialogue = true;
            };

            _GenerativeAudioDialogue.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.GenerativeAudioDialogue = false;
            };

            // corsair
            _RGBLightingEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.RGBLightingEnabled = true;
                Apply(updateUI: false, updateAudio: false, updateDisplay: false, updateRGB: true);
                UpdateButtonVisibility();
            };

            _RGBLightingEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.RGBLightingEnabled = false;
                Apply(updateUI: false, updateAudio: false, updateDisplay: false, updateRGB: true);
                UpdateButtonVisibility();
            };

            _TutorialKeysEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.RGBConfiguration.TutorialKeysEnabled = true;
                Apply(updateUI: false, updateAudio: false, updateDisplay: false, updateRGB: true);
            };

            _TutorialKeysEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.RGBConfiguration.TutorialKeysEnabled = false;
                Apply(updateUI: false, updateAudio: false, updateDisplay: false, updateRGB: true);
            };

            _InputKeysEnabled.Checked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.RGBConfiguration.InputKeysEnabled = true;
                Apply(updateUI: false, updateAudio: false, updateDisplay: false, updateRGB: true);
            };

            _InputKeysEnabled.Unchecked += (sender2, e2) =>
            {
                PlayerPreferences.Instance.RGBConfiguration.InputKeysEnabled = false;
                Apply(updateUI: false, updateAudio: false, updateDisplay: false, updateRGB: true);
            };

            _RGBHueSlider.ValueChanged += (sender2, e2) =>
            {
                float value = e2.NewValue / 180.0f;
                if (value < 0)
                    value = 1 - value;

                PlayerPreferences.Instance.RGBConfiguration.Hue = value;
                RGBBehavior.Instance.SetDirty(true, true);
            };

            _RGBSaturationSlider.ValueChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.RGBConfiguration.Saturation = e2.NewValue / 100.0f;
                RGBBehavior.Instance.SetDirty(true, true);
            };

            _RGBBrightnessSlider.ValueChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.RGBConfiguration.Brightness = e2.NewValue / 100.0f;
                RGBBehavior.Instance.SetDirty(true, true);
            };

            _ResetCorsairColorCorrectionButton.Click += (sender2, e2) =>
            {
                _RGBHueSlider.Value = 0;
                _RGBSaturationSlider.Value = 0;
                _RGBBrightnessSlider.Value = 0;
            };

            if (PlayerPreferences.Instance.RGBLightingEnabled && RGBBehavior.Instance.IsAvailable)
                CreateRGBLightingUI();

            _DisplayInitialized = true;

            // global
            UpdateButtonVisibility();

            // network
            _Environment.SelectionChanged += (sender2, e2) =>
            {
                PlayerPreferences.Instance.ConquestEnvironment = (EnvironmentType)_Environment.SelectedIndex;
            };

            _ClientToServerRateTextBlock.Text = $"Client ({(int)(1000 / Mathf.Lerp(Constants.Network.SpatialUpdateDelayMaxMS, Constants.Network.SpatialUpdateDelayMinMS, PlayerPreferences.Instance.ClientToServerNetworkUpdateRate))} Hz)";
            _ServerToClientRateTextBlock.Text = $"Server ({(int)(1000 / Mathf.Lerp(Constants.Network.SpatialUpdateDelayMaxMS, Constants.Network.SpatialUpdateDelayMinMS, PlayerPreferences.Instance.ServerToClientNetworkUpdateRate))} Hz)";

            _ClientToServerSlider.ValueChanged += (sender2, e2) =>
            {
                _ClientToServerRateTextBlock.Text = $"Client ({(int)(1000 / Mathf.Lerp(Constants.Network.SpatialUpdateDelayMaxMS, Constants.Network.SpatialUpdateDelayMinMS, e2.NewValue))} Hz)";
            };
            _ServerToClientSlider.ValueChanged += (sender2, e2) =>
            {
                _ServerToClientRateTextBlock.Text = $"Server ({(int)(1000 / Mathf.Lerp(Constants.Network.SpatialUpdateDelayMaxMS, Constants.Network.SpatialUpdateDelayMinMS, e2.NewValue))} Hz)";
            };

            // about
            _HyperlinkButton.Click += (sender2, e2) => ShowSoftwareLicenseAttribution();

            _ApplyEnabled = true;
        }

        private void UpdateScreeenResolutions(bool resetSelection)
        {
            bool applyEnabled = _ApplyEnabled;
            _ApplyEnabled = false;

            void AddResolution(List<Resolution> resolutions, float scalar)
            {
                Resolution resolution = new Resolution()
                {
                    width = Mathf.RoundToInt(resolutions[0].width * scalar),
                    height = Mathf.RoundToInt(resolutions[0].height * scalar),
                    refreshRateRatio = resolutions[0].refreshRateRatio
                };

                if (!resolutions.Where(a => a.width == resolution.width && a.height == resolution.height).Any())
                    resolutions.Add(resolution);
            }

            List<Resolution> resolutions = Screen.resolutions
                .Where(a => a.width >= 1024 && a.height >= 1024 && a.refreshRateRatio.value >= 50)
                .OrderByDescending(a => a.width)
                .ThenByDescending(a => a.height)
                .ThenByDescending(a => a.refreshRateRatio.value).ToList();

            if (PlayerPreferences.Instance.FullScreenMode == FullScreenMode.FullScreenWindow)
            {
                AddResolution(resolutions, 0.90f);
                AddResolution(resolutions, 0.80f);
                AddResolution(resolutions, 0.70f);
                AddResolution(resolutions, 0.60f);
                AddResolution(resolutions, 0.50f);
            }

            _SupportedResolutions = resolutions
                .OrderByDescending(a => a.width)
                .ThenByDescending(a => a.height)
                .ThenByDescending(a => a.refreshRateRatio.value)
                .ToList();

            Resolution highestScreenResolution = _SupportedResolutions[0];
            var nativeAspectRatio = UIUtility.GetAspectRatio(highestScreenResolution.width, highestScreenResolution.height);

            _DisplayResolution.Items.Clear();
            foreach (Resolution resolution in _SupportedResolutions)
            {
                var selectedAspectRatio = UIUtility.GetAspectRatio(resolution.width, resolution.height);

                ComboBoxItem i = new ComboBoxItem();

                if (selectedAspectRatio == nativeAspectRatio)
                {
                    i.Content = $"{resolution.width,4} x {resolution.height,-4} @ {Math.Round(resolution.refreshRateRatio.value, 2)}Hz 📐 ";
                    i.FontFamily = FindResource("HeadingFontFamily") as FontFamily;
                }
                else
                    i.Content = $"{resolution.width,4} x {resolution.height,-4} @ {Math.Round(resolution.refreshRateRatio.value, 2)}Hz";

                i.Tag = resolution;
                _DisplayResolution.Items.Add(i);
            }

            if (resetSelection)
                _DisplayResolution.SelectedIndex = 0;
            else
            {
                int selectedResolutionIndex = -1;
                for (int i = 0; i < _SupportedResolutions.Count; i++)
                    if (_SupportedResolutions[i].width == PlayerPreferences.Instance.DisplayResolution.x &&
                        _SupportedResolutions[i].height == PlayerPreferences.Instance.DisplayResolution.y &&
                        (PlayerPreferences.Instance.FullScreenMode != FullScreenMode.ExclusiveFullScreen ||
                        (Math.Round(_SupportedResolutions[i].refreshRateRatio.value, 2) == Math.Round(PlayerPreferences.Instance.DisplayRefreshRate, 2))))
                    {
                        selectedResolutionIndex = i;
                        break;
                    }

                int currentScreenResolutionIndex = -1;
                for (int i = 0; i < _SupportedResolutions.Count; i++)
                {
                    if (_SupportedResolutions[i].width == Screen.currentResolution.width &&
                        _SupportedResolutions[i].height == Screen.currentResolution.height &&
                        (PlayerPreferences.Instance.FullScreenMode != FullScreenMode.ExclusiveFullScreen ||
                        (_SupportedResolutions[i].refreshRateRatio.numerator == Screen.currentResolution.refreshRateRatio.numerator &&
                        _SupportedResolutions[i].refreshRateRatio.denominator == Screen.currentResolution.refreshRateRatio.denominator)))
                    {
                        currentScreenResolutionIndex = i;
                        break;
                    }
                }

                if (selectedResolutionIndex != -1)
                    _DisplayResolution.SelectedIndex = selectedResolutionIndex;
                else if (currentScreenResolutionIndex != -1)
                    _DisplayResolution.SelectedIndex = currentScreenResolutionIndex;
                else
                    _DisplayResolution.SelectedIndex = 0;
            }

            _ApplyEnabled = applyEnabled;
        }


        private void ApplyQualitySetting(QualityType value)
        {
            _ApplyEnabled = false;
            switch (value)
            {
                case QualityType.Low:
                    _ViewNear.IsChecked = true;
                    _ShadowsMedium.IsChecked = true;
                    _ShadowDistanceMedium.IsChecked = true;
                    _TexturesLow.IsChecked = true;
                    _ReflectionsMedium.IsChecked = true;
                    _PostProcessingLow.IsChecked = true;
                    _DetailLow.IsChecked = true;
                    break;
                case QualityType.Medium:
                    _ViewMedium.IsChecked = true;
                    _ShadowsMedium.IsChecked = true;
                    _ShadowDistanceMedium.IsChecked = true;
                    _TexturesMedium.IsChecked = true;
                    _ReflectionsMedium.IsChecked = true;
                    _PostProcessingMedium.IsChecked = true;
                    _DetailMedium.IsChecked = true;
                    break;
                case QualityType.High:
                    _ViewFar.IsChecked = true;
                    _ShadowsHigh.IsChecked = true;
                    _ShadowDistanceFar.IsChecked = true;
                    _TexturesHigh.IsChecked = true;
                    _ReflectionsMedium.IsChecked = true;
                    _PostProcessingHigh.IsChecked = true;
                    _DetailHigh.IsChecked = true;
                    break;
                case QualityType.Epic:
                    _ViewEpic.IsChecked = true;
                    _ShadowsEpic.IsChecked = true;
                    _ShadowDistanceEpic.IsChecked = true;
                    _TexturesHigh.IsChecked = true;
                    _ReflectionsHigh.IsChecked = true;
                    _PostProcessingEpic.IsChecked = true;
                    _DetailEpic.IsChecked = true;
                    break;
                case QualityType.Auto:
                    PlayerPreferences.Instance.VSync = 0;
                    UnityDispatcher.Instance.Invoke(TimeSpan.FromMilliseconds(250), () =>
                    {
                        float fps = GameState.Instance.FrameRateCounter.FPS;
                        PlayerPreferences.Instance.VSync = (VSyncType)_VSync.SelectedIndex;
                        if (fps > 125)
                            ApplyQualitySetting(QualityType.Epic);
                        else if (fps > 100)
                            ApplyQualitySetting(QualityType.High);
                        else if (fps > 75)
                            ApplyQualitySetting(QualityType.Medium);
                        else
                            ApplyQualitySetting(QualityType.Low);
                    });
                    return;
                default:
                    break;
            }

            _ApplyEnabled = true;
            Apply();
        }

        public override void Show()
        {
            base.Show();

            if (Application.isEditor || RuntimeSettings.Instance.IsDeveloperMode || PlayerPreferences.Instance.ConquestEnvironment == EnvironmentType.LocalDevelopment)
                _NetworkTab.Visibility = Visibility.Visible;
            else
                _NetworkTab.Visibility = Visibility.Collapsed;

#if (UNITY_GAMECORE)
            _CorsairTab.Visibility = Visibility.Collapsed;
            _DevicesStackPanel.Visibility = Visibility.Collapsed;
            _ResolutionStackPanel.Visibility = Visibility.Collapsed;
            _QualityStackPanel.Visibility = Visibility.Collapsed;
            _FieldOfViewStackPanel.Visibility = Visibility.Collapsed;
            _WindowDragEnabled.Parent.Visibility = Visibility.Collapsed;
            _UIEnabled.Parent.Visibility = Visibility.Collapsed;
            _GameMenuEnabled.Parent.Visibility = Visibility.Collapsed;
            _GenerativeAIStackPanel.Visibility = Visibility.Collapsed;
#endif
            InputHelpManager.Instance.UpdateRGBKeysForUI();


        }

        public override void Hide()
        {
            base.Hide();

            // update preferences that should change only once, upon save
            PlayerPreferences.Instance.ClientToServerNetworkUpdateRate = _ClientToServerSlider.Value;
            PlayerPreferences.Instance.ServerToClientNetworkUpdateRate = _ServerToClientSlider.Value;

            // save preferences
            PlayerPreferences.Instance.SaveRGB();
            PlayerPreferences.Instance.SavePreferences();
        }

        public void Apply(bool updateUI = true, bool updateAudio = true, bool updateDisplay = true, bool updateRGB = true, bool updateWorld = true, Action completed = null)
        {
            if (_ApplyEnabled)
            {
                if (updateRGB)
                {
                    // RGB state
                    if (_RGBLightingEnabled.IsChecked.Value)
                    {
                        // create device UI if it does not exist, and Corsair RGB is ready
                        if (_CorsairStackPanel.Children.Count == 0)
                            UnityDispatcher.Instance.Invoke(async () =>
                        {
                            int tries = 0;
                            while (tries++ < 4)
                            {
                                if (RGBBehavior.Instance.IsAvailable)
                                {
                                    CreateRGBLightingUI();
                                    break;
                                }
                                else
                                    await Task.Delay(50);
                            }

                            if (RGBBehavior.Instance.IsAvailable)
                            {
                                _RGBStackPanel.Visibility = Visibility.Visible;
                                _CorsairNotFoundTextBlock.Visibility = Visibility.Collapsed;
                            }
                            else
                            {
                                _RGBStackPanel.Visibility = Visibility.Collapsed;
                                _CorsairNotFoundTextBlock.Visibility = Visibility.Visible;
                            }
                        });
                    }
                    else
                    {
                        _RGBStackPanel.Visibility = Visibility.Collapsed;
                        _CorsairNotFoundTextBlock.Visibility = Visibility.Collapsed;
                        _CorsairStackPanel.Children.Clear();
                    }
                }

                PlayerPreferences.Instance.Apply(updateUI, updateAudio, updateDisplay, updateRGB, updateWorld, completed);

                if (RGBBehavior.Instance.IsAvailable && updateRGB)
                    RGBBehavior.Instance.LoadDevices();
            }
        }

        private void UpdateButtonVisibility()
        {
            if (_MasterVolumeEnabled.IsChecked.Value)
            {
                _MasterVolumeSlider.IsEnabled = true;
                _MasterVolumeSlider.Opacity = 1.0f;
            }
            else
            {
                _MasterVolumeSlider.IsEnabled = false;
                _MasterVolumeSlider.Opacity = 0.5f;
            }

            if (_MusicVolumeEnabled.IsChecked.Value)
            {
                _MusicVolumeSlider.IsEnabled = true;
                _MusicVolumeSlider.Opacity = 1.0f;
            }
            else
            {
                _MusicVolumeSlider.IsEnabled = false;
                _MusicVolumeSlider.Opacity = 0.5f;
            }

            if (_EffectsVolumeEnabled.IsChecked.Value)
            {
                _EffectsVolumeSlider.IsEnabled = true;
                _EffectsVolumeSlider.Opacity = 1.0f;
            }
            else
            {
                _EffectsVolumeSlider.IsEnabled = false;
                _EffectsVolumeSlider.Opacity = 0.5f;
            }

            if (_AmbientVolumeEnabled.IsChecked.Value)
            {
                _AmbientVolumeSlider.IsEnabled = true;
                _AmbientVolumeSlider.Opacity = 1.0f;
            }
            else
            {
                _AmbientVolumeSlider.IsEnabled = false;
                _AmbientVolumeSlider.Opacity = 0.5f;
            }


            if (!_RGBLightingEnabled.IsChecked.Value)
            {
                _RGBStackPanel.Visibility = Visibility.Collapsed;
                _CorsairNotFoundTextBlock.Visibility = Visibility.Collapsed;
            }

            if (_WindowMode.SelectedIndex == (int)FullScreenMode.ExclusiveFullScreen || _WindowMode.SelectedIndex == (int)FullScreenMode.FullScreenWindow)
                _DisplayResolutionStackPanel.Visibility = Visibility.Visible;
            else
                _DisplayResolutionStackPanel.Visibility = Visibility.Hidden;
        }

        private void CreateRGBLightingUI()
        {
            if (RGBBehavior.Instance.IsAvailable)
                foreach (RGBDeviceMetadata metadata in RGBBehavior.Instance.Devices)
                {
                    string deviceName = RGBConfiguration.SantitizeDeviceName(metadata.Device.DeviceInfo.DeviceName);

                    StackPanel stackPanel = new StackPanel();
                    stackPanel.Orientation = Orientation.Horizontal;
                    stackPanel.Margin = new Thickness(0, 0, 0, 12);
                    _CorsairStackPanel.Children.Add(stackPanel);

                    TextBlock textBlock = new TextBlock();
                    textBlock.Style = (Style)_Root.FindResource("Heading4Primary");
                    textBlock.HorizontalAlignment = HorizontalAlignment.Right;
                    textBlock.VerticalAlignment = VerticalAlignment.Center;
                    textBlock.Width = 250;
                    textBlock.Margin = new Thickness(0, 0, 5, 0);
                    textBlock.Text = deviceName;
                    ToolTipService.SetToolTip(textBlock, textBlock.Text);
                    stackPanel.Children.Add(textBlock);

                    ComboBox comboBox = new ComboBox();
                    comboBox.Width = 315;
                    stackPanel.Children.Add(comboBox);

                    Array values = Enum.GetValues(typeof(LightingAssignmentType));
                    for (int i = 0; i < values.Length; i++)
                    {
                        LightingAssignmentType lightingAssignmentType = (LightingAssignmentType)i;
                        ComboBoxItem comboBoxItem = new ComboBoxItem();
                        comboBoxItem.Tag = lightingAssignmentType;
                        comboBoxItem.Content = lightingAssignmentType.ToString().Replace("_", " ");
                        comboBox.Items.Add(comboBoxItem);
                    }

                    comboBox.SelectedIndex = (int)metadata.Binding.Assignment;

                    comboBox.SelectionChanged += (sender, e) =>
                    {
                        ComboBoxItem selectedItem = (ComboBoxItem)e.AddedItems[0];
                        LightingAssignmentType assignment = (LightingAssignmentType)selectedItem.Tag;
                        metadata.Binding.Assignment = assignment;

                        Apply(updateUI: false, updateAudio: false, updateDisplay: false, updateRGB: true);
                    };
                }
        }

        private void ShowSoftwareLicenseAttribution()
        {
            Hide();

            LicenseAttributionControl control = new LicenseAttributionControl();
            control.Hidden += (sender, e) => Show();
            control.Show();
        }

        public override void UpdateInput()
        {
            if (OpenFrame != Time.frameCount)
            {
                if (Input.GetKeyDown(KeyCode.Escape) ||
                    Input.GetButtonDown(InputNames.GamePad.B) ||
                    Input.GetButtonDown(InputNames.GamePad.Start) ||
                    Input.GetKeyDown(KeyCode.F1))
                {
                    Hide();
                }
            }
        }
    }
}
SettingsControl.xaml.cs (69,999 bytes)   
OverlayControl.cs (9,635 bytes)   
using Noesis;
using Noesis.Interactivity;
using System;
using UnityEngine;
using Color = Noesis.Color;
using Grid = Noesis.Grid;
using NoesisEventArgs = Noesis.EventArgs;

namespace StellarConquest.Presentation.Unity.UI
{
    public enum HideReasonType
    {
        None,
        Programmatic,
        LightDismiss,
    }


    [AssetDependency]
    public partial class OverlayControl : UserControl, IGameScreenInputProvider
    {
        public event EventHandler<NoesisEventArgs> Shown;
        public event EventHandler<NoesisEventArgs> Hidden;

        public bool IsOverlayVisible { get; private set; }
        public bool IsLightDismissExteriorEnabled { get; set; } = true;
        public bool IsLightDismissInteriorEnabled { get; set; } = false;
        public bool IsBlurEnabled { get; set; } = false;
        public bool IsBackgroundSourceBlendEnabled { get; set; } = true;
        public bool IsModal { get; set; } = true;
        public bool IsFadeEnabled { get; set; } = true;
        public bool IsUnscaled { get; set; } = false;
        public bool IsAnimatedShow { get; set; } = false;
        public bool IsAlternateKeyToCloseEnabled { get; set; } = false;
        public KeyCode AlternateCloseKey { get; set; } = KeyCode.F;
        public bool IsDragAllowed { get; set; } = false;
        public bool IsDragEnabled { get; set; } = false;
        public bool IsEscapeToCloseEnabled { get; set; } = false;
        public OverlayLayerType Layer { get; set; } = OverlayLayerType.Primary;
        public HorizontalAlignment ParentHorizontalAlignment { get; set; } = HorizontalAlignment.Center;
        public VerticalAlignment ParentVerticalAlignment { get; set; } = VerticalAlignment.Center;
        public HideReasonType HideReason { get; private set; } = HideReasonType.None;
        public bool AllowSubsequentPopupDelay { get; protected set; } = true;

        private Grid _ParentGrid;
        private OverlayLayer _OverlayLayer;
       

        public int OpenFrame { get; private set; } = -1;
        public int CloseFrame { get; private set; } = -1;
        public static int CloseFrameGlobal { get; private set; } = -1;

        public bool UseKeyUpEvent;

        public OverlayControl()
        {
        }

        protected virtual void InitializeComponent()
        {
            if (!Application.isPlaying)
                return;

            _OverlayLayer = UIStateMachine.Instance.OverlayLayer;

            _ParentGrid = new Grid();
            _ParentGrid.Name = Name + "_ParentGrid";
            _ParentGrid.HorizontalAlignment = HorizontalAlignment.Stretch;
            _ParentGrid.VerticalAlignment = VerticalAlignment.Stretch;
            if (IsModal)
                _ParentGrid.Background = new SolidColorBrush(Color.FromArgb(0, 0, 0, 0)); // transparent but interactive
            else
                _ParentGrid.Background = null;
            _ParentGrid.Children.Add(this);
        }

        protected virtual void OnInitialized(object sender, NoesisEventArgs args)
        {
            if (!Application.isPlaying)
                return;

            UpdateLightDismiss();
        }

        public virtual void Show()
        {
            _OverlayLayer.Tag = this;
            PickingManager.Instance.InteractionFired = true;
            OpenFrame = Time.frameCount;
            HideReason = HideReasonType.None;

            if (IsUnscaled)
                _OverlayLayer.ShowUnscaled(_ParentGrid, Layer, IsBlurEnabled, IsBackgroundSourceBlendEnabled, IsFadeEnabled, IsModal, ParentHorizontalAlignment, ParentVerticalAlignment);
            else
                _OverlayLayer.Show(_ParentGrid, Layer, IsBlurEnabled, IsBackgroundSourceBlendEnabled, IsFadeEnabled, IsModal, ParentHorizontalAlignment, ParentVerticalAlignment);

            if (IsAnimatedShow)
                this.Grow(_OverlayLayer, TimeSpan.FromMilliseconds(160));

            if (Layer == OverlayLayerType.Primary)
            {
                IsOverlayVisible = true;
                UIStateMachine.Instance.AddInputProvider(this);
            }

            UpdateWindowDragBehavior();
            Shown?.Invoke(this, NoesisEventArgs.Empty);
        }

        public virtual void Hide()
        {
            _OverlayLayer.Tag = null;
            PickingManager.Instance.InteractionFired = true;
            CloseFrame = Time.frameCount;
            CloseFrameGlobal = CloseFrame;
            if (HideReason == HideReasonType.None)
                HideReason = HideReasonType.Programmatic;

            if (IsUnscaled)
                _OverlayLayer.HideUnscaled(_ParentGrid, Layer);
            else
                _OverlayLayer.Hide(_ParentGrid, Layer, AllowSubsequentPopupDelay);

            if (Layer == OverlayLayerType.Primary)
            {
                IsOverlayVisible = false;
                UIStateMachine.Instance.RemoveInputProvider(this);
            }

            if (UIStateMachine.Instance.IsGameScreenOpen)
            {
                if (this == UIStateMachine.Instance.GameScreen.TargetingControl.SelectedTargetMenu)
                    UIStateMachine.Instance.GameScreen.TargetingControl.SelectedTargetMenu = null;
            }

            Hidden?.Invoke(this, NoesisEventArgs.Empty);
        }

        public bool Toggle()
        {
            if (IsOverlayVisible)
            {
                Hide();
                return false;
            }
            else
            {
                Show();
                return true;
            }
        }

        protected void UpdateWindowDragBehavior()
        {
            if (IsDragAllowed && PlayerPreferences.Instance.WindowDragEnabled || IsDragEnabled)
            {
                if (Interaction.GetBehaviors(this).Count == 0)
                {
                    ParentHorizontalAlignment = HorizontalAlignment.Stretch;
                    ParentVerticalAlignment = VerticalAlignment.Stretch;
                    HorizontalAlignment = HorizontalAlignment.Center;
                    VerticalAlignment = VerticalAlignment.Center;

                    MouseDragElementBehavior dragBehavior = new MouseDragElementBehavior();
                    dragBehavior.ConstrainToParentBounds = true;

                    Interaction.GetBehaviors(this).Add(dragBehavior);
                }
            }
            else
            {
                if (Interaction.GetBehaviors(this).Count > 0)
                    Interaction.GetBehaviors(this).Clear();
            }
        }

        public Vector2 GetViewboxScale()
        {
            return _OverlayLayer.GetViewboxScale();
        }

        protected void UpdateLightDismiss()
        {
            // Noesis MouseUp event processing runs independent of PickingManager MouseDown processing.  The Show() method
            // is invoked from InteractionEventManager.  For this reason, it is possible for PickingManager to request Show() AND
            // for _ParentGrid.MouseUp to fire immediately thereafter.  Variable _InteractionFrame tracks the frame for Show(), and 
            // subsequently ignores mouse events occuring on this same frame.  While unfortunate, this is necessary logic to accomodate
            // parallel threads which have access to the same data structure.  The alternative, using Noesis for all UI or Unity for all UI 
            // (ray casting), is not tennable due to the inherent disconnect between 2D (Noesis) and 3D (Picking).

            _ParentGrid.MouseLeftButtonUp -= LightDismissExteriorMouseUp;
            if (IsLightDismissExteriorEnabled)
                _ParentGrid.MouseLeftButtonUp += LightDismissExteriorMouseUp;

            MouseLeftButtonUp -= LightDismissInteriorMouseUp;
            if (!IsLightDismissInteriorEnabled) 
                MouseLeftButtonUp += LightDismissInteriorMouseUp;
        }

        private void LightDismissExteriorMouseUp(object sender, MouseButtonEventArgs args)
        {
            if (OpenFrame != Time.frameCount && !PickingManager.Instance.InteractionFired)
            {
                HideReason = HideReasonType.LightDismiss;
                Hide();
            }
        }

        private void LightDismissInteriorMouseUp(object sender, MouseButtonEventArgs args)
        {
            if (OpenFrame != Time.frameCount && !PickingManager.Instance.InteractionFired)
                args.Handled = true;
        }

        public virtual void UpdateInput()
        {
            if (OpenFrame != Time.frameCount)
                if (!PickingManager.Instance.InteractionFired)
                    if (IsLightDismissExteriorEnabled || IsLightDismissInteriorEnabled || IsEscapeToCloseEnabled)
                    {
                        if (UseKeyUpEvent)
                        {
                            if (Input.GetButtonUp(InputNames.GamePad.B) || Input.GetKeyUp(KeyCode.Escape) ||
                                (IsAlternateKeyToCloseEnabled && Input.GetKeyDown(AlternateCloseKey)))
                                Hide();
                        }
                        else
                        {
                            if (Input.GetButtonDown(InputNames.GamePad.B) || Input.GetKeyDown(KeyCode.Escape) ||
                                (IsAlternateKeyToCloseEnabled && Input.GetKeyDown(AlternateCloseKey)))
                                Hide();
                        }
                    }
        }
    }
}
OverlayControl.cs (9,635 bytes)   
stonstad

stonstad

2026-08-20 19:46

reporter   ~0012539

Last edited: 2026-08-20 19:47

Edited initial issue above to note that the behavior manifests after every edit to a CS file.

stonstad

stonstad

2026-08-20 20:16

reporter   ~0012540

Last edited: 2026-08-20 20:16

OK, I likely misunderstood our previous issue. I thought adding [AssetDependency] was sufficient to resolve dependencies. I have learned that I need to either define asset dependencies in code using the attribute or in XAML using the deprecated Dependencies tag. I am now using the Dependencies tag in XAML and the above behaviors are resolved.

Candidate for closure. Thanks!

Issue History

Date Modified Username Field Change
2026-08-20 19:23 stonstad New Issue
2026-08-20 19:23 stonstad File Added: TitleScreen.xaml
2026-08-20 19:23 stonstad File Added: TitleScreen.xaml.cs
2026-08-20 19:23 stonstad Summary AssetDependency Fails After Subsequent Stop/Play Cycle => 4.0 RC5. AssetDependency Fails After Subsequent Stop/Play Cycle
2026-08-20 19:28 stonstad Description Updated
2026-08-20 19:43 stonstad Note Added: 0012538
2026-08-20 19:43 stonstad File Added: SettingsControl.xaml
2026-08-20 19:43 stonstad File Added: SettingsControl.xaml.cs
2026-08-20 19:43 stonstad File Added: OverlayControl.cs
2026-08-20 19:45 stonstad Description Updated
2026-08-20 19:46 stonstad Note Added: 0012539
2026-08-20 19:47 stonstad Note Edited: 0012539
2026-08-20 20:16 stonstad Note Added: 0012540
2026-08-20 20:16 stonstad Note Edited: 0012540