Compare commits
3 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
17d4cfb263 | |
|
|
570d00d99a | |
|
|
bf2f27231a |
17
CHANGELOG.md
17
CHANGELOG.md
|
|
@ -1,5 +1,22 @@
|
||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
## [0.4.0]
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Updated to Bevy 0.16
|
||||||
|
|
||||||
|
## [0.3.2]
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- `scroll_view_node` function with default values for `ScrollView` node
|
||||||
|
- documentation to the code
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- `ScrollableContent` default Node values fix
|
||||||
|
|
||||||
## [0.3.1]
|
## [0.3.1]
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
|
||||||
11
Cargo.toml
11
Cargo.toml
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bevy_simple_scroll_view"
|
name = "bevy_simple_scroll_view"
|
||||||
version = "0.3.1"
|
version = "0.4.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
exclude = [".github/", "wasm/", "record.gif"]
|
exclude = [".github/", "wasm/", "record.gif"]
|
||||||
categories = ["game-development", "gui"]
|
categories = ["game-development", "gui"]
|
||||||
|
|
@ -11,14 +11,17 @@ license = "MIT OR Apache-2.0"
|
||||||
description = "Simple to use plugin implementing ScrollView into Bevy engine."
|
description = "Simple to use plugin implementing ScrollView into Bevy engine."
|
||||||
|
|
||||||
[dependencies.bevy]
|
[dependencies.bevy]
|
||||||
version = "0.15"
|
version = "0.16"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["bevy_ui", "bevy_asset", "bevy_text"]
|
features = ["bevy_ui", "bevy_asset", "bevy_text"]
|
||||||
|
|
||||||
[dev-dependencies.bevy]
|
[dev-dependencies.bevy]
|
||||||
version = "0.15"
|
version = "0.16"
|
||||||
default-features = true
|
default-features = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
extra_logs = []
|
extra_logs = ["bevy/bevy_log"]
|
||||||
|
|
||||||
|
[lints.rust]
|
||||||
|
missing_docs = "warn"
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ Please keep PRs small and scoped to a single feature or fix.
|
||||||
|
|
||||||
Bevy version | crate version
|
Bevy version | crate version
|
||||||
--- | ---
|
--- | ---
|
||||||
|
0.16 | 0.4
|
||||||
0.15 | 0.3
|
0.15 | 0.3
|
||||||
0.14 | 0.2
|
0.14 | 0.2
|
||||||
0.13 | 0.1
|
0.13 | 0.1
|
||||||
|
|
|
||||||
|
|
@ -1,135 +1,84 @@
|
||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
use bevy::picking::events::{Pointer, Released};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_simple_scroll_view::*;
|
use bevy_simple_scroll_view::*;
|
||||||
|
|
||||||
const CLR_1: Color = Color::srgb(0.168, 0.168, 0.168);
|
const BG_COLOR: BackgroundColor = BackgroundColor(Color::srgb(0.168, 0.168, 0.168));
|
||||||
const CLR_2: Color = Color::srgb(0.109, 0.109, 0.109);
|
const BG_COLOR_2: BackgroundColor = BackgroundColor(Color::srgb(0.109, 0.109, 0.109));
|
||||||
const CLR_3: Color = Color::srgb(0.569, 0.592, 0.647);
|
const CLR_3: Color = Color::srgb(0.569, 0.592, 0.647);
|
||||||
const CLR_4: Color = Color::srgb(0.902, 0.4, 0.004);
|
const TEXT_COLOR: TextColor = TextColor(Color::srgb(0.902, 0.4, 0.004));
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins((DefaultPlugins, ScrollViewPlugin))
|
.add_plugins((DefaultPlugins, ScrollViewPlugin))
|
||||||
.add_systems(Startup, prepare)
|
.add_systems(Startup, setup)
|
||||||
.add_systems(Update, reset_scroll)
|
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare(mut commands: Commands) {
|
fn setup(mut commands: Commands) {
|
||||||
|
let base_node = Node {
|
||||||
|
align_items: AlignItems::Center,
|
||||||
|
justify_content: JustifyContent::Center,
|
||||||
|
min_width: Val::Px(200.0),
|
||||||
|
margin: UiRect::all(Val::Px(10.0)),
|
||||||
|
border: UiRect::all(Val::Px(5.0)),
|
||||||
|
padding: UiRect::all(Val::Px(15.0)),
|
||||||
|
..default()
|
||||||
|
};
|
||||||
commands.spawn(Camera2d);
|
commands.spawn(Camera2d);
|
||||||
commands
|
commands
|
||||||
.spawn((
|
.spawn((
|
||||||
BackgroundColor(CLR_1),
|
BG_COLOR,
|
||||||
Node {
|
Node {
|
||||||
width: Val::Percent(100.0),
|
width: Val::Percent(100.0),
|
||||||
height: Val::Percent(100.0),
|
height: Val::Percent(100.0),
|
||||||
padding: UiRect::all(Val::Px(15.0)),
|
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.with_children(|p| {
|
.with_children(|p| {
|
||||||
p.spawn(Node {
|
p.spawn(Node {
|
||||||
width: Val::Percent(20.0),
|
width: Val::Percent(20.0),
|
||||||
margin: UiRect::all(Val::Px(10.0)),
|
margin: UiRect::all(Val::Px(15.0)),
|
||||||
flex_direction: FlexDirection::Column,
|
flex_direction: FlexDirection::Column,
|
||||||
..default()
|
..default()
|
||||||
})
|
})
|
||||||
.with_children(|p| {
|
.with_children(|p| {
|
||||||
for btn_action in [ScrollButton::MoveToTop, ScrollButton::MoveToBottom] {
|
p.spawn((Text::new("Scroll to:"), TEXT_COLOR));
|
||||||
p.spawn((
|
let btn = (base_node.clone(), BG_COLOR_2, Button);
|
||||||
Node {
|
p.spawn(btn.clone())
|
||||||
margin: UiRect::all(Val::Px(15.0)),
|
.observe(scroll_to_top)
|
||||||
padding: UiRect::all(Val::Px(15.0)),
|
.with_child((Text::new("top"), TEXT_COLOR));
|
||||||
max_height: Val::Px(100.0),
|
p.spawn(btn)
|
||||||
border: UiRect::all(Val::Px(3.0)),
|
.observe(scroll_to_bottom)
|
||||||
align_items: AlignItems::Center,
|
.with_child((Text::new("bottom"), TEXT_COLOR));
|
||||||
..default()
|
|
||||||
},
|
|
||||||
BackgroundColor(CLR_2),
|
|
||||||
BorderColor(CLR_4),
|
|
||||||
Button,
|
|
||||||
btn_action,
|
|
||||||
))
|
|
||||||
.with_children(|p| {
|
|
||||||
p.spawn((
|
|
||||||
Text::new(format!("{:#?}", btn_action)),
|
|
||||||
TextFont {
|
|
||||||
font_size: 25.0,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
TextColor(CLR_4),
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
p.spawn((
|
p.spawn((
|
||||||
Node {
|
Node {
|
||||||
width: Val::Percent(80.0),
|
width: Val::Percent(80.0),
|
||||||
margin: UiRect::all(Val::Px(15.0)),
|
margin: UiRect::all(Val::Px(15.0)),
|
||||||
..default()
|
..scroll_view_node()
|
||||||
},
|
},
|
||||||
BackgroundColor(CLR_2),
|
BG_COLOR_2,
|
||||||
ScrollView::default(),
|
ScrollView::default(),
|
||||||
))
|
))
|
||||||
.with_children(|p| {
|
.with_children(|p| {
|
||||||
p.spawn((
|
p.spawn(ScrollableContent::default())
|
||||||
Node {
|
|
||||||
flex_direction: bevy::ui::FlexDirection::Column,
|
|
||||||
width: Val::Percent(100.0),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
ScrollableContent::default(),
|
|
||||||
))
|
|
||||||
.with_children(|scroll_area| {
|
.with_children(|scroll_area| {
|
||||||
for i in 0..21 {
|
for i in 1..21 {
|
||||||
scroll_area
|
scroll_area
|
||||||
.spawn((
|
.spawn((base_node.clone(), BorderColor(CLR_3)))
|
||||||
Node {
|
.with_child((Text::new(format!("Nr {} out of 20", i)), TEXT_COLOR));
|
||||||
min_width: Val::Px(200.0),
|
|
||||||
margin: UiRect::all(Val::Px(15.0)),
|
|
||||||
border: UiRect::all(Val::Px(5.0)),
|
|
||||||
padding: UiRect::all(Val::Px(30.0)),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
BorderColor(CLR_3),
|
|
||||||
))
|
|
||||||
.with_children(|p| {
|
|
||||||
p.spawn((
|
|
||||||
Text::new(format!("Nr {}", i)),
|
|
||||||
TextFont {
|
|
||||||
font_size: 25.0,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
TextColor(CLR_3),
|
|
||||||
TextLayout::new_with_justify(JustifyText::Center),
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, PartialEq, Debug, Clone, Copy)]
|
fn scroll_to_top(_t: Trigger<Pointer<Released>>, mut scroll: Single<&mut ScrollableContent>) {
|
||||||
#[require(Button)]
|
scroll.scroll_to_top();
|
||||||
enum ScrollButton {
|
|
||||||
MoveToTop,
|
|
||||||
MoveToBottom,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset_scroll(
|
fn scroll_to_bottom(_t: Trigger<Pointer<Released>>, mut scroll: Single<&mut ScrollableContent>) {
|
||||||
q: Query<(&Interaction, &ScrollButton), Changed<Interaction>>,
|
scroll.scroll_to_bottom();
|
||||||
mut scrolls_q: Query<&mut ScrollableContent>,
|
|
||||||
) {
|
|
||||||
let Ok(mut scroll) = scrolls_q.get_single_mut() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
for (interaction, action) in q.iter() {
|
|
||||||
if interaction != &Interaction::Pressed {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
match action {
|
|
||||||
ScrollButton::MoveToTop => scroll.scroll_to_top(),
|
|
||||||
ScrollButton::MoveToBottom => scroll.scroll_to_bottom(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
59
src/lib.rs
59
src/lib.rs
|
|
@ -39,7 +39,7 @@ impl Plugin for ScrollViewPlugin {
|
||||||
|
|
||||||
/// Root component of scroll, it should have clipped style.
|
/// Root component of scroll, it should have clipped style.
|
||||||
#[derive(Component, Debug, Reflect)]
|
#[derive(Component, Debug, Reflect)]
|
||||||
#[require(Interaction, Node)]
|
#[require(Interaction, Node = scroll_view_node())]
|
||||||
pub struct ScrollView {
|
pub struct ScrollView {
|
||||||
/// Field which control speed of the scrolling.
|
/// Field which control speed of the scrolling.
|
||||||
/// Could be negative number to implement invert scroll
|
/// Could be negative number to implement invert scroll
|
||||||
|
|
@ -57,13 +57,40 @@ impl Default for ScrollView {
|
||||||
/// Component containing offset value of the scroll container to the parent.
|
/// Component containing offset value of the scroll container to the parent.
|
||||||
/// It is possible to update the field `pos_y` manually to move scrollview to desired location.
|
/// It is possible to update the field `pos_y` manually to move scrollview to desired location.
|
||||||
#[derive(Component, Debug, Reflect, Default)]
|
#[derive(Component, Debug, Reflect, Default)]
|
||||||
#[require(Node(scroll_view_node))]
|
#[require(Node = scroll_content_node())]
|
||||||
pub struct ScrollableContent {
|
pub struct ScrollableContent {
|
||||||
/// Scroll container offset to the `ScrollView`.
|
/// Scroll container offset to the `ScrollView`.
|
||||||
pub pos_y: f32,
|
pub pos_y: f32,
|
||||||
|
/// Maximum value for the scroll. It is updated automatically based on the size of the children nodes.
|
||||||
pub max_scroll: f32,
|
pub max_scroll: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ScrollableContent {
|
||||||
|
/// Scrolls to the top of the scroll view.
|
||||||
|
pub fn scroll_to_top(&mut self) {
|
||||||
|
self.pos_y = 0.0;
|
||||||
|
}
|
||||||
|
/// Scrolls to the bottom of the scroll view.
|
||||||
|
pub fn scroll_to_bottom(&mut self) {
|
||||||
|
self.pos_y = -self.max_scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scrolls by a specified amount.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `value`: The amount to scroll vertically. Positive values scroll down,
|
||||||
|
/// and negative values scroll up.
|
||||||
|
///
|
||||||
|
/// Ensures the new position is clamped between the valid scroll range.
|
||||||
|
pub fn scroll_by(&mut self, value: f32) {
|
||||||
|
self.pos_y += value;
|
||||||
|
self.pos_y = self.pos_y.clamp(-self.max_scroll, 0.);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a default scroll view node.
|
||||||
|
///
|
||||||
|
/// This function defines the visual and layout properties of a scrollable container.
|
||||||
pub fn scroll_view_node() -> Node {
|
pub fn scroll_view_node() -> Node {
|
||||||
Node {
|
Node {
|
||||||
overflow: Overflow::clip(),
|
overflow: Overflow::clip(),
|
||||||
|
|
@ -74,19 +101,19 @@ pub fn scroll_view_node() -> Node {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScrollableContent {
|
/// Creates a default scroll content node.
|
||||||
pub fn scroll_to_top(&mut self) {
|
pub fn scroll_content_node() -> Node {
|
||||||
self.pos_y = 0.0;
|
Node {
|
||||||
}
|
flex_direction: bevy::ui::FlexDirection::Column,
|
||||||
pub fn scroll_to_bottom(&mut self) {
|
width: Val::Percent(100.0),
|
||||||
self.pos_y = -self.max_scroll;
|
..default()
|
||||||
}
|
|
||||||
pub fn scroll_by(&mut self, value: f32) {
|
|
||||||
self.pos_y += value;
|
|
||||||
self.pos_y = self.pos_y.clamp(-self.max_scroll, 0.);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Applies the default scroll view style to newly added `ScrollView` components.
|
||||||
|
///
|
||||||
|
/// This function updates the style of all new `ScrollView` nodes with the default
|
||||||
|
/// properties defined in `scroll_view_node`.
|
||||||
pub fn create_scroll_view(mut q: Query<&mut Node, Added<ScrollView>>) {
|
pub fn create_scroll_view(mut q: Query<&mut Node, Added<ScrollView>>) {
|
||||||
let Node {
|
let Node {
|
||||||
overflow,
|
overflow,
|
||||||
|
|
@ -113,7 +140,7 @@ fn input_mouse_pressed_move(
|
||||||
if interaction != Interaction::Pressed {
|
if interaction != Interaction::Pressed {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for &child in children.iter() {
|
for child in children.iter() {
|
||||||
let Ok(mut scroll) = content_q.get_mut(child) else {
|
let Ok(mut scroll) = content_q.get_mut(child) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
@ -129,7 +156,7 @@ fn update_size(
|
||||||
) {
|
) {
|
||||||
for (children, scroll_view_node) in q.iter_mut() {
|
for (children, scroll_view_node) in q.iter_mut() {
|
||||||
let container_height = scroll_view_node.size().y * scroll_view_node.inverse_scale_factor();
|
let container_height = scroll_view_node.size().y * scroll_view_node.inverse_scale_factor();
|
||||||
for &child in children.iter() {
|
for child in children.iter() {
|
||||||
let Ok((mut scroll, node)) = content_q.get_mut(child) else {
|
let Ok((mut scroll, node)) = content_q.get_mut(child) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
@ -159,7 +186,7 @@ fn input_touch_pressed_move(
|
||||||
if interaction != Interaction::Pressed {
|
if interaction != Interaction::Pressed {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for &child in children.iter() {
|
for child in children.iter() {
|
||||||
let Ok(mut scroll) = content_q.get_mut(child) else {
|
let Ok(mut scroll) = content_q.get_mut(child) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
@ -190,7 +217,7 @@ fn scroll_events(
|
||||||
#[cfg(feature = "extra_logs")]
|
#[cfg(feature = "extra_logs")]
|
||||||
info!("Scroolling by {:#?}: {} movement", ev.unit, y);
|
info!("Scroolling by {:#?}: {} movement", ev.unit, y);
|
||||||
|
|
||||||
for &child in children.iter() {
|
for child in children.iter() {
|
||||||
let Ok(mut scroll) = content_q.get_mut(child) else {
|
let Ok(mut scroll) = content_q.get_mut(child) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue