From e385d25bcad1ce0ac16ce5819f1d69928770aa64 Mon Sep 17 00:00:00 2001 From: Viktor Sukochev Date: Wed, 23 Aug 2017 12:29:45 +0700 Subject: [PATCH] Basic mouse events --- .../Events/EventsExampleController.swift | 79 +++++++- .../Events/EventsExampleController.swift | 5 + Example/Podfile.lock | 2 +- Source/platform/iOS/MView_iOS.swift | 128 ++++++++----- Source/platform/macOS/MView_macOS.swift | 168 ++++++++++++++++-- Source/views/MacawView.swift | 80 +++++---- Source/views/Touchable.swift | 26 +++ 7 files changed, 380 insertions(+), 108 deletions(-) create mode 100644 Source/views/Touchable.swift diff --git a/Example-macOS/Example-macOS/Examples/Events/EventsExampleController.swift b/Example-macOS/Example-macOS/Examples/Events/EventsExampleController.swift index d81fbd8e..9a20367a 100644 --- a/Example-macOS/Example-macOS/Examples/Events/EventsExampleController.swift +++ b/Example-macOS/Example-macOS/Examples/Events/EventsExampleController.swift @@ -41,15 +41,76 @@ class EventsExampleController: NSViewController { return panelGroup } - private func createCanvas() -> Node { - let canvas = Shape(form: Rect(x: 0.0, y: 0.0, - w: Double(macawView!.bounds.width), - h: Double(macawView!.bounds.height)), - fill: Color.clear) - let objectsGroup = Group(contents:[]) - - return [canvas, objectsGroup].group() - } + private func createCanvas() -> Node { + let canvas = Shape(form: Rect(x: 0.0, y: 0.0, + w: Double(macawView!.bounds.width), + h: Double(macawView!.bounds.height)), + fill: Color.white) + let objectsGroup = Group(contents:[]) + + var startPoint = Point() + var currentFigure: Shape? + + canvas.onTouchPressed { event in + guard let tool = self.selectedTool else { + return + } + + guard let loc = event.points.first?.location else { + return + } + + startPoint = loc + switch tool { + case .ellipse: + currentFigure = Shape(form: Ellipse(cx: startPoint.x, cy: startPoint.y, rx: 0.0, ry: 0.0)) + break + + case .rectangle: + currentFigure = Shape(form: Rect(x: startPoint.x, y: startPoint.y, w: 0.0, h: 0.0)) + break + } + + var updatedContents = objectsGroup.contents + updatedContents.append(currentFigure!) + + objectsGroup.contents = updatedContents + } + + canvas.onTouchMoved { event in + guard let tool = self.selectedTool else { + return + } + + guard let loc = event.points.first?.location else { + return + } + + let width = loc.x - startPoint.x + let height = loc.y - startPoint.y + + switch tool { + case .ellipse: + + currentFigure?.form = Ellipse( + cx: startPoint.x + width / 2.0, + cy: startPoint.y + height / 2.0, + rx: width / 2.0, + ry: height / 2.0) + break + + case .rectangle: + currentFigure?.form = Rect(x: startPoint.x, y: startPoint.y, w: width, h: height) + break + } + + } + + return [ + canvas, + objectsGroup + ].group() + } private func createTools() -> Node { let ellipseTool = Shape(form: Ellipse(cx: 50.0, cy: 50.0, rx: 25, ry: 15), diff --git a/Example/Example/Examples/Events/EventsExampleController.swift b/Example/Example/Examples/Events/EventsExampleController.swift index d9f8ef87..858efda4 100644 --- a/Example/Example/Examples/Events/EventsExampleController.swift +++ b/Example/Example/Examples/Events/EventsExampleController.swift @@ -69,6 +69,8 @@ class EventsExampleController: UIViewController { return } + print("canvas pressed:\(loc.x) \(loc.y)") + startPoint = loc switch tool { case .ellipse: @@ -87,6 +89,7 @@ class EventsExampleController: UIViewController { } canvas.onTouchMoved { event in + guard let tool = self.selectedTool else { return } @@ -95,6 +98,8 @@ class EventsExampleController: UIViewController { return } + print("canvas moving \(loc.x) \(loc.y)") + let width = loc.x - startPoint.x let height = loc.y - startPoint.y diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 4e57e266..3cf6f184 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -11,7 +11,7 @@ EXTERNAL SOURCES: :path: "../" SPEC CHECKSUMS: - Macaw: ceb6b88ed35e027b9b40804fed254b9e0e700c81 + Macaw: 5bd466e34d3602084f43486abbc31fc4a8060c74 SWXMLHash: 701f7d07c032089b9ff34e0352bd4fed1a24652a PODFILE CHECKSUM: cf6c204dbe194da8e03fcb9ffd9c99d086057155 diff --git a/Source/platform/iOS/MView_iOS.swift b/Source/platform/iOS/MView_iOS.swift index a4b4ec2a..dfec3555 100644 --- a/Source/platform/iOS/MView_iOS.swift +++ b/Source/platform/iOS/MView_iOS.swift @@ -9,53 +9,89 @@ import Foundation #if os(iOS) - import UIKit - - open class MView: UIView { - var mLayer: CALayer? { - return self.layer + import UIKit + + open class MView: UIView, Touchable { + var mLayer: CALayer? { + return self.layer + } + + var mGestureRecognizers: [MGestureRecognizer]? { + return self.gestureRecognizers + } + + func removeGestureRecognizers() { + self.gestureRecognizers?.removeAll() + } + + open override func touchesBegan(_ touches: Set, with event: MEvent?) { + super.touchesBegan(touches, with: event) + + let touchPoints = touches.map { touch -> MTouchEvent in + let location = touch.location(in: self) + let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) + + return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) + } + + mTouchesBegan(touchPoints) + } + + open override func touchesMoved(_ touches: Set, with event: MEvent?) { + super.touchesMoved(touches, with: event) + + let touchPoints = touches.map { touch -> MTouchEvent in + let location = touch.location(in: self) + let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) + + return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) + } + + self.mTouchesMoved(touchPoints) + } + + open override func touchesEnded(_ touches: Set, with event: MEvent?) { + super.touchesEnded(touches, with: event) + + let touchPoints = touches.map { touch -> MTouchEvent in + let location = touch.location(in: self) + let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) + + return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) + } + + mTouchesEnded(touchPoints) + } + + override open func touchesCancelled(_ touches: Set, with event: MEvent?) { + super.touchesCancelled(touches, with: event) + + let touchPoints = touches.map { touch -> MTouchEvent in + let location = touch.location(in: self) + let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) + + return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) + } + + mTouchesCancelled(touchPoints) + } + + func mTouchesBegan(_ touches: [MTouchEvent]) { + + } + + func mTouchesMoved(_ touches: [MTouchEvent]) { + + } + + func mTouchesEnded(_ touches: [MTouchEvent]) { + + } + + func mTouchesCancelled(_ touches: [MTouchEvent]) { + + } } - var mGestureRecognizers: [MGestureRecognizer]? { - return self.gestureRecognizers - } - - func removeGestureRecognizers() { - self.gestureRecognizers?.removeAll() - } - - open override func touchesBegan(_ touches: Set, with event: MEvent?) { - self.mTouchesBegan(touches, with: event) - } - - open override func touchesMoved(_ touches: Set, with event: MEvent?) { - self.mTouchesMoved(touches, with: event) - } - - open override func touchesEnded(_ touches: Set, with event: MEvent?) { - self.mTouchesEnded(touches, with: event) - } - - open override func touchesCancelled(_ touches: Set, with event: MEvent?) { - self.mTouchesCancelled(touches, with: event) - } - - open func mTouchesBegan(_ touches: Set, with event: MEvent?) { - super.touchesBegan(touches, with: event) - } - - open func mTouchesMoved(_ touches: Set, with event: MEvent?) { - super.touchesMoved(touches, with: event) - } - - open func mTouchesEnded(_ touches: Set, with event: MEvent?) { - super.touchesEnded(touches, with: event) - } - - open func mTouchesCancelled(_ touches: Set, with event: MEvent?) { - super.touchesCancelled(touches, with: event) - } - } - #endif diff --git a/Source/platform/macOS/MView_macOS.swift b/Source/platform/macOS/MView_macOS.swift index c9c3bf18..fc0cbb25 100644 --- a/Source/platform/macOS/MView_macOS.swift +++ b/Source/platform/macOS/MView_macOS.swift @@ -27,7 +27,7 @@ import Foundation case bottomRight } - open class MView: NSView { + open class MView: NSView, Touchable { public override init(frame frameRect: NSRect) { super.init(frame: frameRect) @@ -38,6 +38,7 @@ import Foundation super.init(coder: coder) self.wantsLayer = true + setupMouse() } open override var isFlipped: Bool { @@ -80,36 +81,177 @@ import Foundation super.resizeSubviews(withOldSize: self.bounds.size) } + // MARK: - Touch pad open override func touchesBegan(with event: NSEvent) { - self.mTouchesBegan(event.touches(matching: .any, in: self), with: event) + super.touchesBegan(with: event) + + let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in + let location = touch.location(in: self) + let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) + + return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) + } + + mTouchesBegan(touchPoints) } open override func touchesEnded(with event: NSEvent) { - self.mTouchesEnded(event.touches(matching: .any, in: self), with: event) + super.touchesEnded(with: event) + + let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in + let location = touch.location(in: self) + let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) + + return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) + } + + mTouchesEnded(touchPoints) } open override func touchesMoved(with event: NSEvent) { - self.mTouchesMoved(event.touches(matching: .any, in: self), with: event) + super.touchesMoved(with: event) + + let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in + let location = touch.location(in: self) + let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) + + return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) + } + + mTouchesMoved(touchPoints) } open override func touchesCancelled(with event: NSEvent) { - self.mTouchesCancelled(event.touches(matching: .any, in: self), with: event) + super.touchesCancelled(with: event) + + let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in + let location = touch.location(in: self) + let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) + + return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) + } + + mTouchesCancelled(touchPoints) } - open func mTouchesBegan(_ touches: Set, with event: MEvent?) { - super.touchesBegan(with: event!) + // MARK: - Mouse + private func setupMouse() { + subscribeForMouseDown() + subscribeForMouseUp() + subscribeForMouseDragged() } - open func mTouchesMoved(_ touches: Set, with event: MEvent?) { - super.touchesMoved(with: event!) + private func subscribeForMouseDown() { + NSEvent.addLocalMonitorForEvents(matching: .leftMouseDown, handler: { [weak self] event -> NSEvent? in + guard let weakSelf = self else { + return event + } + + // Touch pad + guard event.subtype == .touch else { + let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in + let location = touch.location(in: weakSelf) + let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) + + return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) + } + + weakSelf.mTouchesBegan(touchPoints) + return event + } + + // Mouse + let location = weakSelf.convert(event.locationInWindow, to: .none) + let id = 0 + let touchPoint = MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) + + + weakSelf.mTouchesBegan([touchPoint]) + + return event + }) } - open func mTouchesEnded(_ touches: Set, with event: MEvent?) { - super.touchesEnded(with: event!) + private func subscribeForMouseUp() { + NSEvent.addLocalMonitorForEvents(matching: .leftMouseUp, handler: { [weak self] event -> NSEvent? in + guard let weakSelf = self else { + return event + } + + // Touch pad + guard event.subtype == .touch else { + let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in + let location = touch.location(in: weakSelf) + let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) + + return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) + } + + weakSelf.mTouchesEnded(touchPoints) + return event + } + + // Mouse + let location = weakSelf.convert(event.locationInWindow, to: .none) + let id = 0 + + let touchPoint = MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) + + + weakSelf.mTouchesEnded([touchPoint]) + + return event + }) } - open func mTouchesCancelled(_ touches: Set, with event: MEvent?) { - super.touchesCancelled(with: event!) + + private func subscribeForMouseDragged() { + NSEvent.addLocalMonitorForEvents(matching: .leftMouseDragged, handler: { [weak self] event -> NSEvent? in + guard let weakSelf = self else { + return event + } + + // Touch pad + guard event.subtype == .touch else { + let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in + let location = touch.location(in: weakSelf) + let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) + + return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) + } + + weakSelf.mTouchesMoved(touchPoints) + return event + } + + // Mouse + let location = weakSelf.convert(event.locationInWindow, to: .none) + let id = 0 + + let touchPoint = MTouchEvent(x: Double(location.x), y: Double(location.y), id: 0) + + + weakSelf.mTouchesMoved([touchPoint]) + + return event + }) + } + + // MARK: - Touchable + func mTouchesBegan(_ touches: [MTouchEvent]) { + + } + + func mTouchesMoved(_ touches: [MTouchEvent]) { + + } + + func mTouchesEnded(_ touches: [MTouchEvent]) { + + } + + func mTouchesCancelled(_ touches: [MTouchEvent]) { + } } #endif diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index fac43131..10167cfd 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -56,8 +56,8 @@ open class MacawView: MView, MGestureRecognizerDelegate { animationProducer.addStoredAnimations(node) } - var touchesMap = [MTouch: [Node]]() - var touchesOfNode = [Node: [MTouch]]() + var touchesMap = [MTouchEvent: [Node]]() + var touchesOfNode = [Node: [MTouchEvent]]() var recognizersMap = [MGestureRecognizer: [Node]]() var context: RenderContext! @@ -240,7 +240,7 @@ open class MacawView: MView, MGestureRecognizerDelegate { // MARK: - Touches - open override func mTouchesBegan(_ touches: Set, with event: MEvent?) { +override func mTouchesBegan(_ touches: [MTouchEvent]) { if !self.node.shouldCheckForPressed() && !self.node.shouldCheckForMoved() && @@ -253,7 +253,7 @@ open class MacawView: MView, MGestureRecognizerDelegate { } for touch in touches { - let location = touch.location(in: self) + let location = CGPoint(x: touch.x, y: touch.y) NSLog("\(location)") var foundNode: Node? = .none localContext { ctx in @@ -276,7 +276,7 @@ open class MacawView: MView, MGestureRecognizerDelegate { while parent != .none { let currentNode = parent! if touchesOfNode[currentNode] == nil { - touchesOfNode[currentNode] = [MTouch]() + touchesOfNode[currentNode] = [MTouchEvent]() } touchesMap[touch]?.append(currentNode) @@ -289,54 +289,56 @@ open class MacawView: MView, MGestureRecognizerDelegate { } } - open override func mTouchesMoved(_ touches: Set, with event: MEvent?) { - if !self.node.shouldCheckForMoved() { - return - } - - guard let _ = renderer else { - return - } - - touchesOfNode.keys.forEach { currentNode in - guard let touches = touchesOfNode[currentNode] else { - return - } - - var points = [TouchPoint]() - for touch in touches { - let location = touch.location(in: self) - let inverted = currentNode.place.invert()! - let loc = location.applying(RenderUtils.mapTransform(inverted)) - let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) - let point = TouchPoint(id: id, location: Point(x: Double(loc.x), y: Double(loc.y))) - points.append(point) - } - - let touchEvent = TouchEvent(node: currentNode, points: points) - currentNode.handleTouchMoved(touchEvent) + override func mTouchesMoved(_ touches: [MTouchEvent]) { + if !self.node.shouldCheckForMoved() { + return + } + + guard let _ = renderer else { + return + } + + touchesOfNode.keys.forEach { currentNode in + guard let initialTouches = touchesOfNode[currentNode] else { + return + } + + var points = [TouchPoint]() + for initialTouch in initialTouches { + let currentIndex = touches.index(of: initialTouch)! + let currentTouch = touches[currentIndex] + let location = CGPoint(x: currentTouch.x, y: currentTouch.y) + let inverted = currentNode.place.invert()! + let loc = location.applying(RenderUtils.mapTransform(inverted)) + let point = TouchPoint(id: currentTouch.id, location: Point(x: Double(loc.x), y: Double(loc.y))) + points.append(point) + } + + let touchEvent = TouchEvent(node: currentNode, points: points) + currentNode.handleTouchMoved(touchEvent) + } } + + + override func mTouchesCancelled(_ touches: [MTouchEvent]) { + touchesEnded(touches: touches) } - open override func mTouchesCancelled(_ touches: Set, with event: MEvent?) { - touchesEnded(touches: touches, event: event) + override func mTouchesEnded(_ touches: [MTouchEvent]) { + touchesEnded(touches: touches) } - open override func mTouchesEnded(_ touches: Set, with event: MEvent?) { - touchesEnded(touches: touches, event: event) - } - - private func touchesEnded(touches: Set, event: MEvent?) { + private func touchesEnded(touches: [MTouchEvent]) { guard let _ = renderer else { return } for touch in touches { - let location = touch.location(in: self) touchesMap[touch]?.forEach { node in let inverted = node.place.invert()! + let location = CGPoint(x: touch.x, y: touch.y) let loc = location.applying(RenderUtils.mapTransform(inverted)) let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) let point = TouchPoint(id: id, location: Point(x: Double(loc.x), y: Double(loc.y))) diff --git a/Source/views/Touchable.swift b/Source/views/Touchable.swift new file mode 100644 index 00000000..d0418c68 --- /dev/null +++ b/Source/views/Touchable.swift @@ -0,0 +1,26 @@ +class MTouchEvent: Hashable { + let id: Int + let x: Double + let y: Double + + init(x: Double, y: Double, id: Int) { + self.x = x + self.y = y + self.id = id + } + + public var hashValue: Int { + return id.hashValue + } + + public static func ==(lhs: MTouchEvent, rhs: MTouchEvent) -> Bool { + return lhs.id == rhs.id + } +} + +protocol Touchable { + func mTouchesBegan(_ touches: [MTouchEvent]) + func mTouchesMoved(_ touches: [MTouchEvent]) + func mTouchesEnded(_ touches: [MTouchEvent]) + func mTouchesCancelled(_ touches: [MTouchEvent]) +}