refactor: log tableview

This commit is contained in:
mrFq1
2023-06-04 14:53:01 +08:00
parent 8fc7bec255
commit 42b6c2da0e
3 changed files with 170 additions and 36 deletions

View File

@@ -7,6 +7,7 @@
import Cocoa import Cocoa
import SwiftUI import SwiftUI
import CocoaLumberjackSwift import CocoaLumberjackSwift
import DifferenceKit
class ClashApiDatasStorage: NSObject, ObservableObject { class ClashApiDatasStorage: NSObject, ObservableObject {
@@ -125,14 +126,17 @@ class ClashOverviewData: ObservableObject, Identifiable {
class ClashLogStorage: ObservableObject { class ClashLogStorage: ObservableObject {
@Published var logs = [ClashLog]() @Published var logs = [ClashLog]()
class ClashLog: NSObject, ObservableObject, Identifiable { class ClashLog: NSObject, ObservableObject, Identifiable, Differentiable {
let id: String let id: String
var differenceIdentifier: String {
return id
}
let date: Date let date: Date
let level: ClashLogLevel let level: ClashLogLevel
@objc let log: String @objc let log: String
let levelColor: Color let levelColor: NSColor
@objc let levelString: String @objc let levelString: String
init(level: String, log: String) { init(level: String, log: String) {
@@ -144,13 +148,13 @@ class ClashLogStorage: ObservableObject {
self.levelString = level self.levelString = level
switch self.level { switch self.level {
case .info: case .info:
levelColor = .blue levelColor = .systemBlue
case .warning: case .warning:
levelColor = .yellow levelColor = .systemYellow
case .error: case .error:
levelColor = .red levelColor = .systemRed
case .debug: case .debug:
levelColor = .green levelColor = .systemGreen
default: default:
levelColor = .white levelColor = .white
} }

View File

@@ -0,0 +1,158 @@
//
// LogsTableView.swift
//
//
//
import Cocoa
import SwiftUI
import DifferenceKit
struct LogsTableView<Item: Hashable>: NSViewRepresentable {
enum TableColumn: String, CaseIterable {
case date = "Date"
case level = "Level"
case log = "Log"
}
var data: [Item]
var filterString: String
class NonRespondingScrollView: NSScrollView {
override var acceptsFirstResponder: Bool { false }
}
class NonRespondingTableView: NSTableView {
override var acceptsFirstResponder: Bool { false }
}
func makeNSView(context: Context) -> NSScrollView {
let scrollView = NonRespondingScrollView()
scrollView.hasVerticalScroller = true
scrollView.hasHorizontalScroller = false
scrollView.autohidesScrollers = true
let tableView = NonRespondingTableView()
tableView.usesAlternatingRowBackgroundColors = true
tableView.delegate = context.coordinator
tableView.dataSource = context.coordinator
TableColumn.allCases.forEach {
let tableColumn = NSTableColumn(identifier: .init($0.rawValue))
tableColumn.title = $0.rawValue
tableColumn.isEditable = false
switch $0 {
case .date:
tableColumn.minWidth = 60
tableColumn.maxWidth = 140
tableColumn.width = 135
case .level:
tableColumn.minWidth = 40
tableColumn.maxWidth = 65
default:
tableColumn.minWidth = 120
tableColumn.maxWidth = .infinity
}
tableView.addTableColumn(tableColumn)
}
scrollView.documentView = tableView
return scrollView
}
func updateNSView(_ nsView: NSScrollView, context: Context) {
context.coordinator.parent = self
guard let tableView = nsView.documentView as? NSTableView,
let data = data as? [ClashLogStorage.ClashLog] else {
return
}
let target = updateSorts(data, tableView: tableView)
let source = context.coordinator.logs
let changeset = StagedChangeset(source: source, target: target)
tableView.reload(using: changeset) { data in
context.coordinator.logs = data
}
}
func updateSorts(_ objects: [ClashLogStorage.ClashLog],
tableView: NSTableView) -> [ClashLogStorage.ClashLog] {
var re = objects
let filterKeys = [
"levelString",
"log",
]
re = re.filtered(filterString, for: filterKeys)
return re
}
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
class Coordinator: NSObject, NSTableViewDelegate, NSTableViewDataSource {
var parent: LogsTableView
var logs = [ClashLogStorage.ClashLog]()
let dateFormatter = {
let df = DateFormatter()
df.dateFormat = "MM/dd HH:mm:ss.SSS"
return df
}()
init(parent: LogsTableView) {
self.parent = parent
}
func numberOfRows(in tableView: NSTableView) -> Int {
logs.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
guard let cellView = tableView.createCellView(with: "LogsTableCellView"),
let s = tableColumn?.identifier.rawValue.split(separator: ".").last,
let tc = TableColumn(rawValue: String(s))
else { return nil }
let log = logs[row]
let tf = cellView.textField
switch tc {
case .date:
tf?.lineBreakMode = .byTruncatingHead
tf?.textColor = .orange
tf?.stringValue = dateFormatter.string(from: log.date)
case .level:
tf?.lineBreakMode = .byTruncatingTail
tf?.textColor = log.levelColor
tf?.stringValue = log.levelString
case .log:
tf?.lineBreakMode = .byTruncatingTail
tf?.textColor = .labelColor
tf?.stringValue = log.log
}
return cellView
}
}
}

View File

@@ -13,37 +13,9 @@ struct LogsView: View {
@State var searchString: String = "" @State var searchString: String = ""
@State var logLevel = ConfigManager.selectLoggingApiLevel @State var logLevel = ConfigManager.selectLoggingApiLevel
var logs: [ClashLogStorage.ClashLog] {
let logs: [ClashLogStorage.ClashLog] = logStorage.logs.reversed()
if searchString.isEmpty {
return logs
} else {
return logs.filtered(searchString, for: ["log", "levelString"])
}
}
var body: some View { var body: some View {
Table(logs) { Group {
TableColumn("Date") { LogsTableView(data: logStorage.logs.reversed(), filterString: searchString)
Text($0.date.formatted(
Date.FormatStyle()
.year(.twoDigits)
.month(.twoDigits)
.day(.twoDigits)
.hour(.twoDigits(amPM: .omitted))
.minute(.twoDigits)
.second(.twoDigits)
))
.foregroundColor(.orange)
.truncationMode(.head)
}
.width(min: 60, max: 130)
TableColumn("Level") {
Text("[\($0.level.rawValue)]")
.foregroundColor($0.levelColor)
}
.width(min: 40, max: 65)
TableColumn("", value: \.log)
} }
.searchable(text: $searchString) .searchable(text: $searchString)
.onReceive(NotificationCenter.default.publisher(for: .toolbarSearchString)) { .onReceive(NotificationCenter.default.publisher(for: .toolbarSearchString)) {