scroll/
scroll.rs

1//! Scrollable row of scrollable letter columns. Inspired by <https://github.com/mintlu8/bevy-rectray/blob/main/examples/scroll_discrete.rs>.
2
3mod utils;
4use utils::*;
5
6use bevy::prelude::*;
7use haalka::prelude::*;
8
9fn main() {
10    App::new()
11        .add_plugins(examples_plugin)
12        .add_systems(
13            Startup,
14            (
15                |world: &mut World| {
16                    ui_root().spawn(world);
17                },
18                camera,
19            ),
20        )
21        .add_systems(Update, shifter)
22        .run();
23}
24
25const LETTER_SIZE: f32 = 54.167; // 65 / 1.2
26const COMPUTED_SIZE: f32 = 66.; // TODO: how/y tho ?
27
28static SHIFTED: LazyLock<Mutable<bool>> = LazyLock::new(default);
29
30fn letter(letter: String, color: Color) -> impl Element {
31    El::<Text>::new()
32        .text_font(TextFont::from_font_size(LETTER_SIZE))
33        .text_color(TextColor(color))
34        .text(Text::new(letter))
35}
36
37fn letter_column(rotate: usize, color: Color) -> impl Element {
38    let hovered = Mutable::new(false);
39    Column::<Node>::new()
40        .with_node(|mut node| node.height = Val::Px(5. * COMPUTED_SIZE))
41        .mutable_viewport(haalka::prelude::Axis::Vertical)
42        .on_scroll_with_system_disableable_signal(
43            BasicScrollHandler::new()
44                .direction(ScrollDirection::Vertical)
45                .pixels(COMPUTED_SIZE)
46                .into_system(),
47            signal::or(signal::not(hovered.signal()), SHIFTED.signal()),
48        )
49        .with_scroll_position(move |mut scroll_position| scroll_position.offset_y = COMPUTED_SIZE * rotate as f32)
50        .hovered_sync(hovered)
51        .items(
52            "abcdefghijklmnopqrstuvwxyz"
53                .chars()
54                .map(move |c| letter(c.to_string(), color)),
55        )
56}
57
58fn ui_root() -> impl Element {
59    let hovered = Mutable::new(false);
60    El::<Node>::new()
61        .with_node(|mut node| {
62            node.width = Val::Percent(100.);
63            node.height = Val::Percent(100.);
64        })
65        .align_content(Align::center())
66        .child(
67            Row::<Node>::new()
68                .with_node(|mut node| {
69                    node.width = Val::Px(300.);
70                    node.column_gap = Val::Px(30.);
71                    node.padding = UiRect::horizontal(Val::Px(7.5));
72                })
73                .mutable_viewport(haalka::prelude::Axis::Horizontal)
74                .on_scroll_with_system_disableable_signal(
75                    BasicScrollHandler::new()
76                        .direction(ScrollDirection::Horizontal)
77                        // TODO: special handler for auto discrete like rectray https://github.com/mintlu8/bevy-rectray/blob/main/examples/scroll_discrete.rs
78                        .pixels(63.)
79                        .into_system(),
80                    signal::not(signal::and(hovered.signal(), SHIFTED.signal())),
81                )
82                .hovered_sync(hovered)
83                .items(
84                    [
85                        bevy::color::palettes::css::RED,
86                        bevy::color::palettes::css::ORANGE,
87                        bevy::color::palettes::css::YELLOW,
88                        bevy::color::palettes::css::GREEN,
89                        bevy::color::palettes::css::BLUE,
90                        bevy::color::palettes::css::INDIGO,
91                        bevy::color::palettes::css::VIOLET,
92                    ]
93                    .into_iter()
94                    .enumerate()
95                    .map(|(i, color)| letter_column(i, color.into())),
96                ),
97        )
98}
99
100fn shifter(keys: Res<ButtonInput<KeyCode>>) {
101    if keys.just_pressed(KeyCode::ShiftLeft) || keys.just_pressed(KeyCode::ShiftRight) {
102        SHIFTED.set_neq(true);
103    } else if keys.just_released(KeyCode::ShiftLeft) || keys.just_released(KeyCode::ShiftRight) {
104        SHIFTED.set_neq(false);
105    }
106}
107
108fn camera(mut commands: Commands) {
109    commands.spawn(Camera2d);
110}