Skip to content

Commit e59b769

Browse files
committed
fix: guard picker taps during touch scroll
1 parent fa23246 commit e59b769

2 files changed

Lines changed: 82 additions & 18 deletions

File tree

vizmat-core/src/ui.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ pub(crate) use picker::{
3939
set_structure_picker_keyboard_active, setup_structure_picker_panel,
4040
structure_picker_keyboard_search, structure_picker_result_buttons, structure_picker_scroll,
4141
structure_picker_toggle_button, update_structure_picker_scroll_indicator,
42-
StructurePickerCaretState, StructurePickerResultsScroll, StructurePickerState,
43-
StructurePickerToggleButton,
42+
StructurePickerCaretState, StructurePickerResultsScroll, StructurePickerSelectionState,
43+
StructurePickerState, StructurePickerToggleButton,
4444
};
4545

4646
const LAYER_GIZMO: RenderLayers = RenderLayers::layer(1);
@@ -1146,6 +1146,7 @@ pub(crate) fn setup_file_ui(mut commands: Commands, mut font_assets: ResMut<Asse
11461146
visible: false,
11471147
});
11481148
commands.insert_resource(StructurePickerCaretState::default());
1149+
commands.insert_resource(StructurePickerSelectionState::default());
11491150
let p = theme_palette(theme.mode);
11501151
let is_mobile = {
11511152
#[cfg(target_arch = "wasm32")]

vizmat-core/src/ui/picker.rs

Lines changed: 79 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,31 @@ pub(crate) struct StructurePickerState {
5858
pub(crate) visible: bool,
5959
}
6060

61+
#[derive(Resource, Default)]
62+
pub(crate) struct StructurePickerSelectionState {
63+
pending: Option<(Entity, String)>,
64+
suppress_click_frames: u8,
65+
}
66+
67+
impl StructurePickerSelectionState {
68+
fn consume_suppression(&mut self) -> bool {
69+
if self.suppress_click_frames > 0 {
70+
self.suppress_click_frames = self.suppress_click_frames.saturating_sub(1);
71+
true
72+
} else {
73+
false
74+
}
75+
}
76+
77+
fn suppress_for_touch_drag(&mut self) {
78+
self.suppress_click_frames = self.suppress_click_frames.max(2);
79+
}
80+
81+
fn is_suppressed(&self) -> bool {
82+
self.suppress_click_frames > 0
83+
}
84+
}
85+
6186
#[derive(Resource)]
6287
pub(crate) struct StructurePickerCaretState {
6388
timer: Timer,
@@ -640,22 +665,30 @@ pub(crate) fn update_structure_picker_scroll_indicator(
640665
*visibility = Visibility::Inherited;
641666
}
642667

668+
#[allow(clippy::too_many_arguments)]
643669
pub(crate) fn structure_picker_scroll(
644670
mut mouse_wheel_events: EventReader<MouseWheel>,
645671
hover_map: Res<HoverMap>,
646672
scroll_nodes: Query<Entity, With<StructurePickerResultsScroll>>,
673+
panel_nodes: Query<Entity, With<StructurePickerPanel>>,
647674
mut scroll_positions: Query<&mut ScrollPosition>,
648675
parents: Query<&ChildOf>,
649676
touch_gesture_state: Res<TouchGestureState>,
650677
keyboard_input: Res<ButtonInput<KeyCode>>,
678+
mut picker_selection_state: ResMut<StructurePickerSelectionState>,
651679
) {
652-
let picker_hovered = scroll_nodes.iter().any(|scroll_root| {
653-
hover_map.iter().any(|(_, pointer_map)| {
654-
pointer_map
655-
.iter()
656-
.any(|(hovered, _)| is_descendant_or_self(*hovered, scroll_root, &parents))
657-
})
658-
});
680+
let _ = picker_selection_state.consume_suppression();
681+
682+
let picker_hovered = panel_nodes
683+
.iter()
684+
.chain(scroll_nodes.iter())
685+
.any(|picker_root| {
686+
hover_map.iter().any(|(_, pointer_map)| {
687+
pointer_map
688+
.iter()
689+
.any(|(hovered, _)| is_descendant_or_self(*hovered, picker_root, &parents))
690+
})
691+
});
659692

660693
for mouse_wheel in mouse_wheel_events.read() {
661694
let (mut dx, mut dy) = match mouse_wheel.unit {
@@ -692,7 +725,10 @@ pub(crate) fn structure_picker_scroll(
692725
}
693726
}
694727

695-
if picker_hovered && touch_gesture_state.rotate.length_squared() > 0.0004 {
728+
let rotate_drag = touch_gesture_state.rotate.length_squared() > 0.0004;
729+
if picker_hovered && rotate_drag {
730+
picker_selection_state.suppress_for_touch_drag();
731+
picker_selection_state.pending = None;
696732
for scroll_root in scroll_nodes.iter() {
697733
if let Ok(mut scroll_position) = scroll_positions.get_mut(scroll_root) {
698734
scroll_position.offset_x -= touch_gesture_state.rotate.x;
@@ -719,6 +755,7 @@ fn is_descendant_or_self(mut entity: Entity, ancestor: Entity, parents: &Query<&
719755
pub(crate) fn structure_picker_result_buttons(
720756
mut interaction_query: Query<
721757
(
758+
Entity,
722759
&Interaction,
723760
&StructurePickerResultButton,
724761
&mut BackgroundColor,
@@ -729,27 +766,53 @@ pub(crate) fn structure_picker_result_buttons(
729766
mut file_drag_drop: ResMut<crate::io::FileDragDrop>,
730767
catalog_channel: Option<Res<CatalogLoadChannel>>,
731768
theme: Res<UiTheme>,
769+
mut picker_selection_state: ResMut<StructurePickerSelectionState>,
732770
) {
733-
for (interaction, selected, mut background) in &mut interaction_query {
771+
let mut selected_path = None;
772+
773+
for (entity, interaction, selected, mut background) in &mut interaction_query {
734774
match *interaction {
735775
Interaction::Pressed => {
736776
*background = BackgroundColor(themed_button_bg(theme.mode, Interaction::Pressed));
737-
super::load_structure_from_catalog_path(
738-
&selected.path,
739-
&mut file_drag_drop,
740-
catalog_channel.as_deref(),
741-
);
742-
picker.visible = false;
743-
set_structure_picker_keyboard_active(false);
777+
if !picker_selection_state.is_suppressed() {
778+
picker_selection_state.pending = Some((entity, selected.path.clone()));
779+
}
744780
}
745781
Interaction::Hovered => {
746782
*background = BackgroundColor(themed_button_bg(theme.mode, Interaction::Hovered));
783+
784+
if let Some((pending_entity, pending_path)) = picker_selection_state.pending.take()
785+
{
786+
if pending_entity == entity {
787+
if !picker_selection_state.is_suppressed() {
788+
selected_path = Some(pending_path);
789+
}
790+
} else {
791+
picker_selection_state.pending = Some((pending_entity, pending_path));
792+
}
793+
}
747794
}
748795
Interaction::None => {
749796
*background = BackgroundColor(themed_button_bg(theme.mode, Interaction::None));
797+
798+
if let Some((pending_entity, _)) = picker_selection_state.pending.as_ref() {
799+
if *pending_entity == entity {
800+
picker_selection_state.pending = None;
801+
}
802+
}
750803
}
751804
}
752805
}
806+
807+
if let Some(path) = selected_path {
808+
super::load_structure_from_catalog_path(
809+
&path,
810+
&mut file_drag_drop,
811+
catalog_channel.as_deref(),
812+
);
813+
picker.visible = false;
814+
set_structure_picker_keyboard_active(false);
815+
}
753816
}
754817

755818
fn structure_matches_query(path: &str, query: &str) -> bool {

0 commit comments

Comments
 (0)