use rin::graphics::*;
use rin::events::*;
use rin::math::*;
use rin::window;
use rin::window::events::*;
use traits::*;
use rin::color::*;
use rin::color::consts::*;
use control_shape::ControlShape;
use utils;
use std::fmt::{self, Debug};
pub struct Scrollbar{
scroll_pct: Property<'static, f32>,
rendered: Sender<'static, ()>,
color: Rgb<f32>,
non_attended: StreamRc<'static, window::Event>,
geometry: Property<'static, Vec<Vertex2DColor>>,
width: Property<'static, f32>,
height: Property<'static, f32>,
position: Property<'static, Pnt2>,
}
impl Debug for Scrollbar{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result{
fmt.debug_struct("Scrollbar")
.field("scroll_pct", &self.scroll_pct)
.field("width", &self.width)
.field("height", &self.height)
.field("position", &self.position)
.field("color", &self.color)
.finish()
}
}
pub struct ScrollbarBuilder{
content_height: Property<'static, f32>,
events: StreamRc<'static, window::Event>,
scroll_area: Property<'static, Rect<f64>>,
shape: ControlShape,
}
fn geometry(rect: Rect<f32>, content_height: f32, pct: f32, color: Rgb<f32>, alpha: f32) -> Vec<Vertex2DColor>{
let bg_alpha = 0.3;
let scroll_bg_color = &rgba!(color, bg_alpha);
let scroll_bar_color = &rgba!(color, alpha);
let w = rect.width;
let h = rect.height;
let x = rect.pos.x;
let y = rect.pos.y;
let scroll_h = h / content_height * h;
let scroll_y = y + (h - scroll_h) * pct;
vec![
vertex2dcolor(vec2(x, y), scroll_bg_color),
vertex2dcolor(vec2(x + w, y), scroll_bg_color),
vertex2dcolor(vec2(x + w, y + h), scroll_bg_color),
vertex2dcolor(vec2(x + w, y + h), scroll_bg_color),
vertex2dcolor(vec2(x, y + h), scroll_bg_color),
vertex2dcolor(vec2(x, y), scroll_bg_color),
vertex2dcolor(vec2(x, scroll_y), scroll_bar_color),
vertex2dcolor(vec2(x + w, scroll_y), scroll_bar_color),
vertex2dcolor(vec2(x + w, scroll_y + scroll_h), scroll_bar_color),
vertex2dcolor(vec2(x + w, scroll_y + scroll_h), scroll_bar_color),
vertex2dcolor(vec2(x, scroll_y + scroll_h), scroll_bar_color),
vertex2dcolor(vec2(x, scroll_y), scroll_bar_color),
]
}
impl ScrollbarBuilder{
pub fn new<S: StreamT<'static, window::Event>>(scroll_area: Property<'static, Rect<f64>>, events: S) -> ScrollbarBuilder{
let pos = Property::new(origin());
let width = Property::new(200.);
let height = Property::new(15.);
let content_height = height.clone();
let margin = 0.;
ScrollbarBuilder{
scroll_area,
content_height,
events: events.rc(),
shape: ControlShape::new(pos, width, height, margin, margin, WHITE),
}
}
pub fn content_height(mut self, h: Property<'static, f32>) -> ScrollbarBuilder{
self.content_height = h;
self
}
pub fn size(mut self, size: Vec2) -> ScrollbarBuilder{
self.shape.set_size(size);
self
}
pub fn create(self) -> Scrollbar{
let (rendered_sender, rendered_stream) = utils::rendered_stream(self.events.clone().unique());
let rendered = rendered_stream.to_property(false);
let events = self.events.clone()
.filter_by(rendered.clone())
.rc();
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 rect64 = rect.clone().map(|r| convert(r));
let over = events.clone().mouse().moved()
.map(move |pos| pos.inside(&rect64))
.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().rc();
let over_and_pressed = pressed_over.clone()
.to_bool()
.merge(released.clone())
.to_property(false);
let scroll_area = self.scroll_area;
let over_scroll_area = events.clone().mouse().moved()
.to_property(origin())
.zip(scroll_area.clone())
.map(move |(pos, scroll_area)| pos.inside(&scroll_area));
let scroll_pct = {
let over_and_pressed = over_and_pressed.clone();
let content_height = self.content_height.clone();
let rect = rect.clone();
#[derive(Clone, Debug, Copy)]
enum Event{
PressedOver(Pnt2<f64>),
Dragged(Pnt2<f64>),
Scrolled(f64),
}
let dragged = events.clone().mouse().moved().filter_by(over_and_pressed.clone());
let scrolled = events.clone().mouse().scrolled().filter_by(over_scroll_area.clone());
pressed_over.map(|(pressed_pos,_button)| Event::PressedOver(pressed_pos))
.merge(dragged.map(|pos| Event::Dragged(pos)))
.merge(scrolled.map(|scroll| Event::Scrolled(scroll.y)))
.scan((0., 0., 0.), move |pressed_pct, event|{
let pressed = &mut pressed_pct.0;
let pct = &mut pressed_pct.1;
let pressed_pct = &mut pressed_pct.2;
let scroll_speed = 10.;
let scroll_h = rect.height - rect.height / *content_height * rect.height;
match event{
Event::PressedOver(pos) => {
*pressed = pos.y;
*pressed_pct = *pct;
None
}
Event::Dragged(pos) => {
let diff_y = pos.y - *pressed;
let diff_pct = diff_y as f32 / scroll_h;
*pct = clamp(*pressed_pct + diff_pct, 0., 1.);
Some(*pct)
}
Event::Scrolled(diff_y) => {
let diff_pct = -diff_y as f32 / *content_height * scroll_speed;
*pct = clamp(*pct + diff_pct, 0., 1.);
Some(*pct)
}
}
}).to_property(0.)
};
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 if let window::Event::Scroll{..} = e {
!*over_scroll_area
} else {
true
}
});
let color = self.shape.color();
let content_height = self.content_height.clone();
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 geometry = {
let scroll_pct = scroll_pct.clone();
self.events.filter_by(rendered).update()
.map(move |_| (*scroll_pct, *alpha, *rect, *content_height))
.dedup()
.map(move |(pct, alpha, rect, content_height)|{
geometry(rect, content_height, pct, color, alpha)
}).to_property(vec![])
};
Scrollbar{
scroll_pct,
rendered: rendered_sender,
color,
non_attended: non_attended.rc(),
geometry,
width,
height,
position,
}
}
}
impl Scrollbar{
pub fn stream(&self) -> Stream<'static, f32>{
self.scroll_pct.clone().stream().unique()
}
pub fn scroll_pct(&self) -> Property<'static, f32>{
self.scroll_pct.clone()
}
}
impl ControlGeometry for Scrollbar{
fn flexible() -> bool{
false
}
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>> {
None
}
fn texture_geometry(&self, _container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DTex>>{
None
}
fn font(&self) -> Option<&Ttf> {
None
}
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 ControlEvents for Scrollbar{
fn non_attended(&self) -> StreamRc<'static, window::Event>{
self.non_attended.clone()
}
}
impl Control for Scrollbar{
fn name(&self) -> &str{
""
}
}
impl ControlBuilder for ScrollbarBuilder {
type Control = Scrollbar;
fn shape(&mut self) -> &mut ControlShape {
&mut self.shape
}
fn create(self) -> Scrollbar{
self.create()
}
}
#[cfg(any(feature = "gl", feature = "gles", feature="webgl"))]
mod gl{
use rin::gl::{self, Renderer2d};
use rin::graphics::*;
use traits::ControlGeometry;
use utils;
use rin::gl::RenderSurface;
use rin::math::{convert, Rect};
impl gl::Render3d for super::Scrollbar{
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);
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);
}
}
}
}