mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-16 00:40:59 +00:00
Merge branch 'main' of https://github.com/par274/sharpemu
This commit is contained in:
150
src/SharpEmu.GUI/ConsoleWindow.cs
Normal file
150
src/SharpEmu.GUI/ConsoleWindow.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using Avalonia;
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Threading;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace SharpEmu.GUI;
|
||||
|
||||
public sealed class ConsoleWindow : Window
|
||||
{
|
||||
private readonly AvaloniaList<LogLine> _sourceLines;
|
||||
private readonly AvaloniaList<LogLine> _visibleLines = new();
|
||||
private readonly ListBox _list;
|
||||
private readonly TextBox _searchBox;
|
||||
private readonly CheckBox _autoScrollCheck;
|
||||
|
||||
public ConsoleWindow(
|
||||
AvaloniaList<LogLine> lines,
|
||||
Action clear,
|
||||
bool autoScroll)
|
||||
{
|
||||
_sourceLines = lines;
|
||||
Title = "SharpEmu Console";
|
||||
Width = 980;
|
||||
Height = 620;
|
||||
MinWidth = 520;
|
||||
MinHeight = 320;
|
||||
Background = new SolidColorBrush(Color.Parse("#0D1017"));
|
||||
Icon = new WindowIcon(AssetLoader.Open(new Uri("avares://SharpEmu.GUI/Assets/SharpEmu.ico")));
|
||||
|
||||
_searchBox = new TextBox { Watermark = "Search...", Width = 320, Margin = new Thickness(0, 0, 12, 0) };
|
||||
_autoScrollCheck = new CheckBox
|
||||
{
|
||||
Content = "Auto-scroll",
|
||||
IsChecked = autoScroll,
|
||||
FontSize = 12,
|
||||
Margin = new Thickness(0, 0, 12, 0),
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
};
|
||||
var copyButton = new Button
|
||||
{
|
||||
Classes = { "ghost" },
|
||||
Content = "Copy",
|
||||
Padding = new Thickness(10, 4),
|
||||
Margin = new Thickness(0, 0, 8, 0),
|
||||
};
|
||||
var clearButton = new Button { Classes = { "ghost" }, Content = "Clear", Padding = new Thickness(10, 4) };
|
||||
copyButton.Click += async (_, _) => await CopyAsync();
|
||||
clearButton.Click += (_, _) => clear();
|
||||
_searchBox.TextChanged += (_, _) => RefreshVisibleLines();
|
||||
|
||||
_list = new ListBox
|
||||
{
|
||||
Classes = { "console" },
|
||||
ItemsSource = _visibleLines,
|
||||
BorderThickness = new Thickness(1),
|
||||
BorderBrush = new SolidColorBrush(Color.Parse("#232B3A")),
|
||||
ItemTemplate = new FuncDataTemplate<LogLine>((_, _) =>
|
||||
{
|
||||
var text = new TextBlock { TextWrapping = TextWrapping.NoWrap };
|
||||
text.Bind(TextBlock.TextProperty, new Binding(nameof(LogLine.Text)));
|
||||
text.Bind(TextBlock.ForegroundProperty, new Binding(nameof(LogLine.Brush)));
|
||||
return text;
|
||||
}),
|
||||
};
|
||||
|
||||
Content = new Grid
|
||||
{
|
||||
Margin = new Thickness(12),
|
||||
RowDefinitions = new RowDefinitions("Auto,*"),
|
||||
Children =
|
||||
{
|
||||
new Grid
|
||||
{
|
||||
Margin = new Thickness(0, 0, 0, 8),
|
||||
ColumnDefinitions = new ColumnDefinitions("*,Auto,Auto,Auto,Auto"),
|
||||
Children =
|
||||
{
|
||||
new TextBlock
|
||||
{
|
||||
Classes = { "sectionTitle" },
|
||||
Text = "CONSOLE",
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
},
|
||||
_searchBox.WithGridColumn(1),
|
||||
_autoScrollCheck.WithGridColumn(2),
|
||||
copyButton.WithGridColumn(3),
|
||||
clearButton.WithGridColumn(4),
|
||||
},
|
||||
},
|
||||
_list.WithGridRow(1),
|
||||
},
|
||||
};
|
||||
|
||||
lines.CollectionChanged += OnLinesChanged;
|
||||
Closed += (_, _) => lines.CollectionChanged -= OnLinesChanged;
|
||||
RefreshVisibleLines();
|
||||
}
|
||||
|
||||
private void OnLinesChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
RefreshVisibleLines();
|
||||
if (_autoScrollCheck.IsChecked == true)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() => (_list.Scroll as ScrollViewer)?.ScrollToEnd());
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshVisibleLines()
|
||||
{
|
||||
var query = _searchBox.Text ?? string.Empty;
|
||||
_visibleLines.Clear();
|
||||
_visibleLines.AddRange(string.IsNullOrWhiteSpace(query)
|
||||
? _sourceLines
|
||||
: _sourceLines.Where(line => line.Text.Contains(query, StringComparison.OrdinalIgnoreCase)));
|
||||
}
|
||||
|
||||
private async Task CopyAsync()
|
||||
{
|
||||
if (_visibleLines.Count == 0 || Clipboard is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await Clipboard.SetTextAsync(string.Join(Environment.NewLine, _visibleLines.Select(line => line.Text)));
|
||||
}
|
||||
}
|
||||
|
||||
file static class GridExtensions
|
||||
{
|
||||
public static T WithGridColumn<T>(this T control, int column) where T : Control
|
||||
{
|
||||
Grid.SetColumn(control, column);
|
||||
return control;
|
||||
}
|
||||
|
||||
public static T WithGridRow<T>(this T control, int row) where T : Control
|
||||
{
|
||||
Grid.SetRow(control, row);
|
||||
return control;
|
||||
}
|
||||
}
|
||||
@@ -160,15 +160,17 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
<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" Margin="16,12,16,8">
|
||||
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto,Auto,Auto,Auto" Margin="16,12,16,8">
|
||||
<TextBlock Classes="sectionTitle" Text="CONSOLE" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Column="1" FontSize="12" Margin="0,0,12,0" x:Name="ConsoleSearchBox"
|
||||
Watermark="Search..." MaxWidth="5500" />
|
||||
<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="CopyLogButton" Classes="ghost" Content="Copy" FontSize="12"
|
||||
<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="ClearLogButton" Classes="ghost" Content="Clear" FontSize="12"
|
||||
<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"
|
||||
|
||||
@@ -42,6 +42,7 @@ public partial class MainWindow : Window
|
||||
|
||||
private GuiSettings _settings = new();
|
||||
private EmulatorProcess? _emulator;
|
||||
private ConsoleWindow? _consoleWindow;
|
||||
private StreamWriter? _fileLog;
|
||||
private readonly SndPreviewPlayer _sndPreview = new();
|
||||
private string? _emulatorExePath;
|
||||
@@ -98,8 +99,9 @@ public partial class MainWindow : Window
|
||||
StopButton.Click += (_, _) => StopEmulator();
|
||||
ClearLogButton.Click += (_, _) => _consoleLines.Clear();
|
||||
CopyLogButton.Click += async (_, _) => await CopyConsoleAsync();
|
||||
DetachConsoleButton.Click += (_, _) => ShowConsoleWindow();
|
||||
OptionsToggle.IsCheckedChanged += (_, _) => OptionsPanel.IsVisible = OptionsToggle.IsChecked == true;
|
||||
ConsoleToggle.IsCheckedChanged += (_, _) => ConsolePanel.IsVisible = ConsoleToggle.IsChecked == true;
|
||||
ConsoleToggle.IsCheckedChanged += (_, _) => ConsolePanel.IsVisible = ConsoleToggle.IsChecked == true && _consoleWindow is null;
|
||||
SelectLogFilePathButton.Click += async (_, _) => await SelectFilePathAsync();
|
||||
TitleMusicToggle.IsCheckedChanged += (_, _) => OnTitleMusicToggled();
|
||||
DiscordToggle.IsCheckedChanged += (_, _) =>
|
||||
@@ -329,6 +331,7 @@ public partial class MainWindow : Window
|
||||
_gamepadTimer.Stop();
|
||||
_sndPreview.Stop();
|
||||
_discord?.Dispose();
|
||||
_consoleWindow?.Close();
|
||||
_emulator?.Dispose();
|
||||
DropFileLog();
|
||||
}
|
||||
@@ -1470,4 +1473,28 @@ public partial class MainWindow : Window
|
||||
var text = string.Join(Environment.NewLine, _consoleLines.Select(line => line.Text));
|
||||
await Clipboard.SetTextAsync(text);
|
||||
}
|
||||
|
||||
private void ShowConsoleWindow()
|
||||
{
|
||||
if (_consoleWindow is { } window)
|
||||
{
|
||||
window.Activate();
|
||||
return;
|
||||
}
|
||||
|
||||
ConsoleSearchBox.Text = string.Empty;
|
||||
ConsoleToggle.IsChecked = false;
|
||||
ConsolePanel.IsVisible = false;
|
||||
_consoleWindow = new ConsoleWindow(
|
||||
_consoleLines,
|
||||
() => { _consoleLines.Clear(); _allConsoleLines.Clear(); },
|
||||
AutoScrollCheck.IsChecked == true);
|
||||
_consoleWindow.Closed += (_, _) =>
|
||||
{
|
||||
_consoleWindow = null;
|
||||
ConsoleToggle.IsChecked = true;
|
||||
ConsolePanel.IsVisible = true;
|
||||
};
|
||||
_consoleWindow.Show(this);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user