-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathHoverActionsPanel.swift
More file actions
83 lines (72 loc) · 3.03 KB
/
HoverActionsPanel.swift
File metadata and controls
83 lines (72 loc) · 3.03 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
import AppKit
import SwiftUI
import UI
/// A floating panel that displays message hover actions (react, reply, pin)
/// positioned above the hovered timeline row, outside the NSTableView's row clipping.
class HoverActionsPanel: NSPanel {
private let hostingView: NSHostingView<AnyView>
init() {
hostingView = NSHostingView(rootView: AnyView(EmptyView()))
super.init(
contentRect: .zero,
styleMask: [.borderless, .nonactivatingPanel],
backing: .buffered,
defer: true
)
isOpaque = false
backgroundColor = .clear
hasShadow = false
level = .floating
hidesOnDeactivate = true
collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
ignoresMouseEvents = false
contentView = hostingView
}
override var canBecomeKey: Bool { false }
var isMouseInside: Bool {
NSMouseInRect(NSEvent.mouseLocation, frame, false)
}
func update(eventId: String, onReaction: @escaping (String) -> Void, onReply: @escaping () -> Void, onReplyInThread: @escaping () -> Void, onPin: @escaping () -> Void) {
hostingView.rootView = AnyView(
HoverActionsView(onReaction: onReaction, onReply: onReply, onReplyInThread: onReplyInThread, onPin: onPin)
.id(eventId)
)
hostingView.layoutSubtreeIfNeeded()
let size = hostingView.fittingSize
setContentSize(size)
}
func position(relativeTo rowRect: NSRect, in window: NSWindow, topOffset: CGFloat = 0) {
let screenRect = window.convertToScreen(rowRect)
let size = hostingView.fittingSize
let origin = NSPoint(
x: screenRect.maxX - size.width - 20,
y: screenRect.maxY - 6 - topOffset
)
setFrameOrigin(origin)
}
}
private struct HoverActionsView: View {
let onReaction: (String) -> Void
let onReply: () -> Void
let onReplyInThread: () -> Void
let onPin: () -> Void
var body: some View {
HStack(spacing: 0) {
HoverButton(icon: { Text("👍") }, tooltip: "React") { onReaction("👍") }
HoverButton(icon: { Text("🎉") }, tooltip: "React") { onReaction("🎉") }
HoverButton(icon: { Text("❤️") }, tooltip: "React") { onReaction("❤️") }
Divider().frame(height: 18)
HoverButton(icon: { Image(systemName: "face.smiling") }, tooltip: "React") {}
HoverButton(icon: { Image(systemName: "arrowshape.turn.up.left") }, tooltip: "Reply") { onReply() }
HoverButton(icon: { Image(systemName: "ellipsis.message") }, tooltip: "Reply in thread") { onReplyInThread() }
HoverButton(icon: { Image(systemName: "pin") }, tooltip: "Pin") { onPin() }
}
.padding(2)
.background(
RoundedRectangle(cornerRadius: 4)
.fill(Color(NSColor.controlBackgroundColor))
.stroke(Color(NSColor.separatorColor), lineWidth: 1)
.shadow(color: .black.opacity(0.1), radius: 4)
)
}
}