Skip to content

Commit aba9335

Browse files
committed
fix: nested axmenu
1 parent 24b415d commit aba9335

3 files changed

Lines changed: 37 additions & 27 deletions

File tree

src/app_engine/drawing.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ impl AppEngine {
4646
})
4747
}
4848

49+
pub(super) fn finalize_hints(&self) {
50+
for hb in self.hint_boxes.iter() {
51+
hb.refresh(0, &self.overlay_frame, &self.config.theme);
52+
}
53+
}
54+
4955
/// Show/Hide hint_boxes/colored_frames, update hint text and positions.
5056
/// Returns indices of visible hint boxes
5157
pub(super) fn update_hints(&mut self) -> Vec<usize> {

src/app_engine/lifecycle.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,21 +157,30 @@ impl AppEngine {
157157
let result_rx = self.ui_element_traverse_on_activation(target);
158158

159159
let mut color_idx = 0;
160+
let mut popup_level = 0;
161+
let mut no_popup = true;
162+
160163
autoreleasepool(|_| {
161164
for (idx, signal) in result_rx.iter().enumerate() {
162165
match signal {
163-
ElementSignal::ElementFound(Some(ele)) => {
166+
ElementSignal::ElementFound(Some(ele)) if no_popup || popup_level > 0 => {
164167
let need_flush = (idx + 1) % Self::HINTBOX_FLUSH_BATCH_SIZE == 0;
165168
self.handle_element_found(ele, &mut color_idx, need_flush);
166169
}
167170
ElementSignal::TraversalFinished(target) => {
168171
self.handle_traversal_finished(target);
169-
break;
170172
}
171-
ElementSignal::ClearOnPopUp => {
172-
self.element_cache.clear();
173-
self.hint_boxes.iter().for_each(|hb| hb.free());
174-
self.hint_boxes.clear();
173+
ElementSignal::StartPopup => {
174+
if no_popup {
175+
self.hint_boxes.iter().for_each(|hb| hb.free());
176+
self.hint_boxes.clear();
177+
self.element_cache.clear();
178+
no_popup = false;
179+
}
180+
popup_level += 1;
181+
}
182+
ElementSignal::EndPopup => {
183+
popup_level -= 1;
175184
}
176185
_ => (),
177186
}
@@ -245,7 +254,7 @@ impl AppEngine {
245254
if !self.hint_boxes.is_empty() {
246255
resolve_collisions(&mut self.hint_boxes, self.hint_width, &self.config.theme);
247256
// Update layers to match final positions and labels without clearing (avoid flicker)
248-
self.update_hints();
257+
self.finalize_hints();
249258

250259
if need_help_msg {
251260
self.notify("Press Enter to act.", Level::Trace);

src/ax_element.rs

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@ use accessibility::{AXAttribute, AXUIElement, AXUIElementAttributes};
66
use accessibility_sys::{
77
AXUIElementCopyMultipleAttributeValues, AXValueCreate, AXValueGetValue, AXValueRef,
88
kAXButtonRole, kAXCellRole, kAXCheckBoxRole, kAXComboBoxRole, kAXContentListSubrole,
9-
kAXErrorSuccess, kAXGroupRole, kAXHiddenAttribute, kAXImageRole, kAXListRole,
10-
kAXMenuButtonRole, kAXMenuItemRole, kAXMenuRole, kAXPopUpButtonRole, kAXPositionAttribute,
11-
kAXPressAction, kAXRoleAttribute, kAXRowRole, kAXScrollAreaRole, kAXScrollBarRole,
12-
kAXSelectedTextRangeAttribute, kAXSizeAttribute, kAXStaticTextRole, kAXTextAreaRole,
13-
kAXTextFieldRole, kAXTitleAttribute, kAXValueTypeCFRange, kAXValueTypeCGPoint,
14-
kAXValueTypeCGSize, kAXWindowRole,
9+
kAXErrorSuccess, kAXGroupRole, kAXHiddenAttribute, kAXImageRole, kAXListRole, kAXMenuItemRole,
10+
kAXMenuRole, kAXPopUpButtonRole, kAXPositionAttribute, kAXPressAction, kAXRoleAttribute,
11+
kAXRowRole, kAXScrollAreaRole, kAXScrollBarRole, kAXSelectedTextRangeAttribute,
12+
kAXSizeAttribute, kAXStaticTextRole, kAXTextAreaRole, kAXTextFieldRole, kAXTitleAttribute,
13+
kAXValueTypeCFRange, kAXValueTypeCGPoint, kAXValueTypeCGSize, kAXWindowRole,
1514
};
1615
use core_foundation::{
1716
array::{CFArray, CFArrayRef},
@@ -35,7 +34,8 @@ pub enum ElementSignal {
3534
// Traversal
3635
ElementFound(Option<ElementOfInterest>),
3736
TraversalFinished(Target),
38-
ClearOnPopUp,
37+
StartPopup,
38+
EndPopup,
3939
}
4040

4141
fn match_helper(pattern: &str, value: &impl ToString) -> bool {
@@ -834,7 +834,7 @@ fn traverse_elements(
834834
};
835835

836836
let mut window_frame = *window_frame;
837-
let mut finish_early = false;
837+
let mut is_popup = false;
838838

839839
#[allow(non_upper_case_globals)]
840840
match ele_fp.role.as_str() {
@@ -1027,8 +1027,8 @@ fn traverse_elements(
10271027
}
10281028
}
10291029
kAXGroupRole if element.subrole().is_ok_and(|r| r == "AXApplicationDialog") => {
1030-
let _ = result_tx.send(ElementSignal::ClearOnPopUp);
1031-
finish_early = true;
1030+
let _ = result_tx.send(ElementSignal::StartPopup);
1031+
is_popup = true;
10321032
}
10331033
kAXGroupRole => match target {
10341034
Target::Clickable if element.is_clickable() => {
@@ -1050,14 +1050,9 @@ fn traverse_elements(
10501050
}
10511051
_ => (),
10521052
},
1053-
kAXMenuRole
1054-
if element
1055-
.parent()
1056-
.and_then(|p| p.role())
1057-
.is_ok_and(|r| r == kAXMenuButtonRole) =>
1058-
{
1059-
let _ = result_tx.send(ElementSignal::ClearOnPopUp);
1060-
finish_early = true;
1053+
kAXMenuRole => {
1054+
let _ = result_tx.send(ElementSignal::StartPopup);
1055+
is_popup = true;
10611056
}
10621057
kAXMenuItemRole => match target {
10631058
Target::Text => {
@@ -1123,8 +1118,8 @@ fn traverse_elements(
11231118
depth + 1,
11241119
);
11251120
}
1126-
if finish_early {
1127-
let _ = result_tx.send(ElementSignal::TraversalFinished(target.clone()));
1121+
if is_popup {
1122+
let _ = result_tx.send(ElementSignal::EndPopup);
11281123
}
11291124
}
11301125
}

0 commit comments

Comments
 (0)