using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using TeamAAS.Dialogs.Dialogs;
namespace TeamAAS.Logging.Controls
{
///
/// 实时日志控件:订阅 联动显示 AppLogger / PluginLogger 的日志。
/// - 类别筛选:Debug/Info/Warning/Error/Critical 多选开关;
/// - 显示判定:公共日志恒显;插件日志由各节点「显示详细度」闸门决定是否进流(与是否落盘无关);
/// - 插件条目上的 L2/L4 为归一化档位标签,仅用于区分常规/详细,不参与显隐。
///
public partial class LogView : UserControl
{
/// 显示上限依赖属性的默认值(可在 XAML 用 MaxEntries 覆盖)。
private const int DefaultMaxEntries = 500;
/// 显示上限:控件最多保留的最近条目数,超出移除最旧。可在 XAML 用 MaxEntries 设置。
public static readonly DependencyProperty MaxEntriesProperty =
DependencyProperty.Register(nameof(MaxEntries), typeof(int), typeof(LogView),
new PropertyMetadata(DefaultMaxEntries, OnMaxEntriesChanged));
/// 显示上限(最近 N 条),默认 500,<=0 视为 1。
public int MaxEntries
{
get => (int)GetValue(MaxEntriesProperty);
set => SetValue(MaxEntriesProperty, value);
}
private static void OnMaxEntriesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
=> (d as LogView)?.TrimToMax();
private readonly ObservableCollection _items = new ObservableCollection();
private readonly ICollectionView _view;
private ScrollViewer _scrollViewer;
private bool _subscribed;
public LogView()
{
InitializeComponent();
_view = CollectionViewSource.GetDefaultView(_items);
_view.Filter = o => IsCategoryEnabled(((LogEntry)o).Category);
LogList.ItemsSource = _view;
Loaded += OnLoaded;
Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
if (_subscribed) return;
// 首次加载回填历史缓冲(新建控件实例时 _items 为空)
if (_items.Count == 0)
{
foreach (var entry in LogStream.Snapshot())
AppendInternal(entry);
}
LogStream.Published += OnPublished;
_subscribed = true;
ScrollToEnd();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
if (!_subscribed) return;
LogStream.Published -= OnPublished;
_subscribed = false;
}
private void OnPublished(LogEntry entry)
{
var dispatcher = Application.Current?.Dispatcher;
if (dispatcher != null && !dispatcher.CheckAccess())
dispatcher.BeginInvoke(new Action(() => Append(entry)));
else
Append(entry);
}
private void Append(LogEntry entry)
{
bool atBottom = IsScrolledToEnd();
AppendInternal(entry);
if (atBottom) ScrollToEnd();
}
private void AppendInternal(LogEntry entry)
{
_items.Add(entry);
TrimToMax();
}
private void TrimToMax()
{
int max = MaxEntries <= 0 ? 1 : MaxEntries;
while (_items.Count > max)
_items.RemoveAt(0);
}
private void CategoryToggle_Changed(object sender, RoutedEventArgs e)
{
// 初始化阶段(ToggleButton 尚未就绪)不刷新
if (_view != null && LogList != null)
_view.Refresh();
}
private void BtnClear_Click(object sender, RoutedEventArgs e)
{
_items.Clear();
}
// 双击一条日志 → 弹出详情窗(含来源/时间/类别/完整内容,支持复制)
private void LogList_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var entry = FindEntry(e.OriginalSource as DependencyObject) ?? LogList.SelectedItem as LogEntry;
if (entry == null) return;
var window = new LogDetailWindow(entry) { Owner = Window.GetWindow(this) };
window.ShowDialog();
}
// 从双击命中点沿(视觉/逻辑)树上溯,找到 DataContext 为 LogEntry 的元素
private static LogEntry FindEntry(DependencyObject node)
{
while (node != null)
{
if (node is FrameworkElement fe && fe.DataContext is LogEntry entry)
return entry;
node = node is Visual
? VisualTreeHelper.GetParent(node)
: LogicalTreeHelper.GetParent(node);
}
return null;
}
private bool IsCategoryEnabled(LogLevel category)
{
switch (category)
{
case LogLevel.Warning: return TgWarning == null || TgWarning.IsChecked == true;
case LogLevel.Error: return TgError == null || TgError.IsChecked == true;
default: return TgInfo == null || TgInfo.IsChecked == true; // Info
}
}
private bool IsScrolledToEnd()
{
if (_scrollViewer == null) _scrollViewer = FindScrollViewer(LogList);
if (_scrollViewer == null) return true;
return _scrollViewer.VerticalOffset >= _scrollViewer.ScrollableHeight - 4;
}
private void ScrollToEnd()
{
if (LogList.Items.Count > 0)
LogList.ScrollIntoView(LogList.Items[LogList.Items.Count - 1]);
}
private static ScrollViewer FindScrollViewer(DependencyObject root)
{
if (root == null) return null;
int count = VisualTreeHelper.GetChildrenCount(root);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(root, i);
if (child is ScrollViewer sv) return sv;
var result = FindScrollViewer(child);
if (result != null) return result;
}
return null;
}
}
}