ClashX.Meta/ClashX/ViewControllers/Connections/Views/ConnectionDetailInfoView.swift

155 lines
6.8 KiB
Swift

//
// ConnectionDetailInfoView.swift
// ClashX
//
// Created by yicheng on 2023/7/5.
// Copyright © 2023 west2online. All rights reserved.
//
import AppKit
import Combine
@available(macOS 10.15, *)
class ConnectionDetailInfoView: NSView {
private let logoView = NSImageView()
private let processNameLabel = NSTextField(labelWithString: "")
private let hostLabel = NSTextField(labelWithString: "")
private let generalView = ConnectionDetailInfoGeneralView.createFromNib()
private let containerView = NSView()
private var cancelable = Set<AnyCancellable>()
private let closeButton = NSButton()
private var viewModel: ConnectionDetailViewModel?
init() {
super.init(frame: .zero)
wantsLayer = true
updateColor()
setupSubviews()
}
override func viewDidChangeEffectiveAppearance() {
super.viewDidChangeEffectiveAppearance()
updateColor()
}
func updateColor() {
if #available(macOS 11.0, *) {
effectiveAppearance.performAsCurrentDrawingAppearance {
layer?.backgroundColor = NSColor.controlColor.cgColor
}
} else {
let pervious = NSAppearance.current
NSAppearance.current = effectiveAppearance
layer?.backgroundColor = NSColor.controlColor.cgColor
NSAppearance.current = pervious
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupSubviews() {
addSubview(logoView)
logoView.makeConstraints {
[$0.topAnchor.constraint(equalTo: topAnchor, constant: 16),
$0.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
$0.widthAnchor.constraint(equalTo: $0.heightAnchor),
$0.widthAnchor.constraint(equalToConstant: 36)]
}
let nameStackView = NSStackView()
nameStackView.orientation = .vertical
nameStackView.addArrangedSubview(processNameLabel)
nameStackView.addArrangedSubview(hostLabel)
nameStackView.alignment = .left
nameStackView.spacing = 2
addSubview(nameStackView)
nameStackView.makeConstraints {
[$0.centerYAnchor.constraint(equalTo: logoView.centerYAnchor),
$0.leadingAnchor.constraint(equalTo: logoView.trailingAnchor, constant: 10)]
}
processNameLabel.font = NSFont.systemFont(ofSize: 17, weight: .bold)
hostLabel.font = NSFont.systemFont(ofSize: 12)
/*
let segmentControl = NSSegmentedControl(labels: ["General", "Event"], trackingMode: .selectOne, target: self, action: #selector(actionSelectSegment(sender: )))
addSubview(segmentControl)
segmentControl.makeConstraints {
[$0.leftAnchor.constraint(equalTo: logoView.centerXAnchor),
$0.topAnchor.constraint(equalTo: nameStackView.bottomAnchor, constant: 12)]
}
segmentControl.selectedSegment = 0
*/
closeButton.title = NSLocalizedString("Close Connection", comment: "")
closeButton.bezelStyle = .regularSquare
closeButton.target = self
closeButton.action = #selector(actionCloseConn)
addSubview(closeButton)
closeButton.makeConstraints { [
$0.centerYAnchor.constraint(equalTo: nameStackView.centerYAnchor),
$0.rightAnchor.constraint(equalTo: rightAnchor, constant: -12)
] }
let separator = NSView()
separator.wantsLayer = true
separator.layer?.backgroundColor = NSColor.separatorColor.cgColor
addSubview(separator)
separator.makeConstraints {
[$0.heightAnchor.constraint(equalToConstant: 1),
$0.leftAnchor.constraint(equalTo: logoView.centerXAnchor),
$0.rightAnchor.constraint(equalTo: rightAnchor),
$0.centerYAnchor.constraint(equalTo: nameStackView.bottomAnchor, constant: 12)]
}
addSubview(containerView)
containerView.makeConstraints {
[$0.leftAnchor.constraint(equalTo: separator.leftAnchor),
$0.rightAnchor.constraint(equalTo: rightAnchor),
$0.bottomAnchor.constraint(equalTo: bottomAnchor),
$0.topAnchor.constraint(equalTo: separator.bottomAnchor, constant: 16)]
}
addGeneralView()
}
func setup(with viewModel: ConnectionDetailViewModel) {
cancelable.removeAll()
self.viewModel = viewModel
viewModel.$processName.weakAssign(to: \.stringValue, on: processNameLabel).store(in: &cancelable)
viewModel.$applicationPath.weakAssign(to: \.toolTip, on: processNameLabel).store(in: &cancelable)
viewModel.$applicationPath.weakAssign(to: \.toolTip, on: logoView).store(in: &cancelable)
viewModel.$remoteHost.weakAssign(to: \.stringValue, on: hostLabel).store(in: &cancelable)
viewModel.$processImage.weakAssign(to: \.image, on: logoView).store(in: &cancelable)
viewModel.$entry.weakAssign(to: \.stringValue, on: generalView.entryLabel).store(in: &cancelable)
viewModel.$networkType.weakAssign(to: \.stringValue, on: generalView.networkTypeLabel).store(in: &cancelable)
viewModel.$totalUpload.weakAssign(to: \.stringValue, on: generalView.totalUploadLabel).store(in: &cancelable)
viewModel.$totalDownload.weakAssign(to: \.stringValue, on: generalView.totalDownloadLabel).store(in: &cancelable)
viewModel.$maxUpload.weakAssign(to: \.stringValue, on: generalView.maxUploadLabel).store(in: &cancelable)
viewModel.$maxDownload.weakAssign(to: \.stringValue, on: generalView.maxDownloadLabel).store(in: &cancelable)
viewModel.$currentUpload.weakAssign(to: \.stringValue, on: generalView.currentUploadLabel).store(in: &cancelable)
viewModel.$currentDownload.weakAssign(to: \.stringValue, on: generalView.currentDownloadLabel).store(in: &cancelable)
viewModel.$rule.weakAssign(to: \.stringValue, on: generalView.ruleLabel).store(in: &cancelable)
viewModel.$chain.weakAssign(to: \.stringValue, on: generalView.proxyChainLabel).store(in: &cancelable)
viewModel.$sourceIP.weakAssign(to: \.stringValue, on: generalView.sourceIpLabel).store(in: &cancelable)
viewModel.$destination.weakAssign(to: \.stringValue, on: generalView.destLabel).store(in: &cancelable)
viewModel.$otherText.weakAssign(to: \.string, on: generalView.otherTextView).store(in: &cancelable)
viewModel.$showCloseButton.map {!$0}.weakAssign(to: \.isHidden, on: closeButton).store(in: &cancelable)
}
@objc func actionSelectSegment(sender: NSSegmentedControl?) { }
@objc func actionCloseConn() {
viewModel?.closeConnection()
}
func addGeneralView() {
containerView.subviews.forEach { $0.removeFromSuperview() }
containerView.addSubview(generalView)
generalView.makeConstraintsToBindToSuperview()
}
}