feat: redesigned the settings view to work with light/dark mode (#1008)

This commit is contained in:
Serhiy Mytrovtsiy
2022-12-02 17:04:07 +01:00
parent a84309a5b9
commit 736e7cd732
5 changed files with 227 additions and 285 deletions

View File

@@ -21,8 +21,8 @@ public struct Popup_c_s {
}
public struct Settings_c_s {
public let width: CGFloat = 539
public let height: CGFloat = 479
public let width: CGFloat = 540
public let height: CGFloat = 480
public let margin: CGFloat = 10
public let row: CGFloat = 30
}

View File

@@ -131,13 +131,6 @@ open class Module: Module_p {
moduleSettings: self.settingsView,
popupSettings: self.popupView
)
self.settings?.toggleCallback = { [weak self] in
self?.toggleEnabled()
if self?.pauseState == true {
self?.pauseState = false
NotificationCenter.default.post(name: .pause, object: nil, userInfo: ["state": false])
}
}
self.popup = PopupWindow(title: self.config.name, view: self.popupView, visibilityCallback: self.visibilityCallback)
}
@@ -208,15 +201,6 @@ open class Module: Module_p {
debug("Module disabled", log: self.log)
}
// toggle module state
private func toggleEnabled() {
if self.enabled {
self.disable()
} else {
self.enable()
}
}
// add reader to module. If module is enabled will fire a read function and start a reader
public func addReader(_ reader: Reader_p) {
self.readers.append(reader)
@@ -320,8 +304,19 @@ open class Module: Module_p {
} else if !state && self.enabled {
self.disable()
}
} else {
if self.enabled {
self.disable()
} else {
self.enable()
}
}
}
if self.pauseState == true {
self.pauseState = false
NotificationCenter.default.post(name: .pause, object: nil, userInfo: ["state": false])
}
}
}

View File

