diff --git a/CHANGELIST.md b/CHANGELIST.md index 4ee1acee..72546337 100644 --- a/CHANGELIST.md +++ b/CHANGELIST.md @@ -28,6 +28,7 @@ - Remove unnecessary include - Move element items to Core - Move LogLevel enumeration +- Split logging code a bit more ### 2.6.6 (2023-10-04) diff --git a/MPF.UI.Core/UserControls/LogOutput.xaml.cs b/MPF.UI.Core/UserControls/LogOutput.xaml.cs index 80ea3d6c..79408a2f 100644 --- a/MPF.UI.Core/UserControls/LogOutput.xaml.cs +++ b/MPF.UI.Core/UserControls/LogOutput.xaml.cs @@ -1,4 +1,7 @@ -using System.Windows.Controls; +using System; +using System.Windows; +using System.Windows.Controls; +using MPF.Core.Data; using MPF.UI.Core.ViewModels; namespace MPF.UI.Core.UserControls @@ -6,14 +9,130 @@ namespace MPF.UI.Core.UserControls public partial class LogOutput : UserControl { /// - /// Read-only access to the current log view model + /// Read-only access to the current log output view model /// - public LogViewModel LogViewModel => DataContext as LogViewModel; + public LogOutputViewModel LogOutputViewModel => DataContext as LogOutputViewModel; public LogOutput() { InitializeComponent(); - DataContext = new LogViewModel(this); + DataContext = new LogOutputViewModel(); + + // Add handlers + OutputViewer.SizeChanged += OutputViewerSizeChanged; + Output.TextChanged += OnTextChanged; + ClearButton.Click += OnClearButton; + SaveButton.Click += OnSaveButton; + + // Update the internal state + Output.Document = LogOutputViewModel.Document; } + + #region Logging + + /// + /// Enqueue text to the log + /// + /// Text to write to the log + public void Log(string text) => LogInternal(text, LogLevel.USER); + + /// + /// Enqueue text with a newline to the log + /// + /// Text to write to the log + public void LogLn(string text) => Log(text + "\n"); + + /// + /// Enqueue error text to the log + /// + /// Text to write to the log + public void ErrorLog(string text) => LogInternal(text, LogLevel.ERROR); + + /// + /// Enqueue error text with a newline to the log + /// + /// Text to write to the log + public void ErrorLogLn(string text) => ErrorLog(text + "\n"); + + /// + /// Enqueue secret text to the log + /// + /// Text to write to the log + public void SecretLog(string text) => LogInternal(text, LogLevel.SECRET); + + /// + /// Enqueue secret text with a newline to the log + /// + /// Text to write to the log + public void SecretLogLn(string text) => SecretLog(text + "\n"); + + /// + /// Enqueue verbose text to the log + /// + /// Text to write to the log + public void VerboseLog(string text) => LogInternal(text, LogLevel.VERBOSE); + + /// + /// Enqueue verbose text with a newline to the log + /// + /// Text to write to the log + public void VerboseLogLn(string text) => VerboseLog(text + "\n"); + + /// + /// Reset the progress bar state + /// + public void ResetProgressBar() + { + Dispatcher.Invoke(() => + { + ProgressBar.Value = 0; + ProgressLabel.Text = string.Empty; + }); + } + + /// + /// Enqueue text to the log with formatting + /// + /// Text to write to the log + /// LogLevel for the log + private void LogInternal(string text, LogLevel logLevel) + { + // Null text gets ignored + if (text == null) + return; + + // Enqueue the text + LogOutputViewModel.LogQueue.Enqueue(new LogOutputViewModel.LogLine(text, logLevel)); + } + + #endregion + + #region Helpers + + /// + /// Scroll the current view to the bottom + /// + public void ScrollToBottom() => OutputViewer.ScrollToBottom(); + + #endregion + + #region EventHandlers + + private void OnClearButton(object sender, EventArgs e) + { + LogOutputViewModel.ClearInlines(); + ResetProgressBar(); + } + + private void OnSaveButton(object sender, EventArgs e) + => LogOutputViewModel.SaveInlines(); + + private void OnTextChanged(object sender, TextChangedEventArgs e) + => ScrollToBottom(); + + private void OutputViewerSizeChanged(object sender, SizeChangedEventArgs e) + => ScrollToBottom(); + + #endregion } } diff --git a/MPF.UI.Core/ViewModels/LogViewModel.cs b/MPF.UI.Core/ViewModels/LogOutputViewModel.cs similarity index 51% rename from MPF.UI.Core/ViewModels/LogViewModel.cs rename to MPF.UI.Core/ViewModels/LogOutputViewModel.cs index d0985fc9..13fb62f8 100644 --- a/MPF.UI.Core/ViewModels/LogViewModel.cs +++ b/MPF.UI.Core/ViewModels/LogOutputViewModel.cs @@ -1,26 +1,23 @@ using System; using System.IO; -using System.Windows; -using System.Windows.Controls; +using System.Threading.Tasks; using System.Windows.Documents; using System.Windows.Media; using MPF.Core.Data; -using MPF.UI.Core.UserControls; namespace MPF.UI.Core.ViewModels { - public class LogViewModel + public class LogOutputViewModel { - #region Fields + /// + /// Document representing the text + /// + internal FlowDocument Document { get; private set; } /// - /// Parent OptionsWindow object + /// Queue of items that need to be logged /// - public LogOutput Parent { get; private set; } - - #endregion - - #region Private State Variables + internal ProcessingQueue LogQueue { get; private set; } /// /// Paragraph backing the log @@ -32,44 +29,44 @@ namespace MPF.UI.Core.ViewModels /// private Run lastLine = null; - /// - /// Queue of items that need to be logged - /// - private readonly ProcessingQueue logQueue; - - #endregion - - /// - /// Constructor - /// - public LogViewModel(LogOutput parent) + public LogOutputViewModel() { - Parent = parent; - - // Add handlers - Parent.OutputViewer.SizeChanged += OutputViewerSizeChanged; - Parent.Output.TextChanged += OnTextChanged; - Parent.ClearButton.Click += OnClearButton; - Parent.SaveButton.Click += OnSaveButton; - // Update the internal state - var document = new FlowDocument() + Document = new FlowDocument() { Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x20, 0x20, 0x20)) }; _paragraph = new Paragraph(); - document.Blocks.Add(_paragraph); - Parent.Output.Document = document; + Document.Blocks.Add(_paragraph); - logQueue = new ProcessingQueue(ProcessLogLine); + // Setup the processing queue + LogQueue = new ProcessingQueue(ProcessLogLine); } - #region Logging + /// + /// Clear all inlines of the paragraph + /// + public void ClearInlines() => _paragraph.Inlines.Clear(); + + /// + /// Save all inlines to console.log + /// + public void SaveInlines() + { + using (StreamWriter tw = new StreamWriter(File.OpenWrite("console.log"))) + { + foreach (var inline in _paragraph.Inlines) + { + if (inline is Run run) + tw.Write(run.Text); + } + } + } /// /// Log line wrapper /// - private struct LogLine + internal struct LogLine { public readonly string Text; public readonly LogLevel LogLevel; @@ -110,89 +107,29 @@ namespace MPF.UI.Core.ViewModels } } - /// - /// Enqueue text to the log - /// - /// Text to write to the log - public void Log(string text) => LogInternal(text, LogLevel.USER); - - /// - /// Enqueue text with a newline to the log - /// - /// Text to write to the log - public void LogLn(string text) => Log(text + "\n"); - - /// - /// Enqueue error text to the log - /// - /// Text to write to the log - public void ErrorLog(string text) => LogInternal(text, LogLevel.ERROR); - - /// - /// Enqueue error text with a newline to the log - /// - /// Text to write to the log - public void ErrorLogLn(string text) => ErrorLog(text + "\n"); - - /// - /// Enqueue secret text to the log - /// - /// Text to write to the log - public void SecretLog(string text) => LogInternal(text, LogLevel.SECRET); - - /// - /// Enqueue secret text with a newline to the log - /// - /// Text to write to the log - public void SecretLogLn(string text) => SecretLog(text + "\n"); - - /// - /// Enqueue verbose text to the log - /// - /// Text to write to the log - public void VerboseLog(string text) => LogInternal(text, LogLevel.VERBOSE); - - /// - /// Enqueue verbose text with a newline to the log - /// - /// Text to write to the log - public void VerboseLogLn(string text) => VerboseLog(text + "\n"); - - /// - /// Reset the progress bar state - /// - public void ResetProgressBar() - { - Parent.Dispatcher.Invoke(() => - { - Parent.ProgressBar.Value = 0; - Parent.ProgressLabel.Text = string.Empty; - }); - } - /// /// Enqueue text to the log with formatting /// /// Text to write to the log /// LogLevel for the log - private void LogInternal(string text, LogLevel logLevel) + internal void LogInternal(string text, LogLevel logLevel) { // Null text gets ignored if (text == null) return; // Enqueue the text - logQueue.Enqueue(new LogLine(text, logLevel)); + LogQueue.Enqueue(new LogLine(text, logLevel)); } /// /// Process the log lines in the queue /// /// LogLine item to process - private void ProcessLogLine(LogLine nextLogLine) + internal void ProcessLogLine(LogLine nextLogLine) { // Null text gets ignored - string nextText = Parent.Dispatcher.Invoke(() => nextLogLine.Text); + string nextText = nextLogLine.Text; if (nextText == null) return; @@ -216,7 +153,7 @@ namespace MPF.UI.Core.ViewModels /// LogLine value to append private void AppendToTextBox(LogLine logLine) { - Parent.Dispatcher.Invoke(() => + Task.Run(() => { var run = logLine.GenerateRun(); _paragraph.Inlines.Add(run); @@ -230,53 +167,11 @@ namespace MPF.UI.Core.ViewModels /// LogLine value to append private void ReplaceLastLine(LogLine logLine) { - Parent.Dispatcher.Invoke(() => + Task.Run(() => { lastLine.Text = logLine.Text; lastLine.Foreground = logLine.GetForegroundColor(); }); } - - #endregion - - #region Helpers - - /// - /// Scroll the current view to the bottom - /// - public void ScrollToBottom() - { - Parent.OutputViewer.ScrollToBottom(); - } - - #endregion - - #region EventHandlers - - private void OnClearButton(object sender, EventArgs e) - { - _paragraph.Inlines.Clear(); - ResetProgressBar(); - } - - private void OnSaveButton(object sender, EventArgs e) - { - using (StreamWriter tw = new StreamWriter(File.OpenWrite("console.log"))) - { - foreach (var inline in _paragraph.Inlines) - { - if (inline is Run run) - tw.Write(run.Text); - } - } - } - - private void OnTextChanged(object sender, TextChangedEventArgs e) => - ScrollToBottom(); - - private void OutputViewerSizeChanged(object sender, SizeChangedEventArgs e) => - ScrollToBottom(); - - #endregion } } diff --git a/MPF.UI.Core/ViewModels/MainViewModel.cs b/MPF.UI.Core/ViewModels/MainViewModel.cs index 1421ad4a..38ed17a8 100644 --- a/MPF.UI.Core/ViewModels/MainViewModel.cs +++ b/MPF.UI.Core/ViewModels/MainViewModel.cs @@ -9,6 +9,7 @@ using MPF.Core; using MPF.Core.Data; using MPF.Core.Utilities; using MPF.Core.UI.ComboBoxItems; +using MPF.UI.Core.UserControls; using MPF.UI.Core.Windows; using SabreTools.RedumpLib.Data; using WPFCustomMessageBox; @@ -28,7 +29,7 @@ namespace MPF.UI.Core.ViewModels /// /// LogViewModel associated with the parent window /// - public LogViewModel Logger => Parent.LogOutput.LogViewModel; + public LogOutput Logger => Parent.LogOutput; /// /// Access to the current options