bevyengine / bevyengine/bevy

Scroll list with clickable items on mobile

Open
#18,923 3 comments 0 reactions 0 assignees View on GitHub
A-UI C-Feature S-Ready-For-Implementation
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 16h
Merged PRs (30d)
171

Description

## What problem does this solve or what need does it fill?

Scroll list with clickable items on mobile. Need a way to know when the pointer is dragged when the pointer is released/click.

## What solution would you like?

```rust
//! Scrollable list with clickable items in 0.16.0 on mobile.
//!
//! Scroll by drag and click only without drag started.
//!
//! Maybe something like `PointerDragging` and `update_pointer_dragging` must be implemented in bevy?
//!
//! I write small example how I do this:

use bevy::{
color::palettes::tailwind, picking::pointer::PointerId, platform::collections::HashSet,
prelude::*,
};

fn spawn_camera(mut commands: Commands) {
commands.spawn(Camera2d);
}

fn spawn_ui(mut commands: Commands) {
commands
.spawn(Node {
width: Val::Percent(100.),
height: Val::Percent(100.),
overflow: Overflow::scroll_y(),
flex_wrap: FlexWrap::Wrap,
row_gap: Val::Px(10.),
..Default::default()
})
.observe(scroll_drag)
.with_children(|s| {
for i in 1..=100 {
s.spawn((
Node {
width: Val::Percent(100.),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
padding: UiRect::vertical(Val::Px(20.)),
border: UiRect::all(Val::Px(2.)),
..Default::default()
},
BackgroundColor(tailwind::GRAY_500.into()),
BorderColor(tailwind::GRAY_300.into()),
))
.observe(item_click)
.with_child(Text::new(format!("Item #{i}")));
}
});
}

fn scroll_drag(trigger: Trigger>, mut node_query: Query<&mut ScrollPosition>) {
let Ok(mut scroll_position) = node_query.get_mut(trigger.target()) else {
return;
};
scroll_position.offset_y -= trigger.delta.y;
}

fn item_click(
trigger: Trigger>,
state: Res,
text_query: Query<(&ChildOf, &Text)>,
) {
if state.contains(&(trigger.pointer_id, trigger.button)) {
return;
}
if let Some(text) = text_query
.iter()
.find_map(|(child_of, text)| (trigger.target() == child_of.parent()).then_some(text))
{
println!("Click at: {}", text.0);
};
}

#[derive(Debug, Default, Deref, DerefMut, Resource)]
struct PointerDragging(HashSet<(PointerId, PointerButton)>);

fn update_pointer_dragging(
mut state: ResMut,
mut drag_start_events: EventReader>,
mut drag_end_events: EventReader>,
) {
for drag_start_event in drag_start_events.read() {
state.insert((drag_start_event.pointer_id, drag_start_event.button));
}

for drag_end_event in drag_end_events.read() {
state.remove(&(drag_end_event.pointer_id, drag_end_event.button));
}
}

fn main() {
App::new()
.init_resource::()
.add_plugins(DefaultPlugins)
.add_systems(Startup, (spawn_camera, spawn_ui))
.add_systems(Update, update_pointer_dragging)
.run();
}
```

Contributor guide

Open the contributing guide

Research direction

The issue names no repository files or tests. Start by tracing Bevy's picking pointer drag, drag-start, drag-end, and release events alongside UI scroll handling using the supplied example. Done means dragging scrolls the list without activating an item, while releasing without a drag still triggers the item's click behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
frontend, game-dev, mobile-dev
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.