using CommunityToolkit.Mvvm.ComponentModel; using OpenTap; using OpenTap.Diagnostic; using OpenTapEditor.Util; using System.Collections.ObjectModel; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Media; using System.Windows.Threading; using UtilLib; namespace OpenTapEditor { public enum LevelType { /// /// Recoverable error. /// Error = 10, /// /// Noncritical problem. /// Warning = 20, /// /// Informational message. /// Info = 30, /// /// Debugging trace. /// Debug = 40 } public class LogItem { public Event e { get; internal set; } public int Type => e.EventType; public override string ToString() { return $"{(LevelType)e.EventType} {new DateTime(e.Timestamp).ToString("HH:mm:ss.fff")} : {e.Source} : {e.Message}"; } } /// /// LogControl.xaml 的交互逻辑 /// [ObservableObject] public partial class LogControl : UserControl, ILogListener { [ObservableProperty] private ObservableCollection datasource = new ObservableCollection(); [ObservableProperty] private string searchText = string.Empty; [ObservableProperty] private bool showError = true; [ObservableProperty] private bool showWarn = true; [ObservableProperty] private bool showInfo = true; [ObservableProperty] private bool showDebug = true; [ObservableProperty] private bool showAll = false; [ObservableProperty] private CollectionViewSource viewSource; private readonly SolidColorBrush _debugBrush; private readonly SolidColorBrush _successBrush; private readonly SolidColorBrush _warnBrush; private readonly SolidColorBrush _errorBrush; public LogControl() { _debugBrush = new SolidColorBrush(Color.FromArgb(0xaa, 0x71, 0x80, 0x8A)); // 灰色 _successBrush = new SolidColorBrush(Color.FromArgb(0xaa, 0x30, 0xC5, 0x30)); _warnBrush = new SolidColorBrush(Color.FromArgb(0xaa, 0xD6, 0x9E, 0x2E)); // 橙黄色 _errorBrush = new SolidColorBrush(Color.FromArgb(0xaa, 0xC5, 0x30, 0x30)); // 红色 DataContext = this; ViewSource = new CollectionViewSource(); ViewSource.Filter += CollectionViewSource_Filter; BindingOperations.SetBinding(viewSource, CollectionViewSource.SourceProperty, new Binding(nameof(Datasource)) { Source = this, Mode = BindingMode.TwoWay, NotifyOnSourceUpdated = true, }); InitializeComponent(); InitFromSetting(); } private void InitFromSetting() { ShowError = bool.Parse(IniHelper.ReadIniData("Log", nameof(ShowError), "True", PathHelper.SettingIniPath)); ShowWarn = bool.Parse(IniHelper.ReadIniData("Log", nameof(ShowWarn), "True", PathHelper.SettingIniPath)); ShowInfo = bool.Parse(IniHelper.ReadIniData("Log", nameof(ShowInfo), "True", PathHelper.SettingIniPath)); ShowDebug = bool.Parse(IniHelper.ReadIniData("Log", nameof(ShowDebug), "True", PathHelper.SettingIniPath)); ShowAll = bool.Parse(IniHelper.ReadIniData("Log", nameof(ShowAll), "False", PathHelper.SettingIniPath)); } private void WriteSetting(string name, object value) { IniHelper.WriteIniData("Log", name, value.ToString(), PathHelper.SettingIniPath); } partial void OnShowErrorChanged(bool value) { WriteSetting(nameof(ShowError), value); ViewSource.View.Refresh(); } partial void OnShowWarnChanged(bool value) { WriteSetting(nameof(ShowWarn), value); ViewSource.View.Refresh(); } partial void OnShowInfoChanged(bool value) { WriteSetting(nameof(ShowInfo), value); ViewSource.View.Refresh(); } partial void OnShowDebugChanged(bool value) { WriteSetting(nameof(ShowDebug), value); ViewSource.View.Refresh(); } partial void OnShowAllChanged(bool value) { WriteSetting(nameof(ShowAll), value); ViewSource.View.Refresh(); } partial void OnSearchTextChanged(string value) { ViewSource.View.Refresh(); } public void EventsLogged(IEnumerable Events) { foreach (var e in Events) { HandleLog(e); } } private void HandleLog(Event e) { Dispatcher.BeginInvoke(() => { lock (this) { var item = new LogItem() { e = e }; while (Datasource.Count >= 1000) { Datasource.RemoveAt(0); } Datasource.Add(item); log.ScrollIntoView(item); } }, DispatcherPriority.Background); } public void Flush() { Log.Flush(); } private void CollectionViewSource_Filter(object sender, System.Windows.Data.FilterEventArgs e) { if (e.Item is LogItem item) { bool res = true; switch (item.Type) { case 10: res = ShowError || ShowAll; break; case 20: res = ShowWarn || ShowAll; break; case 30: res = ShowInfo || ShowAll; break; case 40: res = ShowDebug || ShowAll; break; default: res = true; break; } if (!string.IsNullOrEmpty(SearchText)) { res &= item.ToString().Contains(SearchText); } e.Accepted = res; } } private void Button_Click(object sender, System.Windows.RoutedEventArgs e) { Datasource.Clear(); } private volatile bool showingResult = false; public void ShowResult(Verdict verdict) { if (showingResult) { return; } showingResult = true; Task.Run(() => { Dispatcher.BeginInvoke(() => { string title = "PASS"; resultShow.Background = _successBrush; if (verdict == Verdict.Aborted) { resultShow.Background = _warnBrush; title = "Aborted"; } else if (verdict == Verdict.NotSet) { resultShow.Background = _debugBrush; title = "NotSet"; } else if (verdict == Verdict.Fail) { resultShow.Background = _errorBrush; title = "FAIL"; } else if (verdict == Verdict.Error) { resultShow.Background = _errorBrush; title = "Error"; } resultLabel.Content = title; resultShow.Visibility = System.Windows.Visibility.Visible; }); Thread.Sleep(3000); Dispatcher.BeginInvoke(() => { resultShow.Visibility = System.Windows.Visibility.Collapsed; }); showingResult = false; }); } } }