-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMouseTrackingView.swift
More file actions
184 lines (155 loc) · 5.99 KB
/
MouseTrackingView.swift
File metadata and controls
184 lines (155 loc) · 5.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
import SwiftUI
#if canImport(UIKit)//ios
public typealias UIRect = CGRect
extension UIView
{
var theLayer: CALayer? { get{ self.layer } }
}
#else
import AppKit
public typealias UIView = NSView
public typealias UIColor = NSColor
public typealias UIRect = NSRect
public typealias UIViewRepresentable = NSViewRepresentable
public typealias UIEvent = NSEvent
extension UIView
{
var theLayer: CALayer? { get{ self.layer } }
}
#endif
public enum MouseEventResponse
{
case EventHandled, // we handled the event, don't do inherited behaviour
InheritedBehaviour // we didn't handle the event, pass it up the chain
}
public class MouseStateTrackingView : UIView
{
var onMouseStateChanged : (MouseState)->MouseEventResponse
var onMouseScroll : (MouseScrollEvent)->MouseEventResponse
#if canImport(AppKit)
var trackingArea: NSTrackingArea?
#endif
required init?(coder: NSCoder)
{
fatalError()
}
public init(onMouseStateChanged:@escaping(MouseState)->MouseEventResponse,onMouseScroll:@escaping(MouseScrollEvent)->MouseEventResponse)
{
self.onMouseStateChanged = onMouseStateChanged
self.onMouseScroll = onMouseScroll
super.init(frame: UIRect())
#if canImport(AppKit)
// enable mouse move events when tracking area changes(layout changes)
self.window?.acceptsMouseMovedEvents = true
#endif
}
#if canImport(AppKit)
func getMouseViewPosition(event:UIEvent) -> CGPoint
{
//let screenPosition = NSEvent.mouseLocation
let windowPosition = event.locationInWindow
// event for wrong window
//if self.window?.windowNumber == event.windowNumber
let viewPosition = self.convert(windowPosition, from:nil)
return viewPosition
}
#endif
func updateState(_ event:UIEvent) -> MouseEventResponse
{
// todo: ios version
#if canImport(AppKit)
let viewPosition = getMouseViewPosition(event: event)
let buttonsPressed = UIEvent.pressedMouseButtons
let leftDown = (buttonsPressed & (1<<0)) != 0
let rightDown = (buttonsPressed & (1<<1)) != 0
let middleDown = (buttonsPressed & (1<<2)) != 0
let state = MouseState(leftDown: leftDown, middleDown: middleDown, rightDown: rightDown, position: viewPosition)
return onMouseStateChanged( state )
#else
return .InheritedBehaviour
#endif
}
#if canImport(AppKit)
public override func updateTrackingAreas()
{
let trackingOptions : NSTrackingArea.Options = [
.activeAlways,
.mouseEnteredAndExited,
.mouseMoved,
.enabledDuringMouseDrag
]
// update tracking area
if let trackingArea
{
self.removeTrackingArea(trackingArea)
self.trackingArea = nil
}
trackingArea = NSTrackingArea(rect: bounds,
options: trackingOptions,
owner: self, userInfo: nil)
self.addTrackingArea(trackingArea!)
super.updateTrackingAreas()
}
public override func mouseDown(with theEvent: NSEvent) { if updateState(theEvent) == .InheritedBehaviour { super.mouseDown(with: theEvent) } }
public override func rightMouseDown(with theEvent: NSEvent) { if updateState(theEvent) == .InheritedBehaviour { super.rightMouseDown(with: theEvent) } }
public override func otherMouseDown(with theEvent: NSEvent) { if updateState(theEvent) == .InheritedBehaviour { super.otherMouseDown(with: theEvent) } }
public override func mouseUp(with theEvent: NSEvent) { if updateState(theEvent) == .InheritedBehaviour { super.mouseUp(with: theEvent) } }
public override func rightMouseUp(with theEvent: NSEvent) { if updateState(theEvent) == .InheritedBehaviour { super.rightMouseUp(with: theEvent) } }
public override func otherMouseUp(with theEvent: NSEvent) { if updateState(theEvent) == .InheritedBehaviour { super.otherMouseUp(with: theEvent) } }
public override func mouseMoved(with theEvent: NSEvent) { if updateState(theEvent) == .InheritedBehaviour { super.mouseMoved(with: theEvent) } }
public override func mouseDragged(with theEvent: NSEvent) { if updateState(theEvent) == .InheritedBehaviour { super.mouseDragged(with: theEvent) } }
public override func rightMouseDragged(with theEvent: NSEvent) { if updateState(theEvent) == .InheritedBehaviour { super.rightMouseDragged(with: theEvent) } }
public override func otherMouseDragged(with theEvent: NSEvent) { if updateState(theEvent) == .InheritedBehaviour { super.otherMouseDragged(with: theEvent) } }
public override func mouseEntered(with theEvent: NSEvent) { if updateState(theEvent) == .InheritedBehaviour { super.mouseEntered(with: theEvent) } }
public override func mouseExited(with theEvent: NSEvent) { if updateState(theEvent) == .InheritedBehaviour { super.mouseExited(with: theEvent) } }
public override func scrollWheel(with theEvent: NSEvent)
{
let viewPosition = getMouseViewPosition(event: theEvent)
// gr: include x scroll once we establish if device has it
let scrollEvent = MouseScrollEvent(scrollDelta: theEvent.scrollingDeltaY, position: viewPosition )
if onMouseScroll( scrollEvent ) == .InheritedBehaviour
{
super.scrollWheel(with: theEvent)
}
}
#endif
}
public struct MouseTrackingView : UIViewRepresentable
{
public typealias UIViewType = MouseStateTrackingView
public typealias NSViewType = MouseStateTrackingView
var onMouseStateChanged : (MouseState)->MouseEventResponse
var onMouseScroll : (MouseScrollEvent)->MouseEventResponse
public init(onMouseStateChanged:@escaping (MouseState)->MouseEventResponse,onMouseScroll:@escaping (MouseScrollEvent)->MouseEventResponse)
{
self.onMouseStateChanged = onMouseStateChanged
self.onMouseScroll = onMouseScroll
}
public func makeNSView(context: Context) -> NSViewType
{
return makeUIView(context:context)
}
public func makeUIView(context: Context) -> UIViewType
{
return UIViewType(onMouseStateChanged: onMouseStateChanged, onMouseScroll:onMouseScroll)
}
public func updateNSView(_ nsView: UIViewType, context: Context)
{
updateUIView( nsView, context: context )
}
public func updateUIView(_ nsView: UIViewType, context: Context)
{
}
}
#Preview
{
MouseTestView()
.contextMenu
{
Text("Context menus still work")
Button(action:{})
{
Text("thing on context menu")
}
}
}