KMDrawView.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. //
  2. // KMDrawView.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by lizhe on 2023/10/9.
  6. //
  7. import Cocoa
  8. private var _index: Int = 0
  9. private var _points: [CGPoint] = [CGPoint](repeating: .zero, count: 5)
  10. @objc protocol KMDrawViewDelegate: NSObjectProtocol {
  11. func drawViewDidFinishTouchMode(_ drawView: KMDrawView)
  12. }
  13. @objcMembers class KMDrawView: NSView {
  14. private var drawImage: NSImage?
  15. var drawColor: NSColor = NSColor(red: 0, green: 0, blue: 0, alpha: 1) {
  16. didSet {
  17. self.needsDisplay = true
  18. }
  19. }
  20. var strokeRadius: CGFloat = 0.3 {
  21. didSet {
  22. self.needsDisplay = true
  23. }
  24. }
  25. private var bezierPath: NSBezierPath = NSBezierPath()
  26. var isAcceptsTouch: Bool = false {
  27. willSet{
  28. }
  29. didSet{
  30. self.acceptsTouchEvents = isAcceptsTouch
  31. let screenHeight = CGDisplayPixelsHigh(CGMainDisplayID())
  32. let frameToWindow = self.convert(self.bounds, to: nil)
  33. var frameToScreen = self.window!.convertToScreen(frameToWindow)
  34. if isAcceptsTouch {
  35. // If the mouse cursor is not already hidden,
  36. if !self.cursorIsHidden {
  37. frameToScreen.origin.x += 5
  38. frameToScreen.origin.y = CGFloat(screenHeight) - frameToScreen.origin.y - 5
  39. CGWarpMouseCursorPosition(frameToScreen.origin)
  40. // Detach the mouse cursor from the mouse
  41. // hardware so that moving the mouse (or a
  42. // single finger) will not move the cursor
  43. CGAssociateMouseAndMouseCursorPosition(0)
  44. // Hide the mouse cursor
  45. NSCursor.hide()
  46. // Remember that we detached and hid the
  47. // mouse cursor
  48. self.cursorIsHidden = true
  49. }
  50. } else {
  51. frameToScreen.origin.y = CGFloat(screenHeight) - frameToScreen.origin.y
  52. CGWarpMouseCursorPosition(frameToScreen.origin)
  53. // Attach the mouse cursor to the mouse
  54. // hardware so that moving the mouse (or a
  55. // single finger) will move the cursor
  56. CGAssociateMouseAndMouseCursorPosition(1)
  57. // Show the mouse cursor
  58. NSCursor.unhide()
  59. // Remember that we attached and unhid the
  60. // mouse cursor so that the next touch that
  61. // begins will detach and hide it
  62. self.cursorIsHidden = false
  63. }
  64. }
  65. }
  66. private var cursorIsHidden: Bool = false
  67. private var mouseIsInView: Bool = false
  68. private var activeTouch: NSTouch?
  69. weak var delegate: KMDrawViewDelegate?
  70. var drawBezierPath: NSBezierPath {
  71. return bezierPath
  72. }
  73. var touchEndCallback: ((Bool) -> Void)?
  74. var changeDrawCallback: ((Bool) -> Void)?
  75. // override func acceptsFirstResponder() -> Bool {
  76. // return true
  77. // }
  78. override init(frame frameRect: NSRect) {
  79. super.init(frame: frameRect)
  80. self.drawImage = NSImage(size: self.frame.size)
  81. self.drawColor = NSColor(red: 0, green: 0, blue: 0, alpha: 1)
  82. self.strokeRadius = 0.3
  83. self.wantsLayer = true
  84. self.layer?.borderWidth = 1.0
  85. self.layer?.borderColor = NSColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.05).cgColor
  86. }
  87. required init?(coder: NSCoder) {
  88. super.init(coder: coder)
  89. }
  90. func clearImage() {
  91. self.drawImage = nil
  92. self.bezierPath.removeAllPoints()
  93. self.needsDisplay = true
  94. if let touchEndCallback = self.touchEndCallback {
  95. touchEndCallback(true)
  96. }
  97. }
  98. func signatureImage() -> NSImage? {
  99. var rect = CGRect.zero
  100. if bezierPath.isEmpty {
  101. return nil
  102. } else {
  103. rect = bezierPath.bounds
  104. }
  105. let size = CGSize(width: rect.size.width + bezierPath.lineWidth,
  106. height: rect.size.height + bezierPath.lineWidth)
  107. if size.width <= 0 && size.height <= 0 {
  108. return nil
  109. }
  110. let image = NSImage(size: self.bounds.size)
  111. image.lockFocus()
  112. drawColor.set()
  113. drawBezierPath.lineWidth = strokeRadius * 2
  114. drawBezierPath.lineCapStyle = .round
  115. drawBezierPath.lineJoinStyle = .round
  116. drawBezierPath.stroke()
  117. image.unlockFocus()
  118. return image
  119. }
  120. override func draw(_ dirtyRect: NSRect) {
  121. super.draw(dirtyRect)
  122. NSGraphicsContext.saveGraphicsState()
  123. NSColor.clear.set()
  124. if window?.firstResponder == self && mouseIsInView {
  125. focusRingType = .`default`
  126. }
  127. NSBezierPath(rect: bounds).fill()
  128. NSGraphicsContext.restoreGraphicsState()
  129. if let drawImage = self.drawImage {
  130. let imageFrame = imageFrameInRect(rect: dirtyRect)
  131. drawImage.draw(in: imageFrame)
  132. }
  133. drawColor.set()
  134. bezierPath.lineWidth = strokeRadius * 2
  135. bezierPath.lineCapStyle = .round
  136. bezierPath.lineJoinStyle = .round
  137. bezierPath.stroke()
  138. }
  139. private func imageFrameInRect(rect: CGRect) -> CGRect {
  140. var originX: CGFloat
  141. var originY: CGFloat
  142. var width: CGFloat
  143. var height: CGFloat
  144. if drawImage?.size.width ?? 0 < rect.size.width &&
  145. drawImage?.size.height ?? 0 < rect.size.height {
  146. originX = (rect.size.width - (drawImage?.size.width ?? 0)) / 2.0
  147. originY = (rect.size.height - (drawImage?.size.height ?? 0)) / 2.0
  148. width = drawImage?.size.width ?? 0
  149. height = drawImage?.size.height ?? 0
  150. } else {
  151. if (drawImage?.size.width ?? 0) / (drawImage?.size.height ?? 0) >
  152. rect.size.width / rect.size.height {
  153. width = rect.size.width
  154. height = (rect.size.width * (drawImage?.size.height ?? 0)) / (drawImage?.size.width ?? 0)
  155. } else {
  156. height = rect.size.height
  157. width = (rect.size.height * (drawImage?.size.width ?? 0)) / (drawImage?.size.height ?? 0)
  158. }
  159. originX = (rect.size.width - width) / 2.0
  160. originY = (rect.size.height - height) / 2.0
  161. }
  162. let rect = NSMakeRect(originX, originY, width, height)
  163. return rect
  164. }
  165. override func viewDidMoveToWindow() {
  166. if window != nil {
  167. addTrackingRect(bounds, owner: self, userData: nil, assumeInside: false)
  168. }
  169. }
  170. override func touchesBegan(with event: NSEvent) {
  171. super.touchesBegan(with: event)
  172. let touches = event.touches(matching: .began, in: self)
  173. self.activeTouch = touches.first
  174. guard let activeTouch = self.activeTouch else { return }
  175. var point = activeTouch.normalizedPosition
  176. point.x *= self.bounds.size.width
  177. point.y *= self.bounds.size.height
  178. _index = 0
  179. _points[0] = point
  180. self.needsDisplay = true
  181. if !self.cursorIsHidden {
  182. CGAssociateMouseAndMouseCursorPosition(0)
  183. NSCursor.hide()
  184. self.cursorIsHidden = true
  185. }
  186. if let changeDrawCallback = changeDrawCallback {
  187. changeDrawCallback(true)
  188. }
  189. }
  190. override func touchesMoved(with event: NSEvent) {
  191. super.touchesMoved(with: event)
  192. let touches = event.touches(matching: .moved, in: self)
  193. var isTouch = false
  194. for touch in touches {
  195. if touch.identity.isEqual(self.activeTouch?.identity) {
  196. isTouch = true
  197. self.activeTouch = touch
  198. }
  199. }
  200. guard isTouch else { return }
  201. var point = self.activeTouch!.normalizedPosition
  202. point.x *= self.bounds.size.width
  203. point.y *= self.bounds.size.height
  204. _index += 1
  205. _points[_index] = point
  206. if _index == 4 {
  207. _points[3] = CGPoint(x: (_points[2].x + _points[4].x)/2.0,
  208. y: (_points[2].y + _points[4].y)/2.0)
  209. bezierPath.move(to: _points[0])
  210. bezierPath.curve(to: _points[3], controlPoint1: _points[1], controlPoint2: _points[2])
  211. _points[0] = _points[3]
  212. _points[1] = _points[4]
  213. _index = 1
  214. self.needsDisplay = true
  215. }
  216. }
  217. override func touchesEnded(with event: NSEvent) {
  218. super.touchesEnded(with: event)
  219. let touches = event.touches(matching: .moved, in: self)
  220. for touch in touches {
  221. if touch.identity.isEqual(self.activeTouch?.identity) {
  222. self.activeTouch = nil
  223. }
  224. }
  225. if _index < 4 {
  226. for i in 0..<_index {
  227. bezierPath.move(to: _points[i])
  228. }
  229. self.needsDisplay = true
  230. }
  231. // var image = signatureImage()
  232. // if let changeDrawCallback = changeDrawCallback {
  233. // changeDrawCallback(image)
  234. // }
  235. }
  236. override func touchesCancelled(with event: NSEvent) {
  237. super.touchesCancelled(with: event)
  238. for i in 0..<_index {
  239. bezierPath.move(to: _points[i])
  240. }
  241. activeTouch = nil
  242. self.needsDisplay = true
  243. }
  244. override func mouseEntered(with event: NSEvent) {
  245. window?.makeFirstResponder(self)
  246. // self.mouseIsInView = true
  247. needsDisplay = true
  248. }
  249. override func mouseExited(with event: NSEvent) {
  250. // self.mouseIsInView = false
  251. needsDisplay = true
  252. }
  253. override func mouseDown(with event: NSEvent) {
  254. if acceptsTouchEvents {
  255. return
  256. }
  257. let point = convert(event.locationInWindow, from: nil)
  258. _index = 0
  259. _points[0] = point
  260. }
  261. override func mouseDragged(with event: NSEvent) {
  262. if acceptsTouchEvents {
  263. return
  264. }
  265. let point = convert(event.locationInWindow, from: nil)
  266. _index += 1
  267. _points[_index] = point
  268. if _index == 4 {
  269. _points[3] = CGPoint(x: (_points[2].x + _points[4].x) / 2.0,
  270. y: (_points[2].y + _points[4].y) / 2.0)
  271. bezierPath.move(to: _points[0])
  272. bezierPath.curve(to: _points[3], controlPoint1: _points[1], controlPoint2: _points[2])
  273. _points[0] = _points[3]
  274. _points[1] = _points[4]
  275. _index = 1
  276. needsDisplay = true
  277. if let changeDrawCallback = self.changeDrawCallback {
  278. changeDrawCallback(true)
  279. }
  280. }
  281. }
  282. override func mouseUp(with event: NSEvent) {
  283. if acceptsTouchEvents {
  284. return
  285. }
  286. if _index < 4 {
  287. for i in 0..<_index {
  288. bezierPath.move(to: _points[i])
  289. }
  290. needsDisplay = true
  291. }
  292. if let touchEndCallback = self.touchEndCallback {
  293. touchEndCallback(false)
  294. }
  295. }
  296. override func keyDown(with event: NSEvent) {
  297. if let characters = event.characters, characters.count > 0 {
  298. let character = characters.first!
  299. // if character == "\u{1B}" { // Check for the Escape key
  300. if isAcceptsTouch {
  301. isAcceptsTouch = false
  302. if let delegate = self.delegate {
  303. delegate.drawViewDidFinishTouchMode(self)
  304. }
  305. return
  306. }
  307. // }
  308. }
  309. super.keyDown(with: event)
  310. }
  311. }