diff options
Diffstat (limited to 'video/out/mac/title_bar.swift')
-rw-r--r-- | video/out/mac/title_bar.swift | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/video/out/mac/title_bar.swift b/video/out/mac/title_bar.swift new file mode 100644 index 0000000..764c1ff --- /dev/null +++ b/video/out/mac/title_bar.swift @@ -0,0 +1,229 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +import Cocoa + +class TitleBar: NSVisualEffectView { + unowned var common: Common + var mpv: MPVHelper? { get { return common.mpv } } + + var systemBar: NSView? { + get { return common.window?.standardWindowButton(.closeButton)?.superview } + } + static var height: CGFloat { + get { return NSWindow.frameRect(forContentRect: CGRect.zero, styleMask: .titled).size.height } + } + var buttons: [NSButton] { + get { return ([.closeButton, .miniaturizeButton, .zoomButton] as [NSWindow.ButtonType]).compactMap { common.window?.standardWindowButton($0) } } + } + + override var material: NSVisualEffectView.Material { + get { return super.material } + set { + super.material = newValue + // fix for broken deprecated materials + if material == .light || material == .dark || material == .mediumLight || + material == .ultraDark + { + state = .active + } else { + state = .followsWindowActiveState + } + + } + } + + init(frame: NSRect, window: NSWindow, common com: Common) { + let f = NSMakeRect(0, frame.size.height - TitleBar.height, + frame.size.width, TitleBar.height + 1) + common = com + super.init(frame: f) + buttons.forEach { $0.isHidden = true } + isHidden = true + alphaValue = 0 + blendingMode = .withinWindow + autoresizingMask = [.width, .minYMargin] + systemBar?.alphaValue = 0 + state = .followsWindowActiveState + wantsLayer = true + + window.contentView?.addSubview(self, positioned: .above, relativeTo: nil) + window.titlebarAppearsTransparent = true + window.styleMask.insert(.fullSizeContentView) + set(appearance: Int(mpv?.macOpts.macos_title_bar_appearance ?? 0)) + set(material: Int(mpv?.macOpts.macos_title_bar_material ?? 0)) + set(color: mpv?.macOpts.macos_title_bar_color ?? "#00000000") + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // catch these events so they are not propagated to the underlying view + override func mouseDown(with event: NSEvent) { } + + override func mouseUp(with event: NSEvent) { + if event.clickCount > 1 { + let def = UserDefaults.standard + var action = def.string(forKey: "AppleActionOnDoubleClick") + + // macOS 10.10 and earlier + if action == nil { + action = def.bool(forKey: "AppleMiniaturizeOnDoubleClick") == true ? + "Minimize" : "Maximize" + } + + if action == "Minimize" { + window?.miniaturize(self) + } else if action == "Maximize" { + window?.zoom(self) + } + } + + common.window?.isMoving = false + } + + func set(appearance: Any) { + if appearance is Int { + window?.appearance = appearanceFrom(string: String(appearance as? Int ?? 0)) + } else { + window?.appearance = appearanceFrom(string: appearance as? String ?? "auto") + } + } + + func set(material: Any) { + if material is Int { + self.material = materialFrom(string: String(material as? Int ?? 0)) + } else { + self.material = materialFrom(string: material as? String ?? "titlebar") + } + } + + func set(color: Any) { + if color is String { + layer?.backgroundColor = NSColor(hex: color as? String ?? "#00000000").cgColor + } else { + let col = color as? m_color ?? m_color(r: 0, g: 0, b: 0, a: 0) + let red = CGFloat(col.r)/255 + let green = CGFloat(col.g)/255 + let blue = CGFloat(col.b)/255 + let alpha = CGFloat(col.a)/255 + layer?.backgroundColor = NSColor(calibratedRed: red, green: green, + blue: blue, alpha: alpha).cgColor + } + } + + func show() { + guard let window = common.window else { return } + if !window.border && !window.isInFullscreen { return } + let loc = common.view?.convert(window.mouseLocationOutsideOfEventStream, from: nil) + + buttons.forEach { $0.isHidden = false } + NSAnimationContext.runAnimationGroup({ (context) -> Void in + context.duration = 0.20 + systemBar?.animator().alphaValue = 1 + if !window.isInFullscreen && !window.isAnimating { + animator().alphaValue = 1 + isHidden = false + } + }, completionHandler: nil ) + + if loc?.y ?? 0 > TitleBar.height { + hideDelayed() + } else { + NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(hide), object: nil) + } + } + + @objc func hide(_ duration: TimeInterval = 0.20) { + guard let window = common.window else { return } + if window.isInFullscreen && !window.isAnimating { + alphaValue = 0 + isHidden = true + return + } + NSAnimationContext.runAnimationGroup({ (context) -> Void in + context.duration = duration + systemBar?.animator().alphaValue = 0 + animator().alphaValue = 0 + }, completionHandler: { + self.buttons.forEach { $0.isHidden = true } + self.isHidden = true + }) + } + + func hideDelayed() { + NSObject.cancelPreviousPerformRequests(withTarget: self, + selector: #selector(hide), + object: nil) + perform(#selector(hide), with: nil, afterDelay: 0.5) + } + + func appearanceFrom(string: String) -> NSAppearance? { + switch string { + case "1", "aqua": + return NSAppearance(named: .aqua) + case "2", "darkAqua": + return NSAppearance(named: .darkAqua) + case "3", "vibrantLight": + return NSAppearance(named: .vibrantLight) + case "4", "vibrantDark": + return NSAppearance(named: .vibrantDark) + case "5", "aquaHighContrast": + return NSAppearance(named: .accessibilityHighContrastAqua) + case "6", "darkAquaHighContrast": + return NSAppearance(named: .accessibilityHighContrastDarkAqua) + case "7", "vibrantLightHighContrast": + return NSAppearance(named: .accessibilityHighContrastVibrantLight) + case "8", "vibrantDarkHighContrast": + return NSAppearance(named: .accessibilityHighContrastVibrantDark) + case "0", "auto": fallthrough + default: + return nil + } + + + let style = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") + return appearanceFrom(string: style == nil ? "aqua" : "vibrantDark") + } + + func materialFrom(string: String) -> NSVisualEffectView.Material { + switch string { + case "0", "titlebar": return .titlebar + case "1", "selection": return .selection + case "2,", "menu": return .menu + case "3", "popover": return .popover + case "4", "sidebar": return .sidebar + case "5,", "headerView": return .headerView + case "6", "sheet": return .sheet + case "7", "windowBackground": return .windowBackground + case "8", "hudWindow": return .hudWindow + case "9", "fullScreen": return .fullScreenUI + case "10", "toolTip": return .toolTip + case "11", "contentBackground": return .contentBackground + case "12", "underWindowBackground": return .underWindowBackground + case "13", "underPageBackground": return .underPageBackground + case "14", "dark": return .dark + case "15", "light": return .light + case "16", "mediumLight": return .mediumLight + case "17", "ultraDark": return .ultraDark + default: break + } + + return .titlebar + } +} |