diff options
Diffstat (limited to 'video/out/mac/view.swift')
-rw-r--r-- | video/out/mac/view.swift | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/video/out/mac/view.swift b/video/out/mac/view.swift new file mode 100644 index 0000000000..b82151ba96 --- /dev/null +++ b/video/out/mac/view.swift @@ -0,0 +1,218 @@ +/* + * 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 View: NSView, CALayerDelegate { + unowned var common: Common + var input: InputHelper? { return common.input } + + var tracker: NSTrackingArea? + var hasMouseDown: Bool = false + + override var isFlipped: Bool { return true } + override var acceptsFirstResponder: Bool { return true } + + init(frame: NSRect, common com: Common) { + common = com + super.init(frame: frame) + autoresizingMask = [.width, .height] + wantsBestResolutionOpenGLSurface = true + wantsExtendedDynamicRangeOpenGLSurface = true + registerForDraggedTypes([ .fileURL, .URL, .string ]) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func updateTrackingAreas() { + if let tracker = self.tracker { + removeTrackingArea(tracker) + } + + tracker = NSTrackingArea(rect: bounds, + options: [.activeAlways, .mouseEnteredAndExited, .mouseMoved, .enabledDuringMouseDrag], + owner: self, userInfo: nil) + // here tracker is guaranteed to be none-nil + addTrackingArea(tracker!) + + if containsMouseLocation() { + input?.put(key: SWIFT_KEY_MOUSE_LEAVE) + } + } + + override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { + guard let types = sender.draggingPasteboard.types else { return [] } + if types.contains(.fileURL) || types.contains(.URL) || types.contains(.string) { + return .copy + } + return [] + } + + func isURL(_ str: String) -> Bool { + guard let regex = try? NSRegularExpression(pattern: "^(https?|ftp)://[^\\s/$.?#].[^\\s]*$", + options: .caseInsensitive) else { + return false + } + let isURL = regex.numberOfMatches(in: str, + options: [], + range: NSRange(location: 0, length: str.count)) + return isURL > 0 + } + + override func performDragOperation(_ sender: NSDraggingInfo) -> Bool { + let pb = sender.draggingPasteboard + guard let types = pb.types else { return false } + var files: [String] = [] + + if types.contains(.fileURL) || types.contains(.URL) { + guard let urls = pb.readObjects(forClasses: [NSURL.self]) as? [URL] else { return false } + files = urls.map { $0.absoluteString } + } else if types.contains(.string) { + guard let str = pb.string(forType: .string) else { return false } + files = str.components(separatedBy: "\n").compactMap { + let url = $0.trimmingCharacters(in: .whitespacesAndNewlines) + let path = (url as NSString).expandingTildeInPath + if isURL(url) { return url } + if path.starts(with: "/") { return path } + return nil + } + } + if files.isEmpty { return false } + input?.open(files: files) + return true + } + + override func acceptsFirstMouse(for event: NSEvent?) -> Bool { + return true + } + + override func becomeFirstResponder() -> Bool { + return true + } + + override func resignFirstResponder() -> Bool { + return true + } + + override func mouseEntered(with event: NSEvent) { + if input?.mouseEnabled() ?? true { + input?.put(key: SWIFT_KEY_MOUSE_ENTER) + } + common.updateCursorVisibility() + } + + override func mouseExited(with event: NSEvent) { + if input?.mouseEnabled() ?? true { + input?.put(key: SWIFT_KEY_MOUSE_LEAVE) + } + common.titleBar?.hide() + common.setCursorVisibility(true) + } + + override func mouseMoved(with event: NSEvent) { + signalMouseMovement(event) + common.titleBar?.show() + } + + override func mouseDragged(with event: NSEvent) { + signalMouseMovement(event) + } + + override func mouseDown(with event: NSEvent) { + hasMouseDown = event.clickCount <= 1 + input?.processMouse(event: event) + } + + override func mouseUp(with event: NSEvent) { + hasMouseDown = false + common.window?.isMoving = false + input?.processMouse(event: event) + } + + override func rightMouseDown(with event: NSEvent) { + hasMouseDown = event.clickCount <= 1 + input?.processMouse(event: event) + } + + override func rightMouseUp(with event: NSEvent) { + hasMouseDown = false + input?.processMouse(event: event) + } + + override func otherMouseDown(with event: NSEvent) { + hasMouseDown = event.clickCount <= 1 + input?.processMouse(event: event) + } + + override func otherMouseUp(with event: NSEvent) { + hasMouseDown = false + input?.processMouse(event: event) + } + + override func magnify(with event: NSEvent) { + common.window?.isAnimating = event.phase != .ended + event.phase == .ended ? common.windowDidEndLiveResize() : common.windowWillStartLiveResize() + common.window?.addWindowScale(Double(event.magnification)) + } + + func signalMouseMovement(_ event: NSEvent) { + var point = convert(event.locationInWindow, from: nil) + point = convertToBacking(point) + point.y = -point.y + + common.window?.updateMovableBackground(point) + if !(common.window?.isMoving ?? false) { + input?.setMouse(position: point) + } + } + + override func scrollWheel(with event: NSEvent) { + input?.processWheel(event: event) + } + + func containsMouseLocation() -> Bool { + var topMargin: CGFloat = 0.0 + let menuBarHeight = NSApp.mainMenu?.menuBarHeight ?? 23.0 + + guard let window = common.window else { return false } + guard var vF = window.screen?.frame else { return false } + + if window.isInFullscreen && (menuBarHeight > 0) { + topMargin = TitleBar.height + 1 + menuBarHeight + } + + vF.size.height -= topMargin + + let vFW = window.convertFromScreen(vF) + let vFV = convert(vFW, from: nil) + let pt = convert(window.mouseLocationOutsideOfEventStream, from: nil) + + var clippedBounds = bounds.intersection(vFV) + if !window.isInFullscreen { + clippedBounds.origin.y += TitleBar.height + clippedBounds.size.height -= TitleBar.height + } + return clippedBounds.contains(pt) + } + + func canHideCursor() -> Bool { + guard let window = common.window else { return false } + return !hasMouseDown && containsMouseLocation() && window.isKeyWindow + } +} |