scroll/
scroll.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//! Scrollable row of scrollable letter columns. Inspired by <https://github.com/mintlu8/bevy-rectray/blob/main/examples/scroll_discrete.rs>.

mod utils;
use utils::*;

use bevy::prelude::*;
use haalka::prelude::*;

fn main() {
    App::new()
        .add_plugins(examples_plugin)
        .add_systems(
            Startup,
            (
                |world: &mut World| {
                    ui_root().spawn(world);
                },
                camera,
            ),
        )
        .add_systems(Update, shifter)
        .run();
}

const LETTER_SIZE: f32 = 65.;

static SHIFTED: Lazy<Mutable<bool>> = Lazy::new(default);

fn letter(letter: &str, color: Color) -> impl Element {
    El::<TextBundle>::new().text(Text::from_section(
        letter,
        TextStyle {
            font_size: LETTER_SIZE,
            color,
            ..default()
        },
    ))
}

fn letter_column(rotate: usize, color: Color) -> impl Element {
    let hovered = Mutable::new(false);
    Column::<NodeBundle>::new()
        .height(Val::Px(5. * LETTER_SIZE))
        .mutable_viewport(Overflow::clip_y(), LimitToBody::Vertical)
        .on_scroll_with_system_disableable_signal(
            BasicScrollHandler::new()
                .direction(ScrollDirection::Vertical)
                .pixels(LETTER_SIZE)
                .into_system(),
            signal::or(signal::not(hovered.signal()), SHIFTED.signal()),
        )
        .with_style(move |mut style| style.top = Val::Px(-LETTER_SIZE * rotate as f32))
        .hovered_sync(hovered)
        .items(
            "abcdefghijklmnopqrstuvwxyz"
                .chars()
                .map(move |c| letter(&c.to_string(), color)),
        )
}

fn ui_root() -> impl Element {
    let hovered = Mutable::new(false);
    El::<NodeBundle>::new()
        .width(Val::Percent(100.))
        .height(Val::Percent(100.))
        .align_content(Align::center())
        .child(
            Row::<NodeBundle>::new()
                .with_style(|mut style: Mut<'_, Style>| {
                    style.column_gap = Val::Px(30.);
                    style.padding = UiRect::horizontal(Val::Px(7.5));
                })
                .width(Val::Px(300.))
                .mutable_viewport(Overflow::clip_x(), LimitToBody::Horizontal)
                .on_scroll_with_system_disableable_signal(
                    BasicScrollHandler::new()
                        .direction(ScrollDirection::Horizontal)
                        // TODO: special handler for auto discrete like rectray https://github.com/mintlu8/bevy-rectray/blob/main/examples/scroll_discrete.rs
                        .pixels(63.)
                        .into_system(),
                    signal::not(signal::and(hovered.signal(), SHIFTED.signal())),
                )
                .hovered_sync(hovered)
                .items(
                    [
                        bevy::color::palettes::css::RED,
                        bevy::color::palettes::css::ORANGE,
                        bevy::color::palettes::css::YELLOW,
                        bevy::color::palettes::css::GREEN,
                        bevy::color::palettes::css::BLUE,
                        bevy::color::palettes::css::INDIGO,
                        bevy::color::palettes::css::VIOLET,
                    ]
                    .into_iter()
                    .enumerate()
                    .map(|(i, color)| letter_column(i, color.into())),
                ),
        )
}

fn shifter(keys: Res<ButtonInput<KeyCode>>) {
    if keys.just_pressed(KeyCode::ShiftLeft) || keys.just_pressed(KeyCode::ShiftRight) {
        SHIFTED.set_neq(true);
    } else if keys.just_released(KeyCode::ShiftLeft) || keys.just_released(KeyCode::ShiftRight) {
        SHIFTED.set_neq(false);
    }
}

fn camera(mut commands: Commands) {
    commands.spawn(Camera2dBundle::default());
}