use zaplib::*;
define_string_with_filename!(MAIN_SHADER);
#[derive(Clone)]
#[repr(C)]
struct DrawFloatSlider {
base: DrawQuad,
norm_value: f32,
hover: f32,
down: f32,
}
impl DrawFloatSlider {
fn new(cx: &mut Cx) -> Self {
Self {
base: DrawQuad::with_slots(
cx,
cx.get_shader(StringHash::new(MAIN_SHADER), location_hash!()),
f32::slots() + f32::slots() + f32::slots(),
),
norm_value: Default::default(),
hover: Default::default(),
down: Default::default(),
}
}
}
pub enum FloatSliderEvent {
Change { scaled_value: f32 },
DoneChanging,
None,
}
#[derive(Clone)]
pub struct FloatSlider {
pub scaled_value: f32,
pub norm_value: f32,
pub animator: Animator,
pub min: Option<f32>,
pub max: Option<f32>,
pub step: Option<f32>,
pub _size: f32,
slider: DrawFloatSlider,
pub dragging: bool,
}
const ANIM_DEFAULT: Anim = Anim {
duration: 0.2,
tracks: &[
Track::Float { key_frames: &[(1.0, 0.0)], ease: Ease::DEFAULT },
Track::Float { key_frames: &[(1.0, 0.0)], ease: Ease::DEFAULT },
],
..Anim::DEFAULT
};
const ANIM_HOVER: Anim = Anim {
duration: 0.2,
tracks: &[
Track::Float { key_frames: &[(0.0, 1.0)], ease: Ease::DEFAULT },
Track::Float { key_frames: &[(1.0, 0.0)], ease: Ease::DEFAULT },
],
..Anim::DEFAULT
};
const ANIM_DOWN: Anim = Anim {
duration: 0.2,
tracks: &[
Track::Float { key_frames: &[(1.0, 1.0)], ease: Ease::DEFAULT },
Track::Float { key_frames: &[(0.0, 0.0), (1.0, 1.0)], ease: Ease::DEFAULT },
],
..Anim::DEFAULT
};
impl FloatSlider {
pub fn new(cx: &mut Cx) -> Self {
Self {
norm_value: 0.0,
scaled_value: 0.0,
animator: Animator::new(ANIM_DEFAULT),
min: None,
max: None,
step: None,
_size: 0.0,
slider: DrawFloatSlider::new(cx),
dragging: false,
}
}
fn animate(&mut self, cx: &mut Cx) {
self.slider.hover = self.animator.get_float(0);
self.animator.get_float(0).write_shader_value(cx, self.slider.base.area(), "hover");
self.slider.down = self.animator.get_float(1);
self.animator.get_float(1).write_shader_value(cx, self.slider.base.area(), "down");
}
pub fn app_load(cx: &mut Cx) {
cx.register_shader(
MAIN_SHADER,
Some(GEOM_QUAD2D),
&[STD_SHADER_PRELUDE, DRAWQUAD_SHADER_PRELUDE],
&code_fragment!(
r#"
instance norm_value: float;
instance hover: float;
instance down: float;
fn pixel() -> vec4 {
let df = Df::viewport(pos * rect_size);
let cy = rect_size.y * 0.5;
let height = 2.;
df.box(1., cy - 0.5 * height, rect_size.x - 1., height, 1.);
df.fill(#4);
let bheight = 15.;
let bwidth = 7.;
df.box((rect_size.x - bwidth) * norm_value, cy - 0.5 * bheight, bwidth, bheight, 1.);
let color = mix(mix(#7, #B, hover), #F, down);
df.fill(color);
return df.result;
}"#
),
)
}
pub fn handle_finger(&mut self, cx: &mut Cx, rel: Vec2) -> FloatSliderEvent {
let norm_value = (rel.x / self._size).max(0.0).min(1.0);
let mut scaled_value = norm_value * (self.max.unwrap_or(1.0) - self.min.unwrap_or(0.0)) + self.min.unwrap_or(0.0);
if self.step.unwrap_or(0.0) > 0.0 {
scaled_value = (scaled_value / self.step.unwrap_or(1.0)).round() * self.step.unwrap_or(1.0);
}
let mut changed = false;
#[allow(clippy::float_cmp)]
if scaled_value != self.scaled_value {
self.scaled_value = scaled_value;
self.norm_value = norm_value;
self.norm_value.write_shader_value(cx, self.slider.base.area(), "norm_value");
changed = true;
}
if changed {
FloatSliderEvent::Change { scaled_value }
} else {
FloatSliderEvent::None
}
}
pub fn handle_float_slider(&mut self, cx: &mut Cx, event: &mut Event) -> FloatSliderEvent {
if self.animator.handle_animator(cx, event) {
self.animate(cx);
}
match event.hits(cx, self.slider.base.area(), HitOpt::default()) {
Event::FingerHover(fe) => {
cx.set_hover_mouse_cursor(MouseCursor::Arrow);
match fe.hover_state {
HoverState::In => {
self.animator.play_anim(cx, ANIM_HOVER);
}
HoverState::Out => {
self.animator.play_anim(cx, ANIM_DEFAULT);
}
_ => (),
}
}
Event::FingerDown(fe) => {
self.animator.play_anim(cx, ANIM_DOWN);
cx.set_down_mouse_cursor(MouseCursor::Arrow);
self.dragging = true;
return self.handle_finger(cx, fe.rel);
}
Event::FingerUp(fe) => {
if fe.is_over {
if fe.input_type.has_hovers() {
self.animator.play_anim(cx, ANIM_HOVER);
} else {
self.animator.play_anim(cx, ANIM_DEFAULT);
}
} else {
self.animator.play_anim(cx, ANIM_DEFAULT);
}
self.dragging = false;
return FloatSliderEvent::DoneChanging;
}
Event::FingerMove(fe) => return self.handle_finger(cx, fe.rel),
Event::FingerScroll(fs) => {
self.norm_value += fs.scroll.x / 1000.0;
self.norm_value = self.norm_value.min(1.0).max(0.0);
self.scaled_value =
self.norm_value * (self.max.unwrap_or(1.0) - self.min.unwrap_or(0.0)) + self.min.unwrap_or(0.0);
self.norm_value.write_shader_value(cx, self.slider.base.area(), "norm_value");
return FloatSliderEvent::Change { scaled_value: self.scaled_value };
}
_ => (),
}
FloatSliderEvent::None
}
pub fn draw_float_slider(
&mut self,
cx: &mut Cx,
scaled_value: f32,
min: Option<f32>,
max: Option<f32>,
step: Option<f32>,
height_scale: f32,
) {
if self.animator.process_animator(cx) {
self.animate(cx);
}
if !self.dragging {
self.scaled_value = scaled_value;
self.min = min;
self.max = max;
self.step = step;
self.norm_value = (scaled_value - min.unwrap_or(0.0)) / (max.unwrap_or(1.0) - min.unwrap_or(0.0));
}
let pad = 10.;
self._size = cx.get_turtle_rect().size.x - 2. * pad;
self.slider.norm_value = self.norm_value;
self.slider.base.draw_quad_walk(
cx,
Walk {
margin: Margin { l: pad, r: pad, ..Margin::default() },
width: Width::Fill,
height: Height::Fix(35.0 * height_scale),
},
);
}
}