diff --git a/Torch.Server/Views/TorchUI.xaml.cs b/Torch.Server/Views/TorchUI.xaml.cs index 26a7294..88c97a0 100644 --- a/Torch.Server/Views/TorchUI.xaml.cs +++ b/Torch.Server/Views/TorchUI.xaml.cs @@ -10,6 +10,7 @@ using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; +using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; @@ -31,6 +32,8 @@ namespace Torch.Server private TorchServer _server; private TorchConfig _config; + private bool _autoscrollLog = true; + public TorchUI(TorchServer server) { WindowStartupLocation = WindowStartupLocation.CenterScreen; @@ -57,6 +60,14 @@ namespace Torch.Server Themes.uiSource = this; Themes.SetConfig(_config); Title = $"{_config.InstanceName} - Torch {server.TorchVersion}, SE {server.GameVersion}"; + + Loaded += TorchUI_Loaded; + } + + private void TorchUI_Loaded(object sender, RoutedEventArgs e) + { + var scrollViewer = FindDescendant(ConsoleText); + scrollViewer.ScrollChanged += ConsoleText_OnScrollChanged; } private void AttachConsole() @@ -69,7 +80,52 @@ namespace Torch.Server doc = (wrapped?.WrappedTarget as FlowDocumentTarget)?.Document; } ConsoleText.Document = doc ?? new FlowDocument(new Paragraph(new Run("No target!"))); - ConsoleText.TextChanged += (sender, args) => ConsoleText.ScrollToEnd(); + ConsoleText.TextChanged += ConsoleText_OnTextChanged; + } + + public static T FindDescendant(DependencyObject obj) where T : DependencyObject + { + if (obj == null) return default(T); + int numberChildren = VisualTreeHelper.GetChildrenCount(obj); + if (numberChildren == 0) return default(T); + + for (int i = 0; i < numberChildren; i++) + { + DependencyObject child = VisualTreeHelper.GetChild(obj, i); + if (child is T) + { + return (T)child; + } + } + + for (int i = 0; i < numberChildren; i++) + { + DependencyObject child = VisualTreeHelper.GetChild(obj, i); + var potentialMatch = FindDescendant(child); + if (potentialMatch != default(T)) + { + return potentialMatch; + } + } + + return default(T); + } + + private void ConsoleText_OnTextChanged(object sender, TextChangedEventArgs args) + { + var textBox = (RichTextBox) sender; + if (_autoscrollLog) + ConsoleText.ScrollToEnd(); + } + + private void ConsoleText_OnScrollChanged(object sender, ScrollChangedEventArgs e) + { + var scrollViewer = (ScrollViewer) sender; + if (e.ExtentHeightChange == 0) + { + // User change. + _autoscrollLog = scrollViewer.VerticalOffset == scrollViewer.ScrollableHeight; + } } public void LoadConfig(TorchConfig config)