use tuix_core::layout::GeometryChanged;
use crate::common::*;
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct Scroll {
pub scroll_pos: f32,
pub scroll_size: f32,
pub overflow: f32,
}
pub struct ScrollContainerH {
container: Entity,
horizontal_scroll: Entity,
pub scroll: Scroll,
pressedx: f32,
pressedy: f32,
moving: bool,
position: f32,
vertical_scroll_animation: Animation,
vertical_container_animation: Animation,
scrollbar: bool,
scroll_wheel: bool,
on_scroll: Option<Box<dyn Fn(&mut Self, &mut State, Entity)>>,
}
impl ScrollContainerH {
pub fn new() -> Self {
ScrollContainerH {
container: Entity::null(),
horizontal_scroll: Entity::null(),
scroll: Scroll::default(),
pressedx: 0.0,
pressedy: 0.0,
moving: false,
position: 0.0,
vertical_scroll_animation: Animation::default(),
vertical_container_animation: Animation::default(),
scrollbar: true,
scroll_wheel: true,
on_scroll: None,
}
}
pub fn disable_scrollbar(mut self) -> Self {
self.scrollbar = false;
self
}
pub fn disable_scroll_wheel(mut self) -> Self {
self.scroll_wheel = false;
self
}
pub fn on_scroll<F>(mut self, callback: F) -> Self
where F: 'static + Fn(&mut Self, &mut State, Entity)
{
self.on_scroll = Some(Box::new(callback));
self
}
}
impl Widget for ScrollContainerH {
type Ret = Entity;
type Data = Scroll;
fn on_build(&mut self, state: &mut State, entity: Entity) -> Self::Ret {
entity
.set_layout_type(state, LayoutType::Column)
.set_min_width(state, Pixels(0.0));
self.container = Element::new().build(state, entity, |builder| {
builder
.set_width(Auto)
.set_height(Stretch(1.0))
.class("container")
});
state.style.clip_widget.insert(self.container, entity);
self.horizontal_scroll = Element::new().build(state, entity, |builder| {
builder
.set_min_width(Pixels(0.0))
.class("scrollbar")
});
entity.set_disabled(state, true);
entity.set_element(state, "scroll_containerh");
let vertical_scroll_animation = AnimationState::new()
.with_duration(std::time::Duration::from_millis(100))
.with_keyframe((0.0, Units::Percentage(0.0)))
.with_keyframe((1.0, Units::Percentage(20.0)));
self.vertical_scroll_animation =
state.style.left.insert_animation(vertical_scroll_animation);
let vertical_container_animation = AnimationState::new()
.with_duration(std::time::Duration::from_millis(100))
.with_keyframe((0.0, Units::Percentage(0.0)))
.with_keyframe((1.0, Units::Percentage(-20.0)));
self.vertical_container_animation = state
.style
.left
.insert_animation(vertical_container_animation);
self.container
}
fn on_update(&mut self, state: &mut State, _entity: Entity, data: &Self::Data) {
self.scroll = *data;
let overflow2 = 1.0 - (1.0 / (1.0 - self.scroll.overflow));
self.container
.set_left(state, Units::Percentage(self.scroll.scroll_pos * self.scroll.overflow * 100.0));
self.horizontal_scroll
.set_left(state, Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0));
}
fn on_event(&mut self, state: &mut State, entity: Entity, event: &mut Event) {
if let Some(window_event) = event.message.downcast::<WindowEvent>() {
match window_event {
WindowEvent::GeometryChanged(geometry_changed) => {
if event.target == self.container || event.target == entity {
if geometry_changed.contains(GeometryChanged::WIDTH_CHANGED) || geometry_changed.contains(GeometryChanged::HEIGHT_CHANGED) {
self.scroll.scroll_size =
state.data.get_width(entity) / state.data.get_width(self.container);
if self.scroll.scroll_size >= 1.0 {
self.scroll.scroll_size = 1.0;
entity.set_disabled(state, true);
}
if self.scroll.scroll_size < 1.0 {
entity.set_disabled(state, false);
}
if !state.style.left.is_animating(self.horizontal_scroll) {
let dist = state.data.get_posx(self.horizontal_scroll)
- state.data.get_posx(entity);
let space = state.data.get_width(entity)
- (self.scroll.scroll_size * state.data.get_width(entity));
self.scroll.scroll_pos = dist / space;
}
if self.scroll.scroll_pos.is_nan() {
self.scroll.scroll_pos = 0.0;
}
if self.scroll.scroll_pos < 0.0 {
self.scroll.scroll_pos = 0.0;
}
if self.scroll.scroll_pos >= 1.0 {
self.scroll.scroll_pos = 1.0;
}
state
.style
.width
.insert(self.horizontal_scroll, Units::Percentage(self.scroll.scroll_size * 100.0))
.expect("");
self.scroll.overflow = 1.0
- (state.data.get_width(self.container)
/ state.data.get_width(entity));
let overflow2 = 1.0 - (1.0 / (1.0 - self.scroll.overflow));
state
.style
.left
.insert(self.container, Units::Percentage(self.scroll.scroll_pos * self.scroll.overflow * 100.0))
.expect("");
state.style.left.insert(
self.horizontal_scroll,
Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0),
).expect("");
state.insert_event(Event::new(WindowEvent::Relayout).target(Entity::root()).origin(entity));
if let Some(callback) = self.on_scroll.take() {
(callback)(self, state, entity);
self.on_scroll = Some(callback);
}
}
}
}
WindowEvent::MouseScroll(x, _) => {
if self.scroll_wheel {
self.scroll.overflow = state.data.get_width(entity)
- state.data.get_width(self.horizontal_scroll);
if self.scroll.overflow == 0.0 {
return;
}
self.scroll.overflow = 1.0
- (state.data.get_width(self.container) / state.data.get_width(entity));
let overflow2 = 1.0
- (state.data.get_width(entity) / state.data.get_width(self.container));
self.scroll.scroll_pos += (30.0 * -*x) / (state.data.get_width(entity) * self.scroll.overflow);
if self.scroll.scroll_pos < 0.0 {
self.scroll.scroll_pos = 0.0;
}
if self.scroll.scroll_pos > 1.0 {
self.scroll.scroll_pos = 1.0;
}
let _current_scroll_top = state
.style
.left
.get(self.horizontal_scroll)
.cloned()
.unwrap_or_default();
let _current_container_top = state
.style
.left
.get(self.container)
.cloned()
.unwrap_or_default();
self.container
.set_left(state, Units::Percentage(self.scroll.scroll_pos * self.scroll.overflow * 100.0));
self.horizontal_scroll
.set_left(state, Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0));
if let Some(callback) = self.on_scroll.take() {
(callback)(self, state, entity);
self.on_scroll = Some(callback);
}
}
}
WindowEvent::WindowResize(_, _) => {
}
WindowEvent::MouseDown(button) => match button {
MouseButton::Left => {
if state.hovered == self.horizontal_scroll {
self.pressedx = state.mouse.cursorx;
self.pressedy = state.mouse.cursory;
self.moving = true;
self.position = self.scroll.scroll_pos;
state.capture(entity);
}
}
_ => {}
},
WindowEvent::MouseUp(button) => match button {
MouseButton::Left => {
self.moving = false;
state.release(entity);
}
_ => {}
},
WindowEvent::MouseMove(x, _) => {
if self.moving {
let dist_x = *x - self.pressedx;
let scroll_bar_overflow = state.data.get_width(entity)
- state.data.get_width(self.horizontal_scroll);
if scroll_bar_overflow == 0.0 {
return;
}
let ratio = dist_x / scroll_bar_overflow;
let r = self.position + ratio;
self.scroll.scroll_pos = r;
if self.scroll.scroll_pos < 0.0 {
self.scroll.scroll_pos = 0.0;
}
if self.scroll.scroll_pos > 1.0 {
self.scroll.scroll_pos = 1.0;
}
self.scroll.overflow = 1.0
- (state.data.get_width(self.container) / state.data.get_width(entity));
let overflow2 = 1.0
- (state.data.get_width(entity) / state.data.get_width(self.container));
self.container
.set_left(state, Units::Percentage(self.scroll.scroll_pos * self.scroll.overflow * 100.0));
self.horizontal_scroll
.set_left(state, Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0));
if let Some(callback) = self.on_scroll.take() {
(callback)(self, state, entity);
self.on_scroll = Some(callback);
}
state.insert_event(Event::new(WindowEvent::Restyle));
state
.insert_event(Event::new(WindowEvent::Relayout).target(Entity::root()));
state.insert_event(Event::new(WindowEvent::Redraw));
}
}
_ => {}
}
}
}
}
pub struct ScrollContainer {
container: Entity,
vertical_scroll: Entity,
pub scroll: Scroll,
scrollbar: bool,
pressedx: f32,
pressedy: f32,
moving: bool,
position: f32,
vertical_scroll_animation: Animation,
vertical_container_animation: Animation,
on_scroll: Option<Box<dyn Fn(&mut Self, &mut State, Entity)>>,
}
impl ScrollContainer {
pub fn new() -> Self {
ScrollContainer {
container: Entity::null(),
vertical_scroll: Entity::null(),
scroll: Scroll::default(),
scrollbar: true,
pressedx: 0.0,
pressedy: 0.0,
moving: false,
position: 0.0,
vertical_scroll_animation: Animation::default(),
vertical_container_animation: Animation::default(),
on_scroll: None,
}
}
pub fn disable_scrollbar(mut self) -> Self {
self.scrollbar = false;
self
}
pub fn on_scroll<F>(mut self, callback: F) -> Self
where F: 'static + Fn(&mut Self, &mut State, Entity)
{
self.on_scroll = Some(Box::new(callback));
self
}
}
impl Widget for ScrollContainer {
type Ret = Entity;
type Data = Scroll;
fn on_build(&mut self, state: &mut State, entity: Entity) -> Self::Ret {
entity
.set_layout_type(state, LayoutType::Row)
.set_min_height(state, Pixels(0.0));
self.container = Element::new().build(state, entity, |builder| {
builder
.set_height(Auto)
.set_width(Stretch(1.0))
.class("container")
});
state.style.clip_widget.insert(self.container, entity);
self.vertical_scroll = Element::new().build(state, entity, |builder| {
builder
.set_min_height(Pixels(0.0))
.class("scrollbar")
});
entity.set_disabled(state, true);
entity.set_element(state, "scroll_container");
let vertical_scroll_animation = AnimationState::new()
.with_duration(std::time::Duration::from_millis(100))
.with_keyframe((0.0, Units::Percentage(0.0)))
.with_keyframe((1.0, Units::Percentage(20.0)));
self.vertical_scroll_animation =
state.style.top.insert_animation(vertical_scroll_animation);
let vertical_container_animation = AnimationState::new()
.with_duration(std::time::Duration::from_millis(100))
.with_keyframe((0.0, Units::Percentage(0.0)))
.with_keyframe((1.0, Units::Percentage(-20.0)));
self.vertical_container_animation = state
.style
.top
.insert_animation(vertical_container_animation);
self.container
}
fn on_update(&mut self, state: &mut State, entity: Entity, data: &Self::Data) {
self.scroll = *data;
let overflow2 = 1.0
- (state.data.get_height(entity)
/ state.data.get_height(self.container));
self.container
.set_top(state, Units::Percentage(self.scroll.scroll_pos * self.scroll.overflow * 100.0));
self.vertical_scroll
.set_top(state, Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0));
}
fn on_event(&mut self, state: &mut State, entity: Entity, event: &mut Event) {
if let Some(window_event) = event.message.downcast::<WindowEvent>() {
match window_event {
WindowEvent::GeometryChanged(geometry_changed) => {
if event.target == self.container || event.target == entity {
if geometry_changed.contains(GeometryChanged::WIDTH_CHANGED) || geometry_changed.contains(GeometryChanged::HEIGHT_CHANGED) {
self.scroll.scroll_size = state.data.get_height(entity)
/ state.data.get_height(self.container);
if self.scroll.scroll_size >= 1.0 {
self.scroll.scroll_size = 1.0;
entity.set_disabled(state, true);
}
if self.scroll.scroll_size < 1.0 {
entity.set_disabled(state, false);
}
if !state.style.top.is_animating(self.vertical_scroll) {
let dist = state.data.get_posy(self.vertical_scroll)
- state.data.get_posy(entity);
let space = state.data.get_height(entity)
- (self.scroll.scroll_size * state.data.get_height(entity));
self.scroll.scroll_pos = dist / space;
}
if self.scroll.scroll_pos.is_nan() {
self.scroll.scroll_pos = 0.0;
}
if self.scroll.scroll_pos < 0.0 {
self.scroll.scroll_pos = 0.0;
}
if self.scroll.scroll_pos >= 1.0 {
self.scroll.scroll_pos = 1.0;
}
state
.style
.height
.insert(self.vertical_scroll, Units::Percentage(self.scroll.scroll_size * 100.0))
.expect("");
self.scroll.overflow = 1.0
- (state.data.get_height(self.container)
/ state.data.get_height(entity));
let overflow2 = 1.0
- (state.data.get_height(entity)
/ state.data.get_height(self.container));
state
.style
.top
.insert(self.container, Units::Percentage(self.scroll.scroll_pos * self.scroll.overflow * 100.0)).expect("Failed to set top position of container");
state.style.top.insert(
self.vertical_scroll,
Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0),
).expect("Failed to set top position of vertical scroll bar.");
state.insert_event(
Event::new(WindowEvent::Relayout)
.target(Entity::root())
.origin(entity),
);
if let Some(callback) = self.on_scroll.take() {
(callback)(self, state, entity);
self.on_scroll = Some(callback);
}
}
}
}
WindowEvent::MouseScroll(_, y) => {
self.scroll.overflow =
state.data.get_height(entity) - state.data.get_height(self.vertical_scroll);
if self.scroll.overflow == 0.0 {
return;
}
self.scroll.overflow = 1.0
- (state.data.get_height(self.container) / state.data.get_height(entity));
let overflow2 = 1.0
- (state.data.get_height(entity) / state.data.get_height(self.container));
self.scroll.scroll_pos += (30.0 * *y) / (state.data.get_height(entity) * self.scroll.overflow);
if self.scroll.scroll_pos < 0.0 {
self.scroll.scroll_pos = 0.0;
}
if self.scroll.scroll_pos > 1.0 {
self.scroll.scroll_pos = 1.0;
}
self.container
.set_top(state, Units::Percentage(self.scroll.scroll_pos * self.scroll.overflow * 100.0));
self.vertical_scroll
.set_top(state, Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0));
if let Some(callback) = self.on_scroll.take() {
(callback)(self, state, entity);
self.on_scroll = Some(callback);
}
}
WindowEvent::WindowResize(_, _) => {
}
WindowEvent::MouseDown(button) => match button {
MouseButton::Left => {
if state.hovered == self.vertical_scroll {
self.pressedx = state.mouse.cursorx;
self.pressedy = state.mouse.cursory;
self.moving = true;
self.position = self.scroll.scroll_pos;
state.capture(entity);
}
}
_ => {}
},
WindowEvent::MouseUp(button) => match button {
MouseButton::Left => {
self.moving = false;
state.release(entity);
}
_ => {}
},
WindowEvent::MouseMove(_, y) => {
if self.moving {
let dist_y = *y - self.pressedy;
let scroll_bar_overflow = state.data.get_height(entity)
- state.data.get_height(self.vertical_scroll);
if scroll_bar_overflow == 0.0 {
return;
}
let ratio = dist_y / scroll_bar_overflow;
let r = self.position + ratio;
self.scroll.scroll_pos = r;
if self.scroll.scroll_pos < 0.0 {
self.scroll.scroll_pos = 0.0;
}
if self.scroll.scroll_pos > 1.0 {
self.scroll.scroll_pos = 1.0;
}
self.scroll.overflow = 1.0
- (state.data.get_height(self.container)
/ state.data.get_height(entity));
let overflow2 = 1.0
- (state.data.get_height(entity)
/ state.data.get_height(self.container));
self.container
.set_top(state, Units::Percentage(self.scroll.scroll_pos * self.scroll.overflow * 100.0));
self.vertical_scroll
.set_top(state, Units::Percentage(self.scroll.scroll_pos * overflow2 * 100.0));
if let Some(callback) = self.on_scroll.take() {
(callback)(self, state, entity);
self.on_scroll = Some(callback);
}
state.insert_event(Event::new(WindowEvent::Restyle));
state
.insert_event(Event::new(WindowEvent::Relayout).target(Entity::root()));
state.insert_event(Event::new(WindowEvent::Redraw));
}
}
_ => {}
}
}
}
}