From: Ruben Beltran del Rio Date: Tue, 25 Jul 2023 21:19:13 +0000 (+0200) Subject: Add resize, keyboard shortcuts, menu items X-Git-Tag: 1.0.0~13 X-Git-Url: https://git.r.bdr.sh/rbdr/captura/commitdiff_plain/e834022c9b363804d36045892a305204f2019216?hp=242203487a56df9edd1aa6eceb106461ef62483c Add resize, keyboard shortcuts, menu items --- diff --git a/Captura.xcodeproj/project.pbxproj b/Captura.xcodeproj/project.pbxproj index 95ecff0..a796c6a 100644 --- a/Captura.xcodeproj/project.pbxproj +++ b/Captura.xcodeproj/project.pbxproj @@ -445,6 +445,7 @@ ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_LSUIElement = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -472,6 +473,7 @@ ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_LSUIElement = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/Captura/CapturaApp.swift b/Captura/CapturaApp.swift index 4dc65a4..933e00b 100644 --- a/Captura/CapturaApp.swift +++ b/Captura/CapturaApp.swift @@ -45,10 +45,17 @@ class CapturaAppDelegate: NSObject, NSApplicationDelegate { statusItem.isVisible = true statusItem.menu = NSMenu() - let recordItem = NSMenuItem(title: "record", action: #selector(CapturaAppDelegate.onClickStartRecording), keyEquivalent: "6") + let recordItem = NSMenuItem(title: "Record", action: #selector(CapturaAppDelegate.onClickStartRecording), keyEquivalent: "6") recordItem.keyEquivalentModifierMask = [.command, .shift] statusItem.menu?.addItem(recordItem) + statusItem.menu?.addItem(NSMenuItem.separator()) + + let preferencesItem = NSMenuItem(title: "Preferences", action: #selector(CapturaAppDelegate.onOpenPreferences), keyEquivalent: "") + statusItem.menu?.addItem(preferencesItem) + + let quitItem = NSMenuItem(title: "Quit", action: #selector(CapturaAppDelegate.onQuit), keyEquivalent: "") + statusItem.menu?.addItem(quitItem) } private func closeWindow() { @@ -63,6 +70,14 @@ class CapturaAppDelegate: NSObject, NSApplicationDelegate { NotificationCenter.default.post(name: .startAreaSelection, object: nil, userInfo: nil) } + @objc private func onOpenPreferences() { + print("Preferences pressed") + } + + @objc private func onQuit() { + NSApplication.shared.terminate(self) + } + // MARK: - App State Event Listeners @@ -93,7 +108,6 @@ class CapturaAppDelegate: NSObject, NSApplicationDelegate { if captureState != .selectingArea { captureState = .selectingArea recordingWindow = RecordingWindow() - print("Recording") } } @@ -111,5 +125,7 @@ class CapturaAppDelegate: NSObject, NSApplicationDelegate { func reset() { captureState = .idle + recordingWindow?.close() + self.recordingWindow = nil } } diff --git a/Captura/RecordingWindow.swift b/Captura/RecordingWindow.swift index cf572e1..d737dec 100644 --- a/Captura/RecordingWindow.swift +++ b/Captura/RecordingWindow.swift @@ -16,6 +16,7 @@ class RecordingWindow: NSWindow { backing: .buffered, defer: false) + self.isReleasedWhenClosed = false self.center() self.isMovableByWindowBackground = false self.isMovable = false @@ -44,13 +45,25 @@ class RecordingWindow: NSWindow { override var canBecomeMain: Bool { return true } + + override func resignMain() { + super.resignMain() + self.ignoresMouseEvents = false + } + + override func becomeMain() { + super.becomeMain() + (self.contentView as? RecordingContentView)?.state = .idle + } +} + +enum RecordingWindowState { + case passthrough, idle, drawing, moving, resizing; } class RecordingContentView: NSView { - var isDrawing = false - var isMoving = false - var isResizing = false + var state: RecordingWindowState = .idle var box: NSRect? = nil var mouseLocation: NSPoint = NSPoint() var origin: NSPoint = NSPoint() @@ -99,7 +112,7 @@ class RecordingContentView: NSView { override func mouseDragged(with event: NSEvent) { self.mouseLocation = self.convert(event.locationInWindow, from: nil) - if isDrawing { + if state == .drawing { box = NSRect( x: round(min(origin.x, mouseLocation.x)), y: round(min(origin.y, mouseLocation.y)), @@ -108,11 +121,22 @@ class RecordingContentView: NSView { ) } - if isMoving && box != nil { - NSCursor.closedHand.set() - box!.origin = NSPoint( - x: self.boxOrigin.x - self.origin.x + self.mouseLocation.x, - y: self.boxOrigin.y - self.origin.y + self.mouseLocation.y) + if box != nil { + if state == .moving { + NSCursor.closedHand.set() + box!.origin = NSPoint( + x: self.boxOrigin.x - self.origin.x + self.mouseLocation.x, + y: self.boxOrigin.y - self.origin.y + self.mouseLocation.y) + } + + if state == .resizing { + box = NSRect( + x: round(min(origin.x, mouseLocation.x)), + y: round(min(origin.y, mouseLocation.y)), + width: round(abs(mouseLocation.x - origin.x)), + height: round(abs(mouseLocation.y - origin.y)) + ) + } } self.setNeedsDisplay(self.bounds) } @@ -122,7 +146,7 @@ class RecordingContentView: NSView { } override func hitTest(_ point: NSPoint) -> NSView? { - return self + return state == .passthrough ? nil : self } override var acceptsFirstResponder: Bool { @@ -132,29 +156,57 @@ class RecordingContentView: NSView { override func mouseDown(with event: NSEvent) { self.origin = self.convert(event.locationInWindow, from: nil) if let box { + if resizeBox!.contains(origin) { + self.origin = NSPoint(x: box.minX, y: box.maxY) + state = .resizing + return + } if box.contains(origin) { - isMoving = true + state = .moving self.boxOrigin = NSPoint(x: box.origin.x, y: box.origin.y) return } } - isDrawing = true + state = .drawing } override func mouseUp(with event: NSEvent) { - isDrawing = false - isMoving = false + state = .idle + } + + override func keyDown(with event: NSEvent) { + print("key down") + switch event.keyCode { + case 53: // Escape key + NotificationCenter.default.post(name: .reset, object: nil, userInfo: nil) + default: + super.keyDown(with: event) + } + } + + override func flagsChanged(with event: NSEvent) { + if event.modifierFlags.contains(.shift) { + state = .passthrough + window?.ignoresMouseEvents = true + } else { + state = .idle + window?.ignoresMouseEvents = false + } } override func draw(_ dirtyRect: NSRect) { - NSColor(white: 1.0, alpha: 0.001).setFill() + if state == .passthrough { + NSColor.clear.setFill() + } else { + NSColor(white: 1.0, alpha: 0.001).setFill() + } dirtyRect.fill() let dashLength: CGFloat = 5.0 let lineWidth = 0.5 - if !isDrawing && box == nil { + if state == .idle && box == nil { let blackLine = NSBezierPath() blackLine.lineWidth = lineWidth blackLine.setLineDash([dashLength, dashLength], count: 2, phase: 0) @@ -220,29 +272,50 @@ class RecordingContentView: NSView { clearBox.fill() } - if box.contains(mouseLocation) && !isResizing { + if state == .moving { + let string = "\(Int(box.minX)), \(Int(box.maxY))" as NSString + drawText(string, NSPoint( + x: box.minX, + y: box.maxY + ), true) + } + + if state == .resizing { + let string = "\(Int(mouseLocation.x)), \(Int(mouseLocation.y))" as NSString + drawText(string, mouseLocation) + } + + if box.contains(mouseLocation) && state != .resizing { return; } } // Draw text - - let offset = NSPoint(x: 10, y: 10) - let padding = NSPoint(x: 5, y: 2) + + let string = "\(Int(mouseLocation.x)), \(Int(mouseLocation.y))" as NSString + drawText(string, mouseLocation) + } + + private func drawText(_ text: NSString, _ location: NSPoint, _ isBottomRight: Bool = false) { let textAttributes = [ NSAttributedString.Key.font: NSFont(name: "Hiragino Mincho ProN", size: 12) ?? NSFont.systemFont(ofSize: 12), NSAttributedString.Key.foregroundColor: NSColor.white, ] - - let string = "\(Int(mouseLocation.x)), \(Int(mouseLocation.y))" as NSString - let size = string.size(withAttributes: textAttributes) - let rect = NSRect(x: mouseLocation.x + offset.x, y: mouseLocation.y + offset.y, width: size.width + 2 * padding.x, height: size.height + 2 * padding.y) - let textRect = NSRect(x: mouseLocation.x + offset.x + padding.x, y: mouseLocation.y + offset.y + padding.y, width: size.width, height: size.height) + let offset = NSPoint(x: 10, y: 10) + let padding = NSPoint(x: 5, y: 2) + let size = text.size(withAttributes: textAttributes) + var rect = NSRect(x: location.x + offset.x, y: location.y + offset.y, width: size.width + 2 * padding.x, height: size.height + 2 * padding.y) + var textRect = NSRect(x: location.x + offset.x + padding.x, y: location.y + offset.y + padding.y, width: size.width, height: size.height) + + if (isBottomRight) { + rect = rect.offsetBy(dx: -size.width - 2 * offset.x, dy: 0) + textRect = textRect.offsetBy(dx: -size.width - 2 * offset.x, dy: 0) + } NSColor.black.set() rect.fill() - string.draw(in: textRect, withAttributes: textAttributes) + text.draw(in: textRect, withAttributes: textAttributes) } }