Skip to content

Commit 9421c7c

Browse files
committed
Add align floating widget to cursor mode
1 parent 48f90bd commit 9421c7c

File tree

5 files changed

+244
-55
lines changed

5 files changed

+244
-55
lines changed

Core/Sources/AXExtension/AXUIElement.swift

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import AppKit
22
import Foundation
33

4+
// MARK: - State
5+
46
public extension AXUIElement {
57
var identifier: String {
68
(try? copyValue(key: kAXIdentifierAttribute)) ?? ""
@@ -9,15 +11,11 @@ public extension AXUIElement {
911
var value: String {
1012
(try? copyValue(key: kAXValueAttribute)) ?? ""
1113
}
12-
14+
1315
var document: String? {
1416
try? copyValue(key: kAXDocumentAttribute)
1517
}
1618

17-
var focusedElement: AXUIElement? {
18-
try? copyValue(key: kAXFocusedUIElementAttribute)
19-
}
20-
2119
var description: String {
2220
(try? copyValue(key: kAXDescriptionAttribute)) ?? ""
2321
}
@@ -32,14 +30,59 @@ public extension AXUIElement {
3230
return nil
3331
}
3432

33+
var isFocused: Bool {
34+
(try? copyValue(key: kAXFocusedAttribute)) ?? false
35+
}
36+
37+
var isEnabled: Bool {
38+
(try? copyValue(key: kAXEnabledAttribute)) ?? false
39+
}
40+
}
41+
42+
// MARK: - Rect
43+
44+
public extension AXUIElement {
45+
var position: CGPoint? {
46+
guard let value: AXValue = try? copyValue(key: kAXPositionAttribute)
47+
else { return nil }
48+
var point: CGPoint = .zero
49+
if AXValueGetValue(value, .cgPoint, &point) {
50+
return point
51+
}
52+
return nil
53+
}
54+
55+
var size: CGSize? {
56+
guard let value: AXValue = try? copyValue(key: kAXSizeAttribute)
57+
else { return nil }
58+
var size: CGSize = .zero
59+
if AXValueGetValue(value, .cgSize, &size) {
60+
return size
61+
}
62+
return nil
63+
}
64+
65+
var rect: CGRect? {
66+
guard let position, let size else { return nil }
67+
return .init(origin: position, size: size)
68+
}
69+
}
70+
71+
// MARK: - Relationship
72+
73+
public extension AXUIElement {
74+
var focusedElement: AXUIElement? {
75+
try? copyValue(key: kAXFocusedUIElementAttribute)
76+
}
77+
3578
var sharedFocusElements: [AXUIElement] {
3679
(try? copyValue(key: kAXChildrenAttribute)) ?? []
3780
}
3881

3982
var window: AXUIElement? {
4083
try? copyValue(key: kAXWindowAttribute)
4184
}
42-
85+
4386
var windows: [AXUIElement] {
4487
(try? copyValue(key: kAXWindowsAttribute)) ?? []
4588
}
@@ -68,14 +111,6 @@ public extension AXUIElement {
68111
(try? copyValue(key: kAXVisibleChildrenAttribute)) ?? []
69112
}
70113

71-
var isFocused: Bool {
72-
(try? copyValue(key: kAXFocusedAttribute)) ?? false
73-
}
74-
75-
var isEnabled: Bool {
76-
(try? copyValue(key: kAXEnabledAttribute)) ?? false
77-
}
78-
79114
func child(identifier: String) -> AXUIElement? {
80115
for child in children {
81116
if child.identifier == identifier { return child }
@@ -92,6 +127,14 @@ public extension AXUIElement {
92127
return nil
93128
}
94129

130+
var verticalScrollBar: AXUIElement? {
131+
try? copyValue(key: kAXVerticalScrollBarAttribute)
132+
}
133+
}
134+
135+
// MARK: - Helper
136+
137+
public extension AXUIElement {
95138
func copyValue<T>(key: String, ofType _: T.Type = T.self) throws -> T {
96139
var value: AnyObject?
97140
let error = AXUIElementCopyAttributeValue(self, key as CFString, &value)
@@ -118,7 +161,6 @@ public extension AXUIElement {
118161
}
119162
throw error
120163
}
121-
122164
}
123165

124166
extension AXError: Error {}

Core/Sources/SuggestionWidget/SuggestionPanelView.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ final class SuggestionPanelViewModel: ObservableObject {
88
@Published var isPanelDisplayed: Bool
99
@Published var suggestionCount: Int
1010
@Published var currentSuggestionIndex: Int
11+
@Published var alignTopToAnchor = false
1112

1213
var onAcceptButtonTapped: (() -> Void)?
1314
var onRejectButtonTapped: (() -> Void)?
@@ -45,9 +46,11 @@ struct SuggestionPanelView: View {
4546

4647
var body: some View {
4748
VStack {
48-
Spacer()
49-
.frame(minHeight: 0, maxHeight: .infinity)
50-
.allowsHitTesting(false)
49+
if !viewModel.alignTopToAnchor {
50+
Spacer()
51+
.frame(minHeight: 0, maxHeight: .infinity)
52+
.allowsHitTesting(false)
53+
}
5154

5255
ZStack(alignment: .topLeading) {
5356
VStack(spacing: 0) {
@@ -79,6 +82,12 @@ struct SuggestionPanelView: View {
7982
}
8083
.allowsHitTesting(viewModel.isPanelDisplayed && !viewModel.suggestion.isEmpty)
8184
.preferredColorScheme(.dark)
85+
86+
if viewModel.alignTopToAnchor {
87+
Spacer()
88+
.frame(minHeight: 0, maxHeight: .infinity)
89+
.allowsHitTesting(false)
90+
}
8291
}
8392
.opacity({
8493
guard viewModel.isPanelDisplayed else { return 0 }

0 commit comments

Comments
 (0)