mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 19:06:15 +08:00
639 lines
37 KiB
XML
639 lines
37 KiB
XML
<!--
|
||
Copyright (C) 2026 SharpEmu Emulator Project
|
||
SPDX-License-Identifier: GPL-2.0-or-later
|
||
-->
|
||
|
||
<Window xmlns="https://github.com/avaloniaui"
|
||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||
xmlns:local="clr-namespace:SharpEmu.GUI"
|
||
xmlns:primitives="clr-namespace:Avalonia.Controls.Primitives;assembly=Avalonia.Controls"
|
||
x:Class="SharpEmu.GUI.MainWindow"
|
||
Title="SharpEmu"
|
||
Width="1280" Height="820"
|
||
MinWidth="980" MinHeight="640"
|
||
WindowState="Maximized"
|
||
WindowStartupLocation="CenterScreen"
|
||
Background="{StaticResource BgBrush}"
|
||
ExtendClientAreaToDecorationsHint="True"
|
||
ExtendClientAreaChromeHints="PreferSystemChrome"
|
||
ExtendClientAreaTitleBarHeightHint="44"
|
||
Icon="avares://SharpEmu.GUI/Assets/SharpEmu.ico"
|
||
KeyDown="OnKeyDown">
|
||
|
||
<Grid x:Name="RootLayout" RowDefinitions="Auto,*,Auto">
|
||
|
||
<!-- Selected-game backdrop: key art behind the main content, dimmed by
|
||
a scrim so tiles and text stay readable. Fades on selection. -->
|
||
<Panel Grid.Row="1" ClipToBounds="True">
|
||
<Image x:Name="BackdropImage" Stretch="UniformToFill" Opacity="0"
|
||
HorizontalAlignment="Center" VerticalAlignment="Center">
|
||
<Image.Transitions>
|
||
<Transitions>
|
||
<DoubleTransition Property="Opacity" Duration="0:0:0.35" />
|
||
</Transitions>
|
||
</Image.Transitions>
|
||
</Image>
|
||
<Border>
|
||
<Border.Background>
|
||
<LinearGradientBrush StartPoint="0%,0%" EndPoint="0%,100%">
|
||
<GradientStop Offset="0" Color="#730D1017" />
|
||
<GradientStop Offset="0.55" Color="#BF0D1017" />
|
||
<GradientStop Offset="1" Color="#EB0D1017" />
|
||
</LinearGradientBrush>
|
||
</Border.Background>
|
||
</Border>
|
||
</Panel>
|
||
|
||
<!-- Title bar; hidden in fullscreen (F11) along with the status bar, so
|
||
the game gets the whole screen. -->
|
||
<Grid x:Name="TitleBar" Grid.Row="0" Height="44" Background="{StaticResource ChromeBrush}">
|
||
<StackPanel Orientation="Horizontal" Spacing="10" Margin="16,0" VerticalAlignment="Center">
|
||
<Image Source="avares://SharpEmu.GUI/Assets/SharpEmu.ico" Width="20" Height="20"
|
||
RenderOptions.BitmapInterpolationMode="HighQuality" />
|
||
<TextBlock Text="SharpEmu" FontSize="14" FontWeight="SemiBold" VerticalAlignment="Center" />
|
||
<Border Classes="pill">
|
||
<TextBlock x:Name="VersionText" Text="v0.0.1" FontSize="11" Foreground="{StaticResource MutedBrush}" />
|
||
</Border>
|
||
</StackPanel>
|
||
</Grid>
|
||
|
||
<!-- Main content -->
|
||
<Grid x:Name="MainContent" Grid.Row="1" Margin="32,24,32,20" RowDefinitions="Auto,*,Auto,Auto">
|
||
|
||
<!-- The game owns the full client area while running. Session controls
|
||
use a native popup so they can stay above this native child surface. -->
|
||
<Border x:Name="GameView" Grid.Row="0" Grid.RowSpan="4" IsVisible="False" Background="#000000" ClipToBounds="True">
|
||
<Grid x:Name="GameSurfaceContainer" />
|
||
</Border>
|
||
|
||
<!-- Library / Options page switcher, with the library toolbar sharing
|
||
the same row on the right. Plain buttons (not TabItem) so there is
|
||
no underline; LB/RB hint chips flank the pair and the gamepad's
|
||
shoulder buttons actually switch pages from anywhere. -->
|
||
<Grid x:Name="ContentToolbar" Grid.Row="0" ColumnDefinitions="Auto,*,Auto" Margin="0,0,0,20">
|
||
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="14" VerticalAlignment="Center">
|
||
<Border Classes="padHint" VerticalAlignment="Center">
|
||
<TextBlock Text="LB" FontSize="11" FontWeight="Bold" Foreground="{StaticResource MutedBrush}" />
|
||
</Border>
|
||
<Button x:Name="LibraryTabButton" Classes="segment active" Content="Library" />
|
||
<Button x:Name="OptionsTabButton" Classes="segment" Content="Options" />
|
||
<Border Classes="padHint" VerticalAlignment="Center">
|
||
<TextBlock Text="RB" FontSize="11" FontWeight="Bold" Foreground="{StaticResource MutedBrush}" />
|
||
</Border>
|
||
</StackPanel>
|
||
|
||
<StackPanel Grid.Column="2" x:Name="LibraryToolbar" Orientation="Horizontal" Spacing="8"
|
||
VerticalAlignment="Center">
|
||
<TextBox x:Name="SearchBox" Watermark="Search library…" Width="240" VerticalAlignment="Center" />
|
||
<Button x:Name="AddFolderButton" Classes="ghost" Content="+ Add folder" VerticalAlignment="Center" />
|
||
<Button x:Name="RescanButton" Classes="ghost" Content="⟳ Rescan" VerticalAlignment="Center" />
|
||
<Button x:Name="OpenFileButton" Classes="ghost" Content="Open file…" VerticalAlignment="Center" />
|
||
</StackPanel>
|
||
</Grid>
|
||
|
||
<Panel Grid.Row="1" x:Name="PagesHost">
|
||
|
||
<!-- Library page. The tile row gets extra top margin so it sits
|
||
closer to eye level (PS5 home-screen style) instead of hugging
|
||
the Library/Options switcher above it. -->
|
||
<Panel x:Name="LibraryPage" Margin="0,64,0,0">
|
||
<ListBox x:Name="GameList" Classes="tileGrid" Background="Transparent"
|
||
SelectionMode="Single" Padding="0">
|
||
<ListBox.ContextMenu>
|
||
<ContextMenu x:Name="GameContextMenu" Placement="Pointer">
|
||
<MenuItem x:Name="CtxLaunch" Header="Launch" FontWeight="SemiBold">
|
||
<MenuItem.Icon>
|
||
<TextBlock Text="▶" FontSize="11" Foreground="{StaticResource AccentHoverBrush}"
|
||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||
</MenuItem.Icon>
|
||
</MenuItem>
|
||
<MenuItem x:Name="CtxOpenFolder" Header="Open game folder">
|
||
<MenuItem.Icon>
|
||
<TextBlock Text="📂" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||
</MenuItem.Icon>
|
||
</MenuItem>
|
||
<Separator />
|
||
<MenuItem x:Name="CtxCopyPath" Header="Copy path">
|
||
<MenuItem.Icon>
|
||
<TextBlock Text="⧉" FontSize="13" Foreground="{StaticResource MutedBrush}"
|
||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||
</MenuItem.Icon>
|
||
</MenuItem>
|
||
<MenuItem x:Name="CtxCopyTitleId" Header="Copy title ID">
|
||
<MenuItem.Icon>
|
||
<TextBlock Text="⧉" FontSize="13" Foreground="{StaticResource MutedBrush}"
|
||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||
</MenuItem.Icon>
|
||
</MenuItem>
|
||
<Separator />
|
||
<MenuItem x:Name="CtxGameSettings" Header="Game settings…">
|
||
<MenuItem.Icon>
|
||
<TextBlock Text="⚙" FontSize="13" Foreground="{StaticResource MutedBrush}"
|
||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||
</MenuItem.Icon>
|
||
</MenuItem>
|
||
<Separator />
|
||
<MenuItem x:Name="CtxRemove" Header="Remove from library"
|
||
Foreground="{StaticResource DangerHoverBrush}">
|
||
<MenuItem.Icon>
|
||
<TextBlock Text="✕" FontSize="12" Foreground="{StaticResource DangerHoverBrush}"
|
||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||
</MenuItem.Icon>
|
||
</MenuItem>
|
||
</ContextMenu>
|
||
</ListBox.ContextMenu>
|
||
<ListBox.ItemsPanel>
|
||
<ItemsPanelTemplate>
|
||
<WrapPanel />
|
||
</ItemsPanelTemplate>
|
||
</ListBox.ItemsPanel>
|
||
<ListBox.ItemTemplate>
|
||
<DataTemplate>
|
||
<StackPanel Width="128" Height="172" Spacing="7">
|
||
<Border Classes="coverShadow" Width="128" Height="128">
|
||
<Border Classes="coverClip">
|
||
<Panel>
|
||
<Border Background="{Binding PlaceholderBrush}" IsVisible="{Binding !HasCover}">
|
||
<TextBlock Text="{Binding Initials}" FontSize="36" FontWeight="Bold"
|
||
Foreground="#E8ECF4" Opacity="0.85"
|
||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||
</Border>
|
||
<Image Source="{Binding Cover}" Stretch="UniformToFill" IsVisible="{Binding HasCover}" />
|
||
</Panel>
|
||
</Border>
|
||
</Border>
|
||
<TextBlock Text="{Binding Name}" FontSize="13" FontWeight="SemiBold"
|
||
TextWrapping="Wrap" MaxLines="2" TextTrimming="CharacterEllipsis"
|
||
TextAlignment="Center" HorizontalAlignment="Center" />
|
||
</StackPanel>
|
||
</DataTemplate>
|
||
</ListBox.ItemTemplate>
|
||
</ListBox>
|
||
|
||
<!-- Empty state -->
|
||
<StackPanel x:Name="EmptyState" Spacing="10" HorizontalAlignment="Center" VerticalAlignment="Center"
|
||
IsVisible="False">
|
||
<TextBlock Text="🎮" FontSize="44" HorizontalAlignment="Center" Opacity="0.7" />
|
||
<TextBlock x:Name="EmptyStateTitle" Text="Your library is empty" FontSize="18" FontWeight="SemiBold"
|
||
HorizontalAlignment="Center" />
|
||
<TextBlock x:Name="EmptyStateHint" Text="Add a folder containing your games to get started."
|
||
FontSize="13" Foreground="{StaticResource MutedBrush}" HorizontalAlignment="Center" />
|
||
<Button x:Name="EmptyAddFolderButton" Classes="accent" Content="+ Add game folder"
|
||
HorizontalAlignment="Center" Margin="0,8,0,0" />
|
||
</StackPanel>
|
||
|
||
<!-- Loading state: covers the grid while a scan (initial load,
|
||
rescan, or a just-added folder) is in flight, so the screen
|
||
never looks blank mid-scan. -->
|
||
<StackPanel x:Name="LoadingState" Spacing="14" HorizontalAlignment="Center" VerticalAlignment="Center"
|
||
IsVisible="False">
|
||
<ProgressBar IsIndeterminate="True" Width="180" Height="3" />
|
||
<TextBlock x:Name="LoadingStateText" Text="Loading library…" FontSize="13"
|
||
Foreground="{StaticResource MutedBrush}" HorizontalAlignment="Center" />
|
||
</StackPanel>
|
||
</Panel>
|
||
|
||
<!-- Options page: sub-tabs so future categories (Graphics, …) slot
|
||
in next to General. Card-grouped sections match the visual
|
||
language used by the console panel and launch bar below. -->
|
||
<Grid x:Name="OptionsPage" IsVisible="False">
|
||
<TabControl>
|
||
<TabItem x:Name="GeneralTabItem" Header="General" FontSize="15">
|
||
<ScrollViewer>
|
||
<StackPanel Margin="0,14,0,16" Spacing="16" MaxWidth="1280" HorizontalAlignment="Left">
|
||
|
||
<Border Classes="card">
|
||
<StackPanel Spacing="14">
|
||
<TextBlock x:Name="EmulationSectionTitle" Classes="sectionTitle" Text="EMULATION" />
|
||
|
||
<local:SettingRow x:Name="CpuEngineRow" Label="CPU engine"
|
||
Description="Execution engine used to run game code.">
|
||
<ComboBox x:Name="CpuEngineBox" Width="160" SelectedIndex="0"
|
||
VerticalAlignment="Center" CornerRadius="8">
|
||
<ComboBoxItem x:Name="CpuEngineNativeItem" Content="Native" />
|
||
</ComboBox>
|
||
</local:SettingRow>
|
||
|
||
<local:SettingRow x:Name="StrictRow" Label="Strict dynlib resolution"
|
||
Description="Fail the launch when an imported symbol cannot be resolved.">
|
||
<ToggleSwitch x:Name="StrictToggle" OnContent="On" OffContent="Off"
|
||
VerticalAlignment="Center" />
|
||
</local:SettingRow>
|
||
</StackPanel>
|
||
</Border>
|
||
|
||
<Border Classes="card">
|
||
<StackPanel Spacing="14">
|
||
<TextBlock x:Name="LoggingSectionTitle" Classes="sectionTitle" Text="LOGGING" />
|
||
|
||
<local:SettingRow x:Name="LogLevelRow" Label="Log level"
|
||
Description="Verbosity of the emulator console output.">
|
||
<ComboBox x:Name="LogLevelBox" Width="160" SelectedIndex="2"
|
||
VerticalAlignment="Center" CornerRadius="8">
|
||
<ComboBoxItem x:Name="LogLevelTraceItem" Content="Trace" />
|
||
<ComboBoxItem x:Name="LogLevelDebugItem" Content="Debug" />
|
||
<ComboBoxItem x:Name="LogLevelInfoItem" Content="Info" />
|
||
<ComboBoxItem x:Name="LogLevelWarningItem" Content="Warning" />
|
||
<ComboBoxItem x:Name="LogLevelErrorItem" Content="Error" />
|
||
<ComboBoxItem x:Name="LogLevelCriticalItem" Content="Critical" />
|
||
</ComboBox>
|
||
</local:SettingRow>
|
||
|
||
<local:SettingRow x:Name="TraceImportsRow" Label="Import trace limit"
|
||
Description="Trace the first N imports per module (0 = off).">
|
||
<NumericUpDown x:Name="TraceImportsBox" Width="160" Minimum="0"
|
||
Maximum="4096" Increment="16" Value="0" FormatString="0"
|
||
VerticalAlignment="Center" CornerRadius="8" />
|
||
</local:SettingRow>
|
||
|
||
<local:SettingRow x:Name="LogToFileRow" Label="Log to file"
|
||
Description="Mirror emulator output to a log file.">
|
||
<ToggleSwitch x:Name="LogToFileToggle" OnContent="On" OffContent="Off"
|
||
VerticalAlignment="Center" />
|
||
</local:SettingRow>
|
||
|
||
<local:SettingRow x:Name="LogFilePathRow" Label="Log file path"
|
||
Description="No custom path">
|
||
<Button x:Name="SelectLogFilePathButton" Classes="ghost" Content="Select…"
|
||
VerticalAlignment="Center" />
|
||
</local:SettingRow>
|
||
|
||
<local:SettingRow x:Name="OverrideLogFileRow" Label="Override log file"
|
||
Description="Use the exact file path instead of appending title ID and timestamp.">
|
||
<ToggleSwitch x:Name="OverrideLogFileToggle" OnContent="On" OffContent="Off"
|
||
VerticalAlignment="Center" />
|
||
</local:SettingRow>
|
||
</StackPanel>
|
||
</Border>
|
||
|
||
<Border Classes="card">
|
||
<StackPanel Spacing="14">
|
||
<TextBlock x:Name="LauncherSectionTitle" Classes="sectionTitle" Text="LAUNCHER" />
|
||
|
||
<local:SettingRow x:Name="LanguageRow" Label="Emulator language"
|
||
Description="Language used throughout the launcher. Applies immediately.">
|
||
<ComboBox x:Name="LanguageBox" Width="160"
|
||
VerticalAlignment="Center" CornerRadius="8"
|
||
DisplayMemberBinding="{Binding NativeName}" />
|
||
</local:SettingRow>
|
||
|
||
<local:SettingRow x:Name="TitleMusicRow" Label="Title music"
|
||
Description="Loop the selected game's preview music in the library.">
|
||
<ToggleSwitch x:Name="TitleMusicToggle" OnContent="On" OffContent="Off"
|
||
IsChecked="True" VerticalAlignment="Center" />
|
||
</local:SettingRow>
|
||
|
||
<local:SettingRow x:Name="DiscordRow" Label="Discord presence"
|
||
Description="Show the running game on your Discord profile.">
|
||
<ToggleSwitch x:Name="DiscordToggle" OnContent="On" OffContent="Off"
|
||
VerticalAlignment="Center" />
|
||
</local:SettingRow>
|
||
|
||
<local:SettingRow x:Name="AutoUpdateRow" Label="Check for updates on startup"
|
||
Description="Checks GitHub without delaying startup.">
|
||
<ToggleSwitch x:Name="AutoUpdateToggle" OnContent="On" OffContent="Off"
|
||
IsChecked="True" VerticalAlignment="Center" />
|
||
</local:SettingRow>
|
||
</StackPanel>
|
||
</Border>
|
||
<Border Classes="card">
|
||
<StackPanel Spacing="14">
|
||
<TextBlock x:Name="AboutSectionTitle"
|
||
Classes="sectionTitle"
|
||
Text="ABOUT" />
|
||
|
||
<!--Latest commit info-->
|
||
<Grid ColumnDefinitions="*,Auto">
|
||
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
||
<Image Source="avares://SharpEmu.GUI/Assets/commit-icon.png"
|
||
Width="24" Height="24" VerticalAlignment="Center" />
|
||
<StackPanel Spacing="2" VerticalAlignment="Center">
|
||
<TextBlock x:Name="LatestCommitLabel" Text="Latest commit" FontSize="13"/>
|
||
<TextBlock x:Name="LatestCommitDescription" Text="Latest commit on the main branch" FontSize="11" Foreground="{StaticResource MutedBrush}"/>
|
||
</StackPanel>
|
||
</StackPanel>
|
||
|
||
<Button Grid.Column="1" x:Name="LatestCommitHashText" Classes="ghost"
|
||
Content="Loading…" FontSize="12" FontFamily="Consolas,monospace"
|
||
VerticalAlignment="Center" IsEnabled="False" />
|
||
|
||
</Grid>
|
||
|
||
<!--Update-->
|
||
<Grid ColumnDefinitions="*,Auto">
|
||
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
||
<Image Source="avares://SharpEmu.GUI/Assets/update-icon.png"
|
||
Width="24"
|
||
Height="24"
|
||
VerticalAlignment="Center" />
|
||
<StackPanel Spacing="2" VerticalAlignment="Center">
|
||
<TextBlock x:Name="UpdateLabel" Text="Updates" FontSize="13" />
|
||
<TextBlock x:Name="UpdateStatusText" Text="Current build: dev" FontSize="11"
|
||
Foreground="{StaticResource MutedBrush}" TextWrapping="Wrap" />
|
||
</StackPanel>
|
||
</StackPanel>
|
||
<Button Grid.Column="1" x:Name="UpdateButton" Classes="ghost"
|
||
Content="Check for updates" VerticalAlignment="Center" />
|
||
</Grid>
|
||
|
||
<!--Github-->
|
||
<Grid ColumnDefinitions="*,Auto">
|
||
<StackPanel Grid.Column="0"
|
||
Orientation="Horizontal"
|
||
Spacing="8"
|
||
VerticalAlignment="Center">
|
||
<Image Source="avares://SharpEmu.GUI/Assets/github.png"
|
||
Width="20"
|
||
Height="20"
|
||
VerticalAlignment="Center" />
|
||
<StackPanel VerticalAlignment="Center"
|
||
Spacing="2">
|
||
<TextBlock x:Name="GithubLabel"
|
||
Text="GitHub"
|
||
FontSize="13" />
|
||
<TextBlock x:Name="GithubDesc"
|
||
Text="Source code, issues and project development."
|
||
FontSize="11"
|
||
Foreground="{StaticResource MutedBrush}"
|
||
TextWrapping="Wrap" />
|
||
</StackPanel>
|
||
</StackPanel>
|
||
|
||
<Button Grid.Column="1"
|
||
x:Name="GithubButton"
|
||
Classes="ghost"
|
||
Content="Open"
|
||
VerticalAlignment="Center" />
|
||
</Grid>
|
||
|
||
<!--Discord-->
|
||
<Grid ColumnDefinitions="*,Auto">
|
||
<StackPanel Grid.Column="0"
|
||
Orientation="Horizontal"
|
||
Spacing="8"
|
||
VerticalAlignment="Center">
|
||
<Image Source="avares://SharpEmu.GUI/Assets/discord.png"
|
||
Width="20"
|
||
Height="20"
|
||
VerticalAlignment="Center" />
|
||
<StackPanel VerticalAlignment="Center"
|
||
Spacing="2">
|
||
<TextBlock x:Name="DiscordServerLabel"
|
||
Text="Discord"
|
||
FontSize="13" />
|
||
<TextBlock x:Name="DiscordServerDesc"
|
||
Text="Join the community, get support and follow development."
|
||
FontSize="11"
|
||
Foreground="{StaticResource MutedBrush}"
|
||
TextWrapping="Wrap" />
|
||
</StackPanel>
|
||
</StackPanel>
|
||
|
||
<Button Grid.Column="1"
|
||
x:Name="DiscordButton"
|
||
Classes="ghost"
|
||
Content="Join"
|
||
VerticalAlignment="Center" />
|
||
</Grid>
|
||
</StackPanel>
|
||
</Border>
|
||
</StackPanel>
|
||
</ScrollViewer>
|
||
</TabItem>
|
||
<TabItem x:Name="EnvTabItem" Header="Environment" FontSize="15">
|
||
<ScrollViewer>
|
||
<StackPanel Margin="0,14,0,16" Spacing="16" MaxWidth="1280" HorizontalAlignment="Left">
|
||
|
||
<Border Classes="card">
|
||
<StackPanel Spacing="14">
|
||
<TextBlock x:Name="EnvSectionTitle" Classes="sectionTitle" Text="ENVIRONMENT VARIABLES" />
|
||
<TextBlock x:Name="EnvDesc"
|
||
Text="Switches passed to the emulator as environment variables at launch."
|
||
FontSize="11" Foreground="{StaticResource MutedBrush}" TextWrapping="Wrap" />
|
||
|
||
<local:SettingRow x:Name="EnvBthidRow" LabelFontFamily="Consolas,monospace" Label="SHARPEMU_BTHID_UNAVAILABLE"
|
||
Description="Report Bluetooth HID as unavailable for titles whose wheel/FFB middleware polls forever. Leave off normally. Some titles freeze when init fails.">
|
||
<ToggleSwitch x:Name="EnvBthidToggle" OnContent="On" OffContent="Off"
|
||
VerticalAlignment="Center" />
|
||
</local:SettingRow>
|
||
|
||
<local:SettingRow x:Name="EnvLoopGuardRow" LabelFontFamily="Consolas,monospace" Label="SHARPEMU_DISABLE_IMPORT_LOOP_GUARD"
|
||
Description="Do not force quit titles that repeat the same call for too long. Try this when a game exits on its own while loading.">
|
||
<ToggleSwitch x:Name="EnvLoopGuardToggle" OnContent="On" OffContent="Off"
|
||
VerticalAlignment="Center" />
|
||
</local:SettingRow>
|
||
|
||
<local:SettingRow x:Name="EnvWritableApp0Row" LabelFontFamily="Consolas,monospace" Label="SHARPEMU_WRITABLE_APP0"
|
||
Description="Allow titles to create and write files inside their install folder. Needed by unpackaged dumps that write their save or config data under /app0.">
|
||
<ToggleSwitch x:Name="EnvWritableApp0Toggle" OnContent="On" OffContent="Off"
|
||
VerticalAlignment="Center" />
|
||
</local:SettingRow>
|
||
|
||
<local:SettingRow x:Name="EnvVkValidationRow" LabelFontFamily="Consolas,monospace" Label="SHARPEMU_VK_VALIDATION"
|
||
Description="Enable Vulkan validation layers for GPU debugging. Slow. Requires the Vulkan SDK to be installed.">
|
||
<ToggleSwitch x:Name="EnvVkValidationToggle" OnContent="On" OffContent="Off"
|
||
VerticalAlignment="Center" />
|
||
</local:SettingRow>
|
||
|
||
<local:SettingRow x:Name="EnvDumpSpirvRow" LabelFontFamily="Consolas,monospace" Label="SHARPEMU_DUMP_SPIRV"
|
||
Description="Dump AGC shaders and their SPIR-V translations to the shader-dumps folder. Use when reporting shader or rendering bugs.">
|
||
<ToggleSwitch x:Name="EnvDumpSpirvToggle" OnContent="On" OffContent="Off"
|
||
VerticalAlignment="Center" />
|
||
</local:SettingRow>
|
||
|
||
<local:SettingRow x:Name="EnvLogDirectMemoryRow" LabelFontFamily="Consolas,monospace" Label="SHARPEMU_LOG_DIRECT_MEMORY"
|
||
Description="Log direct memory allocations and failures to the console. Use when a game aborts or exits during boot.">
|
||
<ToggleSwitch x:Name="EnvLogDirectMemoryToggle" OnContent="On" OffContent="Off"
|
||
VerticalAlignment="Center" />
|
||
</local:SettingRow>
|
||
|
||
<local:SettingRow x:Name="EnvLogIoRow" LabelFontFamily="Consolas,monospace" Label="SHARPEMU_LOG_IO"
|
||
Description="Log file open, read, and path-resolve activity to the console. Use when a game cannot find its data files during boot.">
|
||
<ToggleSwitch x:Name="EnvLogIoToggle" OnContent="On" OffContent="Off"
|
||
VerticalAlignment="Center" />
|
||
</local:SettingRow>
|
||
|
||
<local:SettingRow x:Name="EnvLogNpRow" LabelFontFamily="Consolas,monospace" Label="SHARPEMU_LOG_NP"
|
||
Description="Log NP (PlayStation Network) library calls to the console.">
|
||
<ToggleSwitch x:Name="EnvLogNpToggle" OnContent="On" OffContent="Off"
|
||
VerticalAlignment="Center" />
|
||
</local:SettingRow>
|
||
</StackPanel>
|
||
</Border>
|
||
|
||
</StackPanel>
|
||
</ScrollViewer>
|
||
</TabItem>
|
||
</TabControl>
|
||
</Grid>
|
||
</Panel>
|
||
|
||
<!-- Console (collapsible) -->
|
||
<Border Grid.Row="2" x:Name="ConsolePanel" Classes="card" Padding="0" Height="240"
|
||
Margin="0,12,0,0" IsVisible="False">
|
||
<Grid RowDefinitions="Auto,*">
|
||
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto,Auto,Auto,Auto" Margin="16,12,16,8">
|
||
<TextBlock x:Name="ConsoleSectionTitle" Classes="sectionTitle" Text="CONSOLE" VerticalAlignment="Center" />
|
||
<TextBox Grid.Column="1" FontSize="12" Margin="0,0,12,0" x:Name="ConsoleSearchBox"
|
||
Watermark="Search..." Width="320" />
|
||
<CheckBox Grid.Column="2" x:Name="AutoScrollCheck" Content="Auto-scroll" IsChecked="True"
|
||
FontSize="12" Margin="0,0,12,0" />
|
||
<Button Grid.Column="3" x:Name="DetachConsoleButton" Classes="ghost" Content="Split" FontSize="12"
|
||
Padding="10,4" Margin="0,0,8,0" />
|
||
<Button Grid.Column="4" x:Name="CopyLogButton" Classes="ghost" Content="Copy" FontSize="12"
|
||
Padding="10,4" Margin="0,0,8,0" />
|
||
<Button Grid.Column="5" x:Name="ClearLogButton" Classes="ghost" Content="Clear" FontSize="12"
|
||
Padding="10,4" />
|
||
</Grid>
|
||
<ListBox Grid.Row="1" x:Name="ConsoleList" Classes="console" BorderThickness="0,1,0,0"
|
||
BorderBrush="{StaticResource CardBorderBrush}" CornerRadius="0,0,12,12">
|
||
<ListBox.ItemTemplate>
|
||
<DataTemplate>
|
||
<TextBlock Text="{Binding Text}" Foreground="{Binding Brush}" TextWrapping="NoWrap" />
|
||
</DataTemplate>
|
||
</ListBox.ItemTemplate>
|
||
</ListBox>
|
||
</Grid>
|
||
</Border>
|
||
|
||
<!-- Launch bar; capped so it does not sprawl on a maximized window. -->
|
||
<Border Grid.Row="3" x:Name="LaunchBar" Classes="card" Margin="0,12,0,0" Padding="14" MaxWidth="1280">
|
||
<StackPanel Spacing="14">
|
||
<Grid ColumnDefinitions="Auto,*,Auto">
|
||
|
||
<!-- Selected game cover thumbnail -->
|
||
<Border Grid.Column="0" Classes="coverClip" Width="56" Height="56" CornerRadius="8"
|
||
VerticalAlignment="Center">
|
||
<Panel x:Name="SelectedCoverPanel">
|
||
<Border Background="{Binding PlaceholderBrush, FallbackValue={x:Null}}"
|
||
IsVisible="{Binding !HasCover, FallbackValue=False}">
|
||
<TextBlock Text="{Binding Initials}" FontSize="20" FontWeight="Bold"
|
||
Foreground="#E8ECF4" Opacity="0.85"
|
||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||
</Border>
|
||
<Image Source="{Binding Cover}" Stretch="UniformToFill"
|
||
IsVisible="{Binding HasCover, FallbackValue=False}" />
|
||
</Panel>
|
||
</Border>
|
||
|
||
<StackPanel Grid.Column="1" Spacing="4" VerticalAlignment="Center" Margin="14,0,14,0">
|
||
<Grid ColumnDefinitions="Auto,Auto,*">
|
||
<TextBlock Grid.Column="0" x:Name="SelectedGameTitle" Text="No game selected" FontSize="16"
|
||
FontWeight="Bold" TextTrimming="CharacterEllipsis" VerticalAlignment="Center"
|
||
MaxWidth="340" Margin="0,0,10,0" />
|
||
|
||
<!-- Title id / version / size badges, right next to the
|
||
title. The title's own MaxWidth (not a "*" column) is
|
||
what keeps them from drifting to the far right. -->
|
||
<StackPanel Grid.Column="1" x:Name="SelectedBadgesRow" Orientation="Horizontal" Spacing="6"
|
||
IsVisible="False" VerticalAlignment="Center">
|
||
<Border Classes="pill" IsVisible="{Binding HasTitleId, FallbackValue=False}">
|
||
<TextBlock Text="{Binding TitleId}" FontSize="10" FontWeight="SemiBold"
|
||
Foreground="{StaticResource MutedBrush}" />
|
||
</Border>
|
||
<Border Classes="pill" IsVisible="{Binding HasVersion, FallbackValue=False}">
|
||
<TextBlock Text="{Binding VersionText}" FontSize="10" FontWeight="SemiBold"
|
||
Foreground="{StaticResource MutedBrush}" />
|
||
</Border>
|
||
<Border Classes="pill">
|
||
<TextBlock Text="{Binding SizeText, FallbackValue=''}" FontSize="10" FontWeight="SemiBold"
|
||
Foreground="{StaticResource MutedBrush}" />
|
||
</Border>
|
||
</StackPanel>
|
||
</Grid>
|
||
|
||
<TextBlock x:Name="SelectedGamePath" Text="Pick a game from the library, or open an eboot.bin directly."
|
||
FontSize="11" Foreground="{StaticResource MutedBrush}" TextTrimming="CharacterEllipsis" />
|
||
<StackPanel Orientation="Horizontal" Spacing="7">
|
||
<Ellipse x:Name="StatusDot" Width="8" Height="8" Fill="{StaticResource FaintBrush}"
|
||
VerticalAlignment="Center" />
|
||
<TextBlock x:Name="StatusText" Text="Idle" FontSize="11" Foreground="{StaticResource MutedBrush}"
|
||
VerticalAlignment="Center" />
|
||
</StackPanel>
|
||
</StackPanel>
|
||
|
||
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
||
<ToggleButton x:Name="ConsoleToggle" Classes="ghost" Content="≡ Console" />
|
||
<Button x:Name="LaunchButton" Classes="accent" Content="▶ Launch" IsEnabled="False" />
|
||
<Button x:Name="StopButton" Classes="danger" Content="■ Stop" IsEnabled="False" />
|
||
</StackPanel>
|
||
</Grid>
|
||
</StackPanel>
|
||
</Border>
|
||
</Grid>
|
||
|
||
<!-- Avalonia's regular overlay layer cannot appear over a native child
|
||
HWND/X11/Metal surface. Keep the running-session controls in a native
|
||
popup so the game reaches the bottom status bar without losing Stop. -->
|
||
<primitives:Popup x:Name="SessionBarPopup"
|
||
IsOpen="False"
|
||
PlacementTarget="{Binding #GameView}"
|
||
Placement="Bottom"
|
||
VerticalOffset="-66"
|
||
Topmost="True"
|
||
ShouldUseOverlayLayer="False"
|
||
TakesFocusFromNativeControl="False"
|
||
IsLightDismissEnabled="False">
|
||
<Border Classes="card" Width="598" Height="58" CornerRadius="16" Padding="14,8">
|
||
<Grid ColumnDefinitions="*,Auto">
|
||
<StackPanel Spacing="3" VerticalAlignment="Center">
|
||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||
<TextBlock x:Name="SessionGameTitle" Text="GAME RUNNING" FontSize="13" FontWeight="SemiBold"
|
||
MaxWidth="240" TextTrimming="CharacterEllipsis" VerticalAlignment="Center" />
|
||
<Border Classes="badge running" VerticalAlignment="Center">
|
||
<TextBlock Text="RUNNING" FontSize="9" FontWeight="Bold" LetterSpacing="1"
|
||
Foreground="{StaticResource SuccessBrush}" />
|
||
</Border>
|
||
</StackPanel>
|
||
<StackPanel Orientation="Horizontal" Spacing="7">
|
||
<Border x:Name="SessionF11Badge" Classes="badge key" VerticalAlignment="Center">
|
||
<TextBlock Text="F11" FontSize="9" FontWeight="Bold"
|
||
Foreground="{StaticResource InfoBrush}" />
|
||
</Border>
|
||
<TextBlock x:Name="SessionHintText" Text="Fullscreen" FontSize="11"
|
||
Foreground="{StaticResource MutedBrush}" VerticalAlignment="Center" />
|
||
</StackPanel>
|
||
</StackPanel>
|
||
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
||
<Button x:Name="SessionConsoleButton" Classes="ghost" Content="≡ Console" />
|
||
<Button x:Name="SessionStopButton" Classes="danger" Content="■ Stop" IsEnabled="False" />
|
||
</StackPanel>
|
||
</Grid>
|
||
</Border>
|
||
</primitives:Popup>
|
||
|
||
<!-- This is a native popup rather than an Avalonia overlay because the
|
||
emulated Vulkan surface is a native child window. -->
|
||
<!-- Anchored to MainContent, not GameView: the surface host is parked in
|
||
a 1x1 corner while loading/closing, which would pull a GameView-
|
||
anchored popup into the corner with it. -->
|
||
<primitives:Popup x:Name="SessionLoadingPopup"
|
||
IsOpen="False"
|
||
PlacementTarget="{Binding #MainContent}"
|
||
Placement="Center"
|
||
Topmost="True"
|
||
ShouldUseOverlayLayer="False"
|
||
TakesFocusFromNativeControl="False"
|
||
IsLightDismissEnabled="False">
|
||
<Border Classes="card" Width="380" CornerRadius="18" Padding="22,18">
|
||
<StackPanel Spacing="12">
|
||
<TextBlock x:Name="SessionLoadingTitle" Text="Loading game" FontSize="16" FontWeight="SemiBold" />
|
||
<TextBlock x:Name="SessionLoadingDetail" Text="Preparing the emulation session..." FontSize="12"
|
||
Foreground="{StaticResource MutedBrush}" TextTrimming="CharacterEllipsis" />
|
||
<ProgressBar IsIndeterminate="True" Height="5" />
|
||
</StackPanel>
|
||
</Border>
|
||
</primitives:Popup>
|
||
|
||
<!-- Status bar -->
|
||
<Grid x:Name="StatusBar" Grid.Row="2" Height="32" Background="{StaticResource ChromeBrush}"
|
||
ColumnDefinitions="*,Auto">
|
||
<TextBlock x:Name="EmulatorPathText" Grid.Column="0" Text="Emulator: locating…" FontSize="11"
|
||
Foreground="{StaticResource FaintBrush}" VerticalAlignment="Center" Margin="16,0"
|
||
TextTrimming="CharacterEllipsis" />
|
||
<TextBlock x:Name="StatusBarRight" Grid.Column="1" Text="" FontSize="11"
|
||
Foreground="{StaticResource FaintBrush}" VerticalAlignment="Center" Margin="16,0" />
|
||
</Grid>
|
||
</Grid>
|
||
</Window>
|