use crate::events::{Event, TickEvent, MouseEvent, MouseState, KeyboardEvent, KeyboardState};
use crate::{events, Drawable, Context, Component};
use crate::events::OnEvent;
use crate::layouts::Stack;
use std::time::Duration;
#[derive(Debug, Component)]
pub struct Button<D: Drawable + 'static>(Stack, pub D);
impl<D: Drawable + 'static> Button<D> {
pub fn new(child: D) -> Self {Button(Stack::default(), child)}
}
impl<D: Drawable + 'static> OnEvent for Button<D> {
fn on_event(&mut self, _ctx: &mut Context, event: Box<dyn Event>) -> Vec<Box<dyn Event>> {
if let Some(event) = event.downcast_ref::<MouseEvent>() {
return match event.state {
MouseState::Pressed if event.position.is_some() =>
events![events::Button::Pressed(true)],
MouseState::Moved | MouseState::Scroll(..) =>
events![events::Button::Hover(event.position.is_some())],
MouseState::Released => {
match !roost::IS_MOBILE && event.position.is_some() {
true => events![events::Button::Hover(true)],
false => events![events::Button::Pressed(false)],
}
},
_ => Vec::new()
};
}
vec![event]
}
}
#[derive(Debug, Component)]
pub struct Selectable<D: Drawable + 'static>(Stack, pub D, #[skip] uuid::Uuid, #[skip] uuid::Uuid);
impl<D: Drawable + 'static> Selectable<D> {
pub fn new(child: D, group_id: uuid::Uuid) -> Self {
Selectable(Stack::default(), child, uuid::Uuid::new_v4(), group_id)
}
}
impl<D: Drawable + 'static> OnEvent for Selectable<D> {
fn on_event(&mut self, ctx: &mut Context, event: Box<dyn Event>) -> Vec<Box<dyn Event>> {
if let Some(MouseEvent { state: MouseState::Pressed, position: Some(_) }) = event.downcast_ref::<MouseEvent>() {
ctx.trigger_event(events::Selectable::Pressed(self.2, self.3));
} else if let Some(events::Selectable::Pressed(id, group_id)) = event.downcast_ref::<events::Selectable>() {
if *group_id == self.3 {
return events![events::Selectable::Selected(*id == self.2)];
}
}
vec![event]
}
}
#[derive(Debug, Component)]
pub struct Slider<D: Drawable + 'static>(Stack, pub D, #[skip] bool);
impl<D: Drawable + 'static> Slider<D> {
pub fn new(child: D) -> Self {Slider(Stack::default(), child, false)}
}
impl<D: Drawable + 'static> OnEvent for Slider<D> {
fn on_event(&mut self, _ctx: &mut Context, event: Box<dyn Event>) -> Vec<Box<dyn Event>> {
if let Some(MouseEvent { state, position, }) = event.downcast_ref::<MouseEvent>() {
return match (state, position) {
(MouseState::Pressed, Some((x, _))) => {
self.2 = true;
events![events::Slider::Start(*x)]
},
(MouseState::Released, _) => {
self.2 = false;
Vec::new()
},
(MouseState::Scroll(..) | MouseState::Moved, Some((x, _)))
if self.2 => {
events![events::Slider::Moved(*x)]
}
_ => Vec::new()
};
}
vec![event]
}
}
#[derive(Debug, Component)]
pub struct TextInput<D: Drawable + 'static>(Stack, pub D, #[skip] bool);
impl<D: Drawable + 'static> TextInput<D> {
pub fn new(child: D) -> Self {TextInput(Stack::default(), child, false)}
}
impl<D: Drawable + 'static> OnEvent for TextInput<D> {
fn on_event(&mut self, _ctx: &mut Context, event: Box<dyn Event>) -> Vec<Box<dyn Event>> {
if let Some(e) = event.downcast_ref::<MouseEvent>() {
let mut events: Vec<Box<dyn Event>> = Vec::new();
match e.state {
MouseState::Pressed if e.position.is_some() => {
self.2 = true;
events.push(Box::new(events::TextInput::Focused(true)));
}
MouseState::Pressed if e.position.is_none() => self.2 = false,
MouseState::Moved | MouseState::Scroll(..) => {
events.push(Box::new(events::TextInput::Hover(e.position.is_some())));
}
MouseState::Released => {
match !roost::IS_MOBILE && e.position.is_some() {
true => events.push(Box::new(events::TextInput::Hover(true))),
false => events.push(Box::new(events::TextInput::Focused(false))),
}
}
_ => {}
}
events.push(event);
return events;
} else if let Some(KeyboardEvent { state: KeyboardState::Pressed, key: _ }) = event.downcast_ref() {
return if self.2 { vec![event] } else { Vec::new() };
}
vec![event]
}
}
#[derive(Debug, Component)]
pub struct Scrollable<D: Drawable + 'static> {
layout: Stack,
pub inner: D,
#[skip] touching: bool,
#[skip] start_touch: Option<(f32, f32)>,
#[skip] mouse: (f32, f32),
#[skip] scroll: Option<(f32, f32)>,
#[skip] time: Option<Duration>,
#[skip] speed: Option<f32>,
}
impl<D: Drawable + 'static> Scrollable<D> {
pub fn new(child: D) -> Self {
Scrollable {
layout: Stack::default(),
inner: child,
touching: false,
start_touch: None,
mouse: (0.0, 0.0),
scroll: None,
time: None,
speed: None,
}
}
}
impl<D: Drawable + 'static> OnEvent for Scrollable<D> {
fn on_event(&mut self, ctx: &mut Context, event: Box<dyn Event>) -> Vec<Box<dyn Event>> {
if maverick_os::IS_MOBILE {
if let Some(MouseEvent{position: Some(position), state}) = event.downcast_ref::<MouseEvent>() {
match state {
MouseState::Pressed => {
self.scroll = Some(*position);
self.scroll = Some(*position);
self.touching = true;
},
MouseState::Moved => {
self.mouse = *position;
},
MouseState::Released => {
self.touching = false;
},
MouseState::Scroll(..) => {
self.scroll = Some(*position);
},
}
self.mouse = *position;
} else if event.downcast_ref::<TickEvent>().is_some() {
if !self.touching {
if let Some(time) = self.time {
match &mut self.speed {
Some(speed) => {
*speed *= 0.92;
if speed.abs() < 0.1 {
self.time = None;
self.speed = None;
self.start_touch = None;
return vec![event];
}
}
None => {
let start_y = self.start_touch.unwrap_or((0.0, 0.0)).1;
let end_y = self.scroll.unwrap_or((0.0, 0.0)).1;
let y_traveled = end_y - start_y;
let time_secs = time.as_secs_f32();
self.speed = Some(-((y_traveled / time_secs) * 0.05));
}
}
if let Some(speed) = self.speed {
let state = (speed.abs() > 0.01).then_some(
MouseState::Scroll(0.0, speed)
);
if let Some(s) = state {
ctx.trigger_event(MouseEvent { position: Some(self.mouse), state: s });
}
}
}
}
}
}
vec![event]
}
}