+/*
+ Copyright (C) 2024 Rubén Beltrán del Río
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see https://captura.tranquil.systems.
+ */
import Cocoa
import Combine
self.contentView as! RecordingContentView
}
- init(_ button: NSRect?) {
+ init(_ configuration: CaptureSessionConfiguration, _ button: NSRect?) {
- let screens = NSScreen.screens
- var boundingBox = NSZeroRect
- for screen in screens {
- boundingBox = NSUnionRect(boundingBox, screen.frame)
- }
+ let boundingBox = NSScreen.screenWithMouse?.frame ?? NSZeroRect
super.init(
contentRect: boundingBox,
backing: .buffered,
defer: false)
+
self.isReleasedWhenClosed = false
self.collectionBehavior = [.canJoinAllSpaces]
- self.center()
self.isMovableByWindowBackground = false
self.isMovable = false
+ self.canHide = false
self.titlebarAppearsTransparent = true
self.setFrame(boundingBox, display: true)
self.titleVisibility = .hidden
- let recordingView = RecordingContentView()
+ let recordingView = RecordingContentView(configuration, frame: boundingBox, button: button ?? NSZeroRect)
recordingView.frame = boundingBox
- recordingView.button = button
self.contentView = recordingView
self.backgroundColor = NSColor(white: 1.0, alpha: 0.001)
+ // uncomment below to debug window placement visually
+ // self.backgroundColor = NSColor(red: 1.0, green: 0.0, blue: 1.0, alpha: 0.5)
self.level = .screenSaver
self.isOpaque = false
self.hasShadow = false
- self.makeKeyAndOrderFront(nil)
}
// MARK: - Window Behavior Overrides
class RecordingContentView: NSView {
+ init(_ configuration: CaptureSessionConfiguration, frame: NSRect, button: NSRect) {
+ self.buttonSize = button.size
+ var buttonOffset = NSPoint()
+ for screen in NSScreen.screens {
+ if screen.frame.intersects(button) {
+ let relativeY = screen.frame.height - (button.minY - screen.frame.minY)
+ let relativeX = screen.frame.width - (button.minX - screen.frame.minX)
+ buttonOffset = NSPoint(x: relativeX, y: relativeY)
+ }
+ }
+ self.buttonOffset = buttonOffset
+ super.init(frame: frame)
+ preventResize = configuration.preventResize
+ preventMove = configuration.preventMove
+ autoStart = configuration.autoStart
+
+ self.bounds = frame
+ self.button = NSRect(x: frame.maxX - buttonOffset.x, y: frame.maxY - buttonOffset.y, width: buttonSize.width, height: buttonSize.height)
+
+ if configuration.x != nil || configuration.y != nil || configuration.width != nil || configuration.height != nil {
+ box = NSRect(
+ x: configuration.x ?? Int(frame.width / 2.0),
+ y: configuration.y ?? Int(frame.height / 2.0),
+ width: configuration.width ?? 400,
+ height: configuration.height ?? 400
+ )
+ }
+
+ if autoStart {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
+ NotificationCenter.default.post(name: .startRecording, object: nil, userInfo: nil)
+ }
+ }
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ private let buttonSize: NSSize
+ private let buttonOffset: NSPoint
public var button: NSRect? = nil
@Published public var box: NSRect? = nil
public var state: RecordingWindowState = .idle
private var mouseLocation: NSPoint = NSPoint()
private var origin: NSPoint = NSPoint()
private var boxOrigin: NSPoint = NSPoint()
+ private var preventResize = false
+ private var preventMove = false
+ private var autoStart = false
private var resizeBox: NSRect? {
if let box {
self.addTrackingArea(trackingArea)
}
+ override func mouseExited(with event: NSEvent) {
+ if state == .idle && box == nil {
+ self.moveWindow()
+ }
+ }
+
override func mouseMoved(with event: NSEvent) {
self.mouseLocation = self.convert(event.locationInWindow, from: nil)
}
}
- if resizeBox!.contains(origin) {
+ if resizeBox!.contains(origin) && !preventResize {
self.origin = NSPoint(x: box.minX, y: box.maxY)
state = .resizing
return
}
- if box.contains(origin) {
+ if box.contains(origin) && !preventMove {
state = .moving
self.boxOrigin = NSPoint(x: box.origin.x, y: box.origin.y)
return
}
}
+ if preventResize || preventMove {
+ return
+ }
+
state = .drawing
}
let dashLength: CGFloat = 5.0
let lineWidth = 0.5
+// Uncomment below to debug button placement visually
+// if let button {
+// let buttonPath = NSBezierPath()
+// buttonPath.move(to: NSPoint(x: button.minX, y: button.minY))
+// buttonPath.line(to: NSPoint(x: button.maxX, y: button.minY))
+// buttonPath.line(to: NSPoint(x: button.maxX, y: button.maxY))
+// buttonPath.line(to: NSPoint(x: button.minX, y: button.maxY))
+// buttonPath.line(to: NSPoint(x: button.minX, y: button.minY))
+// NSColor(red: 1, green: 0, blue: 1, alpha: 1).setFill()
+// buttonPath.fill()
+// }
+
if state == .idle && box == nil {
let blackLine = NSBezierPath()
blackLine.lineWidth = lineWidth
text.draw(in: textRect, withAttributes: textAttributes)
}
+
+ private func moveWindow() {
+ let screen = NSScreen.screenWithMouse
+ if let currentScreen = self.window?.screen {
+ if currentScreen != screen {
+ let frame = screen?.frame ?? NSZeroRect
+ self.frame = CGRect(origin: NSZeroPoint, size: frame.size)
+ self.bounds = CGRect(origin: NSZeroPoint, size: frame.size)
+ self.updateTrackingAreas()
+
+ if let window = self.window {
+ self.bounds = frame
+ self.button = NSRect(x: frame.maxX - buttonOffset.x, y: frame.maxY - buttonOffset.y, width: buttonSize.width, height: buttonSize.height)
+ window.setFrame(frame, display: true, animate: false)
+ window.makeKeyAndOrderFront(nil)
+ window.orderFrontRegardless()
+ }
+ }
+ }
+ }
}