use rin::graphics::*;
use rin::events::*;
use rin::math::*;
use num_traits::{FromPrimitive,ToPrimitive};
use std::ops::Sub;
use rin::window;
use rin::window::events::*;
use std::fmt::Display;
use std::rc::Rc;
use traits::*;
use rin::color::*;
use rin::color::consts::*;
use std::fmt::Debug;
use rin::events::{RangedParameterMut, ConnectToParameter};
use utils;
use control_shape::ControlShape;
pub struct Slider<T: Debug + Clone + 'static>{
label: String,
parameter: RangedParameterMut<'static, T>,
rendered: Sender<'static, ()>,
value: StreamRc<'static, T>,
font: Rc<Ttf>,
color: Rgb<f32>,
non_attended: StreamRc<'static, window::Event>,
geometry: Property<'static, Vec<Vertex2DColor>>,
text_geometry: Property<'static, Vec<Vertex2DTex>>,
width: Property<'static, f32>,
height: Property<'static, f32>,
position: Property<'static, Pnt2>,
}
pub struct SliderBuilder<T: Clone + Debug + 'static>{
label: String,
parameter: RangedParameterMut<'static, T>,
events: StreamRc<'static, window::Event>,
shape: ControlShape,
}
fn geometry(rect: Rect<f32>, pct: f32, color: Rgb<f32>, alpha: f32) -> Vec<Vertex2DColor>{
let x = rect.pos.x;
let y = rect.pos.y;
let w = rect.width;
let h = rect.height;
let w_value = clamp(pct as f32, 0., 1.) * w;
let border_color = &rgba!(color, 0.1);
let slider_color = &rgba!(color, alpha);
vec![
vertex2dcolor(vec2(x,y), border_color),
vertex2dcolor(vec2(x+w,y), border_color),
vertex2dcolor(vec2(x+w,y+h), border_color),
vertex2dcolor(vec2(x+w,y+h), border_color),
vertex2dcolor(vec2(x,y+h), border_color),
vertex2dcolor(vec2(x,y), border_color),
vertex2dcolor(vec2(x,y), slider_color),
vertex2dcolor(vec2(x+w_value,y), slider_color),
vertex2dcolor(vec2(x+w_value,y+h), slider_color),
vertex2dcolor(vec2(x+w_value,y+h), slider_color),
vertex2dcolor(vec2(x,y+h), slider_color),
vertex2dcolor(vec2(x,y), slider_color),
]
}
impl<T: FromPrimitive + Sub<Output=T> + Clone + ToPrimitive + Display + PartialEq + Debug + 'static> SliderBuilder<T>{
pub fn new<S: StreamT<'static, window::Event>>(name: &str, parameter: RangedParameterMut<'static, T>, events: S) -> SliderBuilder<T>{
let pos = Property::new(origin());
let width = Property::new(200.);
let height = Property::new(15.);
let margin = 0.;
SliderBuilder{
label: name.to_string(),
parameter: parameter,
events: events.rc(),
shape: ControlShape::new(pos, width, height, margin, margin, WHITE),
}
}
pub fn create(mut self) -> Slider<T>{
let (rendered_sender, rendered_stream) = utils::rendered_stream(self.events.clone().unique());
let rendered = rendered_stream.to_property(false);
let events = self.events.clone();
let width = self.shape.width();
let height = self.shape.height();
let position = self.shape.position();
let rect = position.clone()
.zip(width.clone())
.zip(height.clone())
.flatten()
.map(|(pos, width, height)| Rect{pos, width, height});
let font = self.shape.font().unwrap_or_else(|| ::utils::default_font());
let rect64: Property<'_, Rect<f64>> = rect.clone().map(|r| convert(r));
let over = {
let rect = rect64.clone();
let rendered = rendered.clone();
events.clone().mouse().moved()
.map(move |pos| *rendered && pos.inside(&rect))
.to_property(false)
};
let pressed = events.clone().mouse().pressed(MouseButton::Left);
let pressed_over = pressed
.filter_by(over.clone())
.rc();
let released = events.clone().mouse().released(MouseButton::Left).to_bool().not();
let over_and_pressed = pressed_over.clone()
.to_bool()
.merge(released)
.to_property(false);
let value = {
let rect = rect64;
events.clone().mouse().moved()
.filter_by(over_and_pressed.clone())
.merge(pressed_over.map(|(pos,_button)| pos))
.map(move |pos| clamp((pos.x - rect.pos.x) / rect.width, 0., 1.) )
.connect_to_ranged_parameter_pct(&mut self.parameter)
};
let over_copy = over.clone();
let over_and_pressed_copy = over_and_pressed.clone();
let non_attended = self.events.clone()
.filter(move |e| {
if let window::Event::MousePressed{..} = e {
!*over_copy && !*over_and_pressed_copy
}else{
true
}
});
let filtered_update = self.events.clone()
.filter_by(rendered)
.update()
.rc();
let parameter = self.parameter.clone();
let color = self.shape.color();
let alpha = over_and_pressed.zip(over).map(|(over_and_pressed, over)|{
if over_and_pressed {0.9} else if over {0.7} else {0.5}
});
let rect1 = rect.clone();
let geometry = filtered_update.clone()
.map(move |_| (parameter.pct() as f32, *alpha, *rect1))
.dedup()
.map(move |(pct, alpha, rect)| {
geometry(rect, pct, color, alpha)
}).to_property(vec![]);
let font_copy = font.clone();
let label = self.label.to_string();
let max = self.parameter.max();
let parameter = self.parameter.clone();
let text_geometry = if self.shape.label_visible(){
filtered_update
.map(move |_| (parameter.to_value(), *rect))
.dedup()
.map(move |(value, rect)| {
let x = rect.pos.x;
let y = rect.pos.y + rect.height + 15.;
let total_w = rect.width;
let value_w = font_copy.bounding_box(&format!(": {}", max), x, y, CoordinateOrigin::TopLeft).width;
let label = utils::shorten(&label, &font_copy, total_w - value_w);
let text = format!("{}: {}", label, value);
let mesh = font_copy.mesh(&text, x, y, CoordinateOrigin::TopLeft).into();
mesh
}).to_property(vec![])
}else{
Property::default()
};
let height = if self.shape.label_visible(){
let line_height = font.line_height();
height.map(move |h| h + line_height)
}else{
height
};
Slider{
label: self.label.to_string(),
parameter: self.parameter.clone(),
value: value.rc(),
rendered: rendered_sender,
font: font,
color: self.shape.color(),
non_attended: non_attended.rc(),
geometry: geometry,
text_geometry: text_geometry,
width,
height,
position,
}
}
}
impl<T: Clone + Debug + 'static + FromPrimitive + ToPrimitive + Sub<Output=T>> Slider<T>{
pub fn stream(&self) -> Stream<'static, T>{
let parameter = self.parameter.clone();
self.value.clone().map(move |_| parameter.to_value())
}
pub fn set_parameter(&mut self, parameter: RangedParameterMut<'static, T>){
self.parameter = parameter;
}
}
impl<T: Debug + Clone + 'static> ControlGeometry for Slider<T>{
fn flexible() -> bool{
true
}
fn geometry(&self, _container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DColor>>{
self.rendered.send(());
Some(self.geometry.to_value())
}
fn text_geometry(&self, _container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DTex>>{
Some(self.text_geometry.to_value())
}
fn texture_geometry(&self, _container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DTex>>{
None
}
fn font(&self) -> Option<&Ttf>{
Some(&self.font)
}
fn color(&self) -> &Rgb<f32>{
&self.color
}
fn position(&self) -> &Property<'static, Pnt2>{
&self.position
}
fn width(&self) -> &Property<'static, f32>{
&self.width
}
fn height(&self) -> &Property<'static, f32>{
&self.height
}
}
impl<T: Debug + Clone + 'static> ControlEvents for Slider<T>{
fn non_attended(&self) -> StreamRc<'static, window::Event>{
self.non_attended.clone()
}
}
use std::any::Any;
impl<T: Any + Debug + 'static + Clone> Control for Slider<T>{
fn name(&self) -> &str{
&self.label
}
}
impl<T: Debug> ControlBuilder for SliderBuilder<T>
where T: FromPrimitive + Sub<Output=T> + Clone + ToPrimitive + Display + PartialEq + Debug + 'static{
type Control = Slider<T>;
fn shape(&mut self) -> &mut ControlShape {
&mut self.shape
}
fn create(self) -> Slider<T>{
self.create()
}
}
impl<T: Debug> ControlBuilt<RangedParameterMut<'static, T>> for Slider<T>
where T: FromPrimitive + Sub<Output=T> + Clone + ToPrimitive + Display + PartialEq + Debug + 'static{
type Builder = SliderBuilder<T>;
}
impl<T: Debug> ControlDefaultProperty for RangedParameterMut<'static, T>
where T: FromPrimitive + Sub<Output=T> + Clone + ToPrimitive + Display + PartialEq + Debug + 'static{
type Builder = SliderBuilder<T>;
}
impl<T: Debug> BuilderFromProperty<RangedParameterMut<'static, T>> for SliderBuilder<T>
where T: FromPrimitive + Sub<Output=T> + Clone + ToPrimitive + Display + PartialEq + Debug + 'static{
fn from_parameter<S: StreamT<'static, window::Event>>(parameter: RangedParameterMut<'static, T>, name: &str, events: S) -> SliderBuilder<T>{
SliderBuilder::new(name, parameter, events)
}
}
#[cfg(any(feature = "gl", feature = "gles", feature="webgl"))]
mod gl{
use rin::gl::{self, Renderer2d};
use rin::graphics::*;
use traits::ControlGeometry;
use std::fmt::Debug;
use utils;
use rin::gl::RenderSurface;
use rin::math::{convert, Rect, InsideRect};
impl<T: Debug + Clone> gl::Render3d for super::Slider<T>{
fn render<R: RenderSurface>(&self, gl: &gl::Renderer<R>){
let viewport = *gl.context().state().viewport();
let viewport: Rect<i32> = viewport.into();
let viewport: Rect<f32> = convert(viewport);
if self.rect().inside(&viewport) {
let viewport = Some(&viewport);
if let Some(geometry) = self.geometry(viewport).map(|g| mesh(g, PrimitiveType::Triangles)){
let gl = gl.with_properties(&[
gl::Property::Blend(true),
gl::Property::BlendEquation(gl::FUNC_ADD),
gl::Property::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA),
]);
gl.draw_mesh(&geometry);
}
if let (Some(text_geometry), Some(font)) = (self.text_geometry(viewport), self.font()){
let font_material = utils::gl::font_material(&gl, font, self.color());
let text_geometry = mesh(text_geometry, PrimitiveType::Triangles);
gl.draw_mesh_with_material(&text_geometry, font_material);
}
}
}
}
}