cocoa-cb: change border and borderless window styling

the title bar is now within the window bounds instead of outside. same
as QuickTime Player. it supports several standard styles, two dark and
two light ones. additionally we have properly rounded corners now and
the borderless window also has the proper window shadow.

Also make the earliest supported macOS version 10.10.

Fixes #4789, #3944
This commit is contained in:
Akemi 2018-02-16 13:07:15 +01:00 committed by Kevin Mitchell
parent a4c436bac2
commit 938ad6ebc0
9 changed files with 158 additions and 20 deletions

View File

@ -4852,6 +4852,18 @@ The following video options are currently all specific to ``--vo=gpu`` and
OS X only.
``--macos-title-bar-style=<dark|ultradark|light|mediumlight|auto>``
Sets the styling of the title bar (default: dark).
OS X and cocoa-cb only
:dark: Dark title bar with vibrancy, a subtle blurring effect that
dynamically blends the background (Video) into the title bar.
:ultradark: Darker title bar with vibrancy (like QuickTime Player).
:light: Bright title bar with vibrancy.
:mediumlight: Less bright title bar with vibrancy.
:auto: Detects the system settings and sets the title bar styling
appropriately, either ultradark or mediumlight.
``--android-surface-size=<WxH>``
Set dimensions of the rendering surface used by the Android gpu context.
Needs to be set by the embedding application if the dimensions change during

View File

@ -87,6 +87,7 @@ extern const struct m_sub_options d3d11_conf;
extern const struct m_sub_options d3d11va_conf;
extern const struct m_sub_options angle_conf;
extern const struct m_sub_options cocoa_conf;
extern const struct m_sub_options macos_conf;
extern const struct m_sub_options android_conf;
static const struct m_sub_options screenshot_conf = {
@ -735,6 +736,10 @@ const m_option_t mp_opts[] = {
OPT_SUBSTRUCT("", cocoa_opts, cocoa_conf, 0),
#endif
#if HAVE_MACOS_COCOA_CB
OPT_SUBSTRUCT("", macos_opts, macos_conf, 0),
#endif
#if HAVE_ANDROID
OPT_SUBSTRUCT("", android_opts, android_conf, 0),
#endif

View File

@ -341,6 +341,7 @@ typedef struct MPOpts {
struct d3d11_opts *d3d11_opts;
struct d3d11va_opts *d3d11va_opts;
struct cocoa_opts *cocoa_opts;
struct macos_opts *macos_opts;
struct android_opts *android_opts;
struct dvd_opts *dvd_opts;

View File

@ -39,6 +39,7 @@ class MPVHelper: NSObject {
mpv_observe_property(mpvHandle, 0, "ontop", MPV_FORMAT_FLAG)
mpv_observe_property(mpvHandle, 0, "border", MPV_FORMAT_FLAG)
mpv_observe_property(mpvHandle, 0, "keepaspect-window", MPV_FORMAT_FLAG)
mpv_observe_property(mpvHandle, 0, "macos-title-bar-style", MPV_FORMAT_STRING)
}
func setGLCB() {

View File

@ -23,6 +23,7 @@
#include "common/msg.h"
#include "input/input.h"
#include "player/client.h"
#include "options/m_config.h"
#import "osdep/macosx_application_objc.h"
#include "osdep/macosx_compat.h"
@ -39,6 +40,21 @@
#define MPV_PROTOCOL @"mpv://"
struct macos_opts {
int macos_title_bar_style;
};
#define OPT_BASE_STRUCT struct macos_opts
const struct m_sub_options macos_conf = {
.opts = (const struct m_option[]) {
OPT_CHOICE("macos-title-bar-style", macos_title_bar_style, 0,
({"dark", 0}, {"ultradark", 1}, {"light", 2},
{"mediumlight", 3}, {"auto", 4})),
{0}
},
.size = sizeof(struct macos_opts),
};
// Whether the NSApplication singleton was created. If this is false, we are
// running in libmpv mode, and cocoa_main() was never called.
static bool application_instantiated;

View File

@ -110,12 +110,14 @@ class EventsView: NSView {
if mpv.getBoolProperty("input-cursor") {
cocoa_put_key_with_modifiers(SWIFT_KEY_MOUSE_LEAVE, 0)
}
cocoaCB.window.hideTitleBar()
}
override func mouseMoved(with event: NSEvent) {
if mpv != nil && mpv.getBoolProperty("input-cursor") {
signalMouseMovement(event)
}
cocoaCB.window.showTitleBar()
}
override func mouseDragged(with event: NSEvent) {
@ -233,8 +235,7 @@ class EventsView: NSView {
let menuBarHeight = NSApp.mainMenu!.menuBarHeight
if cocoaCB.window.isInFullscreen && (menuBarHeight > 0) {
let titleBar = NSWindow.frameRect(forContentRect: CGRect.zero, styleMask: .titled)
topMargin = titleBar.size.height + 1 + menuBarHeight
topMargin = cocoaCB.window.titleBarHeight + 1 + menuBarHeight
}
var vF = window!.screen!.frame
@ -244,7 +245,11 @@ class EventsView: NSView {
let vFV = convert(vFW, from: nil)
let pt = convert(window!.mouseLocationOutsideOfEventStream, from: nil)
let clippedBounds = bounds.intersection(vFV)
var clippedBounds = bounds.intersection(vFV)
if !cocoaCB.window.isInFullscreen {
clippedBounds.origin.y += cocoaCB.window.titleBarHeight
clippedBounds.size.height -= cocoaCB.window.titleBarHeight
}
return clippedBounds.contains(pt)
}

View File

@ -49,6 +49,21 @@ class Window: NSWindow, NSWindowDelegate {
}
}
var border: Bool = true {
didSet { if !border { hideTitleBar() } }
}
var titleBarEffect: NSVisualEffectView?
var titleBar: NSView {
get { return (standardWindowButton(.closeButton)?.superview)! }
}
var titleBarHeight: CGFloat {
get { return NSWindow.frameRect(forContentRect: CGRect.zero, styleMask: .titled).size.height }
}
var titleButtons: [NSButton] {
get { return ([.closeButton, .miniaturizeButton, .zoomButton] as [NSWindow.ButtonType]).flatMap { standardWindowButton($0) } }
}
override var canBecomeKey: Bool { return true }
override var canBecomeMain: Bool { return true }
@ -95,6 +110,94 @@ class Window: NSWindow, NSWindowDelegate {
}
}
func initTitleBar() {
var f = contentView!.bounds
f.origin.y = f.size.height - titleBarHeight
f.size.height = titleBarHeight
styleMask.insert(.fullSizeContentView)
titleBar.alphaValue = 0
titlebarAppearsTransparent = true
titleBarEffect = NSVisualEffectView(frame: f)
titleBarEffect!.alphaValue = 0
titleBarEffect!.blendingMode = .withinWindow
titleBarEffect!.autoresizingMask = [.viewWidthSizable, .viewMinYMargin]
setTitleBarStyle(mpv.getStringProperty("macos-title-bar-style") ?? "dark")
contentView!.addSubview(titleBarEffect!, positioned: .above, relativeTo: nil)
border = mpv.getBoolProperty("border")
}
func setTitleBarStyle(_ style: String) {
var effect = style
if effect == "auto" {
let systemStyle = UserDefaults.standard.string(forKey: "AppleInterfaceStyle")
effect = systemStyle == nil ? "mediumlight" : "ultradark"
}
switch effect {
case "mediumlight":
appearance = NSAppearance(named: NSAppearanceNameVibrantLight)
titleBarEffect!.material = .titlebar
titleBarEffect!.state = .followsWindowActiveState
case "light":
appearance = NSAppearance(named: NSAppearanceNameVibrantLight)
titleBarEffect!.material = .light
titleBarEffect!.state = .active
case "ultradark":
appearance = NSAppearance(named: NSAppearanceNameVibrantDark)
titleBarEffect!.material = .titlebar
titleBarEffect!.state = .followsWindowActiveState
case "dark": fallthrough
default:
appearance = NSAppearance(named: NSAppearanceNameVibrantDark)
titleBarEffect!.material = .dark
titleBarEffect!.state = .active
}
}
func showTitleBar() {
if !border && !isInFullscreen { return }
let loc = cocoaCB.view.convert(mouseLocationOutsideOfEventStream, from: nil)
titleButtons.forEach { $0.isHidden = false }
NSAnimationContext.runAnimationGroup({ (context) -> Void in
context.duration = 0.20
titleBar.animator().alphaValue = 1
if !isInFullscreen && !isAnimating {
titleBarEffect!.animator().alphaValue = 1
}
}, completionHandler: nil )
if loc.y > titleBarHeight {
hideTitleBarDelayed()
} else {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(hideTitleBar), object: nil)
}
}
func hideTitleBar() {
if isInFullscreen && !isAnimating {
titleBarEffect!.alphaValue = 0
return
}
NSAnimationContext.runAnimationGroup({ (context) -> Void in
context.duration = 0.20
titleBar.animator().alphaValue = 0
titleBarEffect!.animator().alphaValue = 0
}, completionHandler: {
self.titleButtons.forEach { $0.isHidden = true }
})
}
func hideTitleBarDelayed() {
NSObject.cancelPreviousPerformRequests(withTarget: self,
selector: #selector(hideTitleBar),
object: nil)
perform(#selector(hideTitleBar), with: nil, afterDelay: 0.5)
}
override func toggleFullScreen(_ sender: Any?) {
if isAnimating {
return
@ -146,6 +249,7 @@ class Window: NSWindow, NSWindowDelegate {
let cRect = contentRect(forFrameRect: frame)
var intermediateFrame = aspectFit(rect: cRect, in: targetScreen!.frame)
intermediateFrame = frameRect(forContentRect: intermediateFrame)
hideTitleBar()
NSAnimationContext.runAnimationGroup({ (context) -> Void in
context.duration = duration - 0.05
@ -156,6 +260,7 @@ class Window: NSWindow, NSWindowDelegate {
func window(_ window: NSWindow, startCustomAnimationToExitFullScreenWithDuration duration: TimeInterval) {
let newFrame = calculateWindowPosition(for: targetScreen!, withoutBounds: targetScreen == screen)
let intermediateFrame = aspectFit(rect: newFrame, in: screen!.frame)
hideTitleBar()
setFrame(intermediateFrame, display: true)
NSAnimationContext.runAnimationGroup({ (context) -> Void in
@ -169,6 +274,7 @@ class Window: NSWindow, NSWindowDelegate {
cocoaCB.flagEvents(VO_EVENT_FULLSCREEN_STATE)
cocoaCB.updateCusorVisibility()
endAnimation(frame)
showTitleBar()
}
func windowDidExitFullScreen(_ notification: Notification) {
@ -222,18 +328,6 @@ class Window: NSWindow, NSWindowDelegate {
cocoaCB.layer.neededFlips += 1
}
func setBorder(_ state: Bool) {
if styleMask.contains(.titled) != state {
if state {
styleMask.remove(.borderless)
styleMask.insert(.titled)
} else {
styleMask.remove(.titled)
styleMask.insert(.borderless)
}
}
}
func setOnTop(_ state: Bool) {
if state {
let ontopLevel = mpv.getStringProperty("ontop-level") ?? "window"

View File

@ -99,12 +99,12 @@ class CocoaCB: NSObject {
screen: targetScreen, cocoaCB: self)
win.title = window.title
win.setOnTop(mpv.getBoolProperty("ontop"))
win.setBorder(mpv.getBoolProperty("border"))
win.keepAspect = mpv.getBoolProperty("keepaspect-window")
window.close()
window = win
window.contentView!.addSubview(view)
view.frame = window.contentView!.frame
window.initTitleBar()
setAppIcon()
window.isRestorable = false
@ -486,7 +486,7 @@ class CocoaCB: NSObject {
switch String(cString: property.name) {
case "border":
if let data = MPVHelper.mpvFlagToBool(property.data) {
window.setBorder(data)
window.border = data
}
case "ontop":
if let data = MPVHelper.mpvFlagToBool(property.data) {
@ -496,6 +496,10 @@ class CocoaCB: NSObject {
if let data = MPVHelper.mpvFlagToBool(property.data) {
window.keepAspect = data
}
case "macos-title-bar-style":
if let data = MPVHelper.mpvStringArrayToString(property.data) {
window.setTitleBarStyle(data)
}
default:
break
}

View File

@ -8,8 +8,9 @@ def __run(cmd):
return ""
def __add_swift_flags(ctx):
ctx.env.SWIFT_FLAGS = ('-frontend -c -sdk %s -enable-objc-interop -emit-objc-header'
' -emit-module -parse-as-library') % (ctx.env.MACOS_SDK)
ctx.env.SWIFT_FLAGS = ('-frontend -c -sdk %s -enable-objc-interop'
' -emit-objc-header -parse-as-library'
' -target x86_64-apple-macosx10.10') % (ctx.env.MACOS_SDK)
swift_version = __run([ctx.env.SWIFT, '-version']).split(' ')[3].split('.')[:2]
major, minor = [int(n) for n in swift_version]
@ -31,7 +32,6 @@ def __find_swift_library(ctx):
'Toolchains/XcodeDefault.xctoolchain/usr/lib/swift_static/macosx',
'usr/lib/swift_static/macosx'
]
dev_path = __run('xcode-select -p')[1:]
dev_path = __run(['xcode-select', '-p'])[1:]
ctx.start_msg('Checking for Swift Library')