@@ -12,7 +12,6 @@
import Cocoa
public protocol Settings_p: NSView {
var toggleCallback: () -> Void { get set }
func setState(_ newState: Bool)
}
@@ -22,8 +21,6 @@ public protocol Settings_v: NSView {
}
open class Settings: NSStackView, Settings_p {
public var toggleCallback: () -> Void = {}
private var config: UnsafePointer<module_c>
private var widgets: [Widget]
private var moduleSettings: Settings_v?
@@ -35,14 +32,6 @@ open class Settings: NSStackView, Settings_p {
private var enableControl: NSControl?
private let headerSeparator: NSView = {
let view: NSView = NSView()
view.heightAnchor.constraint(equalToConstant: 1).isActive = true
view.wantsLayer = true
view.layer?.backgroundColor = NSColor(hexString: "#d1d1d1").cgColor
return view
}()
private let noWidgetsView: EmptyView = EmptyView(msg: localizedString("No available widgets to configure"))
private let noPopupSettingsView: EmptyView = EmptyView(msg: localizedString("No options to configure for the popup in this module"))
@@ -61,136 +50,43 @@ open class Settings: NSStackView, Settings_p {
self.moduleSettings = moduleSettings
self.popupSettings = popupSettings
super.init(frame: NSRect(x: 0, y: 0, width: Constants.Settings.width, height: Constants.Settings.height))
NotificationCenter.default.addObserver(self, selector: #selector(externalModuleToggle), name: .toggleModule, object: nil)
self.wantsLayer = true
self.appearance = NSAppearance(named: .aqua)
self.layer?.backgroundColor = NSColor(hexString: "#ececec").cgColor
super.init(frame: NSRect.zero)
self.orientation = .vertical
self.alignment = .width
self.distribution = .fill
self.spacing = 0
self.addArrangedSubview(self.header(enabled))
self.addArrangedSubview(self.headerSeparator)
self.addArrangedSubview(self.body())
self.addArrangedSubview(NSView())
}
deinit {
NotificationCenter.default.removeObserver(self)
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - parts
private func header(_ enabled: Bool) -> NSStackView {
let view: NSStackView = NSStackView()
view.orientation = .horizontal
view.distribution = .fillEqually
view.alignment = .centerY
view.distribution = .fillProportionally
view.spacing = 0
view.edgeInsets = NSEdgeInsets(
self.spacing = Constants.Settings.margin
self.edgeInsets = NSEdgeInsets(
top: Constants.Settings.margin,
left: Constants.Settings.margin,
bottom: Constants.Settings.margin,
right: Constants.Settings.margin
)
let titleView = NSTextField()
titleView.isEditable = false
titleView.isSelectable = false
titleView.isBezeled = false
titleView.wantsLayer = true
titleView.textColor = .black
titleView.backgroundColor = .clear
titleView.canDrawSubviewsIntoLayer = true
titleView.alignment = .natural
titleView.font = NSFont.systemFont(ofSize: 18, weight: .light)
titleView.stringValue = localizedString(self.config.pointee.name)
let widgetSelector = WidgetSelectorView(module: self.config.pointee.name, widgets: self.widgets, stateCallback: self.loadWidget)
var toggleBtn: NSControl = NSControl()
if #available(OSX 10.15, *) {
let switchButton = NSSwitch()
switchButton.state = enabled ? .on : .off
switchButton.action = #selector(self.toggleEnable)
switchButton.target = self
toggleBtn = switchButton
} else {
let button: NSButton = NSButton()
button.setButtonType(.switch)
button.state = enabled ? .on : .off
button.title = ""
button.action = #selector(self.toggleEnable)
button.isBordered = false
button.isTransparent = false
button.target = self
toggleBtn = button
}
self.enableControl = toggleBtn
view.addArrangedSubview(titleView)
view.addArrangedSubview(NSView())
view.addArrangedSubview(toggleBtn)
return view
}
private func body() -> NSStackView {
let view: NSStackView = NSStackView()
view.translatesAutoresizingMaskIntoConstraints = false
view.orientation = .vertical
view.edgeInsets = NSEdgeInsets(
top: Constants.Settings.margin,
left: Constants.Settings.margin,
bottom: Constants.Settings.margin,
right: Constants.Settings.margin
)
view.spacing = Constants.Settings.margin
view.addArrangedSubview(WidgetSelectorView(module: self.config.pointee.name, widgets: self.widgets, stateCallback: self.loadWidget))
view.addArrangedSubview(self.settings())
return view
}
// MARK: - views
private func settings() -> NSView {
let view: NSTabView = NSTabView(frame: NSRect(x: 0, y: 0,
width: Constants.Settings.width - Constants.Settings.margin*2,
height: Constants.Settings.height - 40 - Constants.Widget.height - (Constants.Settings.margin*5)
))
view.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
view.heightAnchor.constraint(equalToConstant: view.frame.height).isActive = true
view.tabViewType = .topTabsBezelBorder
view.tabViewBorderType = .line
let tabView = NSTabView()
tabView.tabViewType = .topTabsBezelBorder
tabView.tabViewBorderType = .line
let moduleTab: NSTabViewItem = NSTabViewItem()
moduleTab.label = localizedString("Module settings")
moduleTab.view = {
let view = ScrollableStackView(frame: view.frame)
self.moduleSettingsContainer = view.stackView
let container = NSStackView()
container.translatesAutoresizingMaskIntoConstraints = false
let scrollView = ScrollableStackView()
self.moduleSettingsContainer = scrollView.stackView
self.loadModuleSettings()
return view
container.addArrangedSubview(scrollView)
return container
}()
let widgetTab: NSTabViewItem = NSTabViewItem()
widgetTab.label = localizedString("Widget settings")
widgetTab.view = {
let view = ScrollableStackView(frame: view.frame)
let view = ScrollableStackView(frame: tabView.frame)
view.stackView.spacing = 0
self.widgetSettingsContainer = view.stackView
self.loadWidgetSettings()
@@ -200,34 +96,27 @@ open class Settings: NSStackView, Settings_p {
let popupTab: NSTabViewItem = NSTabViewItem()
popupTab.label = localizedString("Popup settings")
popupTab.view = {
let view = ScrollableStackView(frame: view.frame)
let view = ScrollableStackView(frame: tabView.frame)
view.stackView.spacing = 0
self.popupSettingsContainer = view.stackView
self.loadPopupSettings()
return view
}()
view.addTabViewItem(moduleTab)
view.addTabViewItem(widgetTab)
view.addTabViewItem(popupTab)
tabView.addTabViewItem(moduleTab)
tabView.addTabViewItem(widgetTab)
tabView.addTabViewItem(popupTab)
return view
self.addArrangedSubview(widgetSelector)
self.addArrangedSubview(tabView)
}
// MARK: - helpers
@objc private func toggleEnable(_ sender: Any) {
self.toggleCallback()
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc private func externalModuleToggle(_ notification: Notification) {
if let name = notification.userInfo?["module"] as? String {
if name == self.config.pointee.name {
if let state = notification.userInfo?["state"] as? Bool {
toggleNSControlState(self.enableControl, state: state ? .on : .off)
}
}
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func setState(_ newState: Bool) {
@@ -249,7 +138,6 @@ open class Settings: NSStackView, Settings_p {
self.moduleSettingsContainer?.addArrangedSubview(NSView())
}
}
private func loadWidgetSettings() {
self.widgetSettingsContainer?.subviews.forEach{ $0.removeFromSuperview() }
let list = self.widgets.filter({ $0.isActive && $0.type != .label })
@@ -301,14 +189,7 @@ open class Settings: NSStackView, Settings_p {
}
@objc private func toggleOneView(_ sender: NSControl) {
var state: NSControl.StateValue? = nil
if #available(OSX 10.15, *) {
state = sender is NSSwitch ? (sender as! NSSwitch).state: nil
} else {
state = sender is NSButton ? (sender as! NSButton).state: nil
}
self.oneViewState = state! == .on ? true : false
self.oneViewState = controlState(sender)
NotificationCenter.default.post(name: .toggleOneView, object: nil, userInfo: ["module": self.config.pointee.name])
}
}
@@ -317,15 +198,26 @@ class WidgetSelectorView: NSStackView {
private var module: String
private var stateCallback: () -> Void = {}
private var background: NSVisualEffectView = {
let view = NSVisualEffectView(frame: NSRect.zero)
view.blendingMode = .withinWindow
if #available(macOS 10.14, *) {
view.material = .contentBackground
} else {
view.material = .popover
}
view.state = .active
return view
}()
public init(module: String, widgets: [Widget], stateCallback: @escaping () -> Void) {
self.module = module
self.stateCallback = stateCallback
super.init(frame: NSRect(x: 0, y: 0, width: 0, height: 0))
super.init(frame: NSRect.zero)
self.translatesAutoresizingMaskIntoConstraints = false
self.wantsLayer = true
self.layer?.backgroundColor = .white
self.layer?.cornerRadius = 3
self.edgeInsets = NSEdgeInsets(
top: Constants.Settings.margin,
@@ -375,6 +267,7 @@ class WidgetSelectorView: NSStackView {
}
self.addArrangedSubview(NSView())
self.addSubview(self.background, positioned: .below, relativeTo: .none)
NSLayoutConstraint.activate([
self.heightAnchor.constraint(equalToConstant: Constants.Widget.height + (Constants.Settings.margin*2)),
@@ -387,6 +280,10 @@ class WidgetSelectorView: NSStackView {
fatalError("init(coder:) has not been implemented")
}
override func updateLayer() {
self.background.setFrameSize(self.frame.size)
}
override func mouseDown(with event: NSEvent) {
let location = convert(event.locationInWindow, from: nil)
guard let targetIdx = self.views.firstIndex(where: { $0.hitTest(location) != nil }),
@@ -501,6 +398,7 @@ internal class WidgetPreview: NSStackView {
self.layer?.cornerRadius = 2
self.layer?.borderColor = NSColor(hexString: "#dddddd").cgColor
self.layer?.borderWidth = 1
self.layer?.backgroundColor = NSColor.white.cgColor
self.identifier = NSUserInterfaceItemIdentifier(rawValue: type.rawValue)
self.toolTip = localizedString("Move widget", type.name())

View File

@@ -25,11 +25,9 @@ class Dashboard: NSScrollView {
self.drawsBackground = false
self.borderType = .noBorder
self.scrollerStyle = .overlay
self.hasVerticalScroller = true
self.hasHorizontalScroller = false
self.autohidesScrollers = true
self.horizontalScrollElasticity = .none
self.automaticallyAdjustsContentInsets = false
NotificationCenter.default.addObserver(self, selector: #selector(windowOpens), name: .openModuleSettings, object: nil)
@@ -56,8 +54,10 @@ class Dashboard: NSScrollView {
grid.row(at: 2).height = specsView.frame.height
self.documentView = grid
if let documentView = self.documentView {
documentView.scroll(NSPoint(x: 0, y: documentView.bounds.size.height))
DispatchQueue.main.async {
if let documentView = self.documentView {
documentView.scroll(NSPoint(x: 0, y: documentView.bounds.size.height))
}
}
}
@@ -66,7 +66,7 @@ class Dashboard: NSScrollView {
}
private func versions() -> NSView {
let view: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: 280))
let view: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: 220))
let h: CGFloat = 120+60+18
let container: NSGridView = NSGridView(frame: NSRect(x: 0, y: (view.frame.height-h)/2, width: self.frame.width, height: h))

View File

@@ -12,9 +12,21 @@
import Cocoa
import Kit
class SettingsWindow: NSWindow, NSWindowDelegate {
public extension NSToolbarItem.Identifier {
static let toggleButton = NSToolbarItem.Identifier("toggleButton")
}
class SettingsWindow: NSWindow, NSWindowDelegate, NSToolbarDelegate {
static let size: CGSize = CGSize(width: 720, height: 480)
private let vc: SettingsView = SettingsView()
private let mainView: MainView = MainView(frame: NSRect(x: 0, y: 0, width: 540, height: 480))
private let sidebarView: SidebarView = SidebarView(frame: NSRect(x: 0, y: 0, width: 180, height: 480))
private var dashboard: NSView = Dashboard()
private var settings: ApplicationSettings = ApplicationSettings()
private var toggleButton: NSControl? = nil
private var activeModuleName: String? = nil
private var pauseState: Bool {
Store.shared.bool(key: "pause", defaultValue: false)
@@ -33,29 +45,59 @@ class SettingsWindow: NSWindow, NSWindowDelegate {
defer: false
)
self.contentViewController = self.vc
self.titlebarAppearsTransparent = true
self.backgroundColor = .clear
self.center()
self.setIsVisible(false)
let sidebarViewController = NSSplitViewController()
let windowController = NSWindowController()
windowController.window = self
windowController.loadWindow()
let sidebarVC: NSViewController = NSViewController(nibName: nil, bundle: nil)
sidebarVC.view = self.sidebarView
let mainVC: NSViewController = NSViewController(nibName: nil, bundle: nil)
mainVC.view = self.mainView
let sidebarItem = NSSplitViewItem(sidebarWithViewController: sidebarVC)
let contentItem = NSSplitViewItem(viewController: mainVC)
sidebarItem.canCollapse = false
contentItem.canCollapse = false
sidebarViewController.addSplitViewItem(sidebarItem)
sidebarViewController.addSplitViewItem(contentItem)
let newToolbar = NSToolbar(identifier: "eu.exelban.Stats.Settings.Toolbar")
newToolbar.allowsUserCustomization = false
newToolbar.autosavesConfiguration = true
newToolbar.displayMode = .default
newToolbar.showsBaselineSeparator = true
newToolbar.delegate = self
self.toolbar = newToolbar
self.contentViewController = sidebarViewController
self.titlebarAppearsTransparent = true
self.backgroundColor = .clear
// self.center()
self.setIsVisible(true)
let windowController = NSWindowController()
windowController.window = self
windowController.loadWindow()
NSLayoutConstraint.activate([
self.sidebarView.widthAnchor.constraint(equalToConstant: 180),
self.mainView.widthAnchor.constraint(equalToConstant: 540),
self.mainView.container.widthAnchor.constraint(equalToConstant: 540),
self.mainView.container.topAnchor.constraint(equalTo: (self.contentLayoutGuide as! NSLayoutGuide).topAnchor),
self.mainView.container.bottomAnchor.constraint(equalTo: (self.contentLayoutGuide as! NSLayoutGuide).bottomAnchor)
])
NotificationCenter.default.addObserver(self, selector: #selector(menuCallback), name: .openModuleSettings, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(toggleSettingsHandler), name: .toggleSettings, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(externalModuleToggle), name: .toggleModule, object: nil)
self.sidebarView.openMenu("Dashboard")
}
deinit {
NotificationCenter.default.removeObserver(self, name: .toggleSettings, object: nil)
NotificationCenter.default.removeObserver(self, name: .openModuleSettings, object: nil)
NotificationCenter.default.removeObserver(self, name: .toggleModule, object: nil)
}
override func performKeyEquivalent(with event: NSEvent) -> Bool {
@@ -68,10 +110,55 @@ class SettingsWindow: NSWindow, NSWindowDelegate {
return true
}
}
return super.performKeyEquivalent(with: event)
}
override func mouseUp(with: NSEvent) {
NotificationCenter.default.post(name: .clickInSettings, object: nil, userInfo: nil)
}
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
switch itemIdentifier {
case .toggleButton:
var toggleBtn: NSControl = NSControl()
if #available(OSX 10.15, *) {
let switchButton = NSSwitch()
switchButton.state = .on
switchButton.action = #selector(self.toggleEnable)
switchButton.target = self
toggleBtn = switchButton
} else {
let button: NSButton = NSButton()
button.setButtonType(.switch)
button.state = .on
button.title = ""
button.action = #selector(self.toggleEnable)
button.isBordered = false
button.isTransparent = false
button.target = self
toggleBtn = button
}
self.toggleButton = toggleBtn
let toolbarItem = NSToolbarItem(itemIdentifier: itemIdentifier)
toolbarItem.label = "Toggle the module"
toolbarItem.paletteLabel = "Toggle the module"
toolbarItem.toolTip = "Toggle the module"
toolbarItem.view = toggleBtn
return toolbarItem
default:
return nil
}
}
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [.flexibleSpace, .toggleButton]
}
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [.flexibleSpace, .toggleButton]
}
@objc private func toggleSettingsHandler(_ notification: Notification) {
if !self.isVisible {
self.setIsVisible(true)
@@ -82,112 +169,102 @@ class SettingsWindow: NSWindow, NSWindowDelegate {
}
if let name = notification.userInfo?["module"] as? String {
self.vc.openMenu(name)
self.sidebarView.openMenu(name)
}
}
public func setModules() {
self.vc.setModules(modules)
if !self.pauseState && modules.filter({ $0.enabled != false && $0.available != false && !$0.menuBar.widgets.filter({ $0.isActive }).isEmpty }).isEmpty {
self.setIsVisible(true)
}
}
public func openMenu(_ title: String) {
self.vc.openMenu(title)
}
override func mouseUp(with: NSEvent) {
NotificationCenter.default.post(name: .clickInSettings, object: nil, userInfo: nil)
}
}
private class SettingsView: NSSplitViewController {
private var modules: [Module] = []
private let split: NSSplitView = SplitView()
private let sidebar: SidebarView = SidebarView(frame: NSRect(x: 0, y: 0, width: 180, height: 480))
private let main: MainView = MainView(frame: NSRect(x: 0, y: 0, width: 540, height: 480))
private var dashboard: NSView = Dashboard()
private var settings: ApplicationSettings = ApplicationSettings()
init() {
super.init(nibName: nil, bundle: nil)
self.splitView = self.split
let sidebarVC: NSViewController = NSViewController(nibName: nil, bundle: nil)
sidebarVC.view = self.sidebar
let mainVC: NSViewController = NSViewController(nibName: nil, bundle: nil)
mainVC.view = self.main
let sidebarItem = NSSplitViewItem(sidebarWithViewController: sidebarVC)
let contentItem = NSSplitViewItem(viewController: mainVC)
self.addSplitViewItem(sidebarItem)
self.addSplitViewItem(contentItem)
self.splitViewItems[0].canCollapse = false
NotificationCenter.default.addObserver(self, selector: #selector(menuCallback), name: .openModuleSettings, object: nil)
self.openMenu("Dashboard")
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func openMenu(_ title: String) {
self.sidebar.openMenu(title)
}
public func setModules(_ list: [Module]) {
self.sidebar.setModules(list)
self.modules = list
}
@objc private func menuCallback(_ notification: Notification) {
if let title = notification.userInfo?["module"] as? String {
var view: NSView = NSView()
if let detectedModule = self.modules.first(where: { $0.config.name == title }) {
if let detectedModule = modules.first(where: { $0.config.name == title }) {
if let v = detectedModule.settings {
view = v
}
self.activeModuleName = detectedModule.config.name
toggleNSControlState(self.toggleButton, state: detectedModule.enabled ? .on : .off)
self.toggleButton?.isHidden = false
} else if title == "Dashboard" {
view = self.dashboard
self.toggleButton?.isHidden = true
} else if title == "Settings" {
self.settings.viewWillAppear()
view = self.settings
self.toggleButton?.isHidden = true
}
self.main.setView(view)
self.sidebar.openMenu(title)
self.title = localizedString(title)
self.mainView.setView(view)
self.sidebarView.openMenu(title)
}
}
@objc private func toggleEnable(_ sender: NSControl) {
guard let moduleName = self.activeModuleName else { return }
NotificationCenter.default.post(name: .toggleModule, object: nil, userInfo: ["module": moduleName, "state": controlState(sender)])
}
@objc private func externalModuleToggle(_ notification: Notification) {
if let name = notification.userInfo?["module"] as? String, name == self.activeModuleName {
if let state = notification.userInfo?["state"] as? Bool {
toggleNSControlState(self.toggleButton, state: state ? .on : .off)
}
}
}
public func setModules() {
self.sidebarView.setModules(modules)
if !self.pauseState && modules.filter({ $0.enabled != false && $0.available != false && !$0.menuBar.widgets.filter({ $0.isActive }).isEmpty }).isEmpty {
self.setIsVisible(true)
}
self.sidebarView.openMenu("Network")
}
}
private class SplitView: NSSplitView, NSSplitViewDelegate {
init() {
// MARK: - MainView
private class MainView: NSView {
public let container: NSStackView
override init(frame: NSRect) {
self.container = NSStackView(frame: NSRect(x: 0, y: 0, width: frame.width, height: frame.height))
let foreground = NSVisualEffectView(frame: NSRect(x: 0, y: 0, width: frame.width, height: frame.height))
foreground.blendingMode = .withinWindow
if #available(macOS 10.14, *) {
foreground.material = .windowBackground
} else {
foreground.material = .popover
}
foreground.state = .active
super.init(frame: NSRect.zero)
self.isVertical = true
self.delegate = self
self.container.translatesAutoresizingMaskIntoConstraints = false
self.widthAnchor.constraint(equalToConstant: SettingsWindow.size.width).isActive = true
self.heightAnchor.constraint(equalToConstant: SettingsWindow.size.height).isActive = true
self.addSubview(foreground, positioned: .below, relativeTo: .none)
self.addSubview(self.container)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func cursorUpdate(with event: NSEvent) {
NSCursor.arrow.set()
public func setView(_ view: NSView) {
self.container.subviews.forEach{ $0.removeFromSuperview() }
self.container.addArrangedSubview(view)
NSLayoutConstraint.activate([
view.leftAnchor.constraint(equalTo: self.container.leftAnchor),
view.rightAnchor.constraint(equalTo: self.container.rightAnchor),
view.topAnchor.constraint(equalTo: self.container.topAnchor),
view.bottomAnchor.constraint(equalTo: self.container.bottomAnchor)
])
}
}
// MARK: - Sidebar
private class SidebarView: NSStackView {
private let scrollView: ScrollableStackView
@@ -433,31 +510,3 @@ private class MenuItem: NSView {
self.active = false
}
}
private class MainView: NSView {
override init(frame: NSRect) {
super.init(frame: frame)
self.wantsLayer = true
let foreground = NSVisualEffectView(frame: NSRect(x: 0, y: 0, width: frame.width, height: frame.height))
foreground.blendingMode = .withinWindow
if #available(macOS 10.14, *) {
foreground.material = .windowBackground
} else {
foreground.material = .popover
}
foreground.state = .active
self.addSubview(foreground, positioned: .below, relativeTo: .none)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func setView(_ view: NSView) {
self.subviews.filter{ !($0 is NSVisualEffectView) }.forEach{ $0.removeFromSuperview() }
self.addSubview(view)
}
}