use rin::graphics::*;
use rin::events::*;
use rin::math::*;
use rin::window;
use rin::window::events::*;
use std::rc::Rc;
use traits::*;
use rin::color::*;
use rin::color::consts::*;
use control_shape::ControlShape;
use utils;
pub struct Button{
label: String,
_parameter: Property<'static, ()>,
font: Rc<Ttf>,
value: StreamRc<'static, ()>,
rendered: Sender<'static, ()>,
color: Rgb<f32>,
non_attended: StreamRc<'static, window::Event>,
geometry: Option<Property<'static, Vec<Vertex2DColor>>>,
text_geometry: Property<'static, Vec<Vertex2DTex>>,
texture_geometry: Option<Property<'static, Vec<Vertex2DTex>>>,
width: Property<'static, f32>,
height: Property<'static, f32>,
position: Property<'static, Pnt2>,
}
pub struct ButtonBuilder{
label: String,
parameter: Property<'static, ()>,
events: StreamRc<'static, window::Event>,
shape: ControlShape,
texture_enabled: Option<utils::TextureHandle>,
texture_hovered: Option<utils::TextureHandle>,
texture_pressed: Option<utils::TextureHandle>,
texture_hovered_pressed: Option<utils::TextureHandle>,
}
fn geometry(rect: Rect<f32>, toggle_color: &Rgba<f32>) -> Vec<Vertex2DColor>{
let x = rect.pos.x;
let y = rect.pos.y;
let w = rect.width;
let h = rect.height;
vec![
vertex2dcolor(vec2(x,y), toggle_color),
vertex2dcolor(vec2(x+w,y), toggle_color),
vertex2dcolor(vec2(x+w,y+h), toggle_color),
vertex2dcolor(vec2(x+w,y+h), toggle_color),
vertex2dcolor(vec2(x,y+h), toggle_color),
vertex2dcolor(vec2(x,y), toggle_color),
]
}
fn texture_geometry(rect: Rect<f32>, tex_handle: &utils::TextureHandle) -> Vec<Vertex2DTex>{
let x = rect.pos.x;
let y = rect.pos.y;
let w = rect.width;
let h = rect.height;
vec![
vertex2dtex(vec2(x,y), tex_handle.transform_uv(&vec2(0., 0.))),
vertex2dtex(vec2(x+w,y), tex_handle.transform_uv(&vec2(1., 0.))),
vertex2dtex(vec2(x+w,y+h), tex_handle.transform_uv(&vec2(1., 1.))),
vertex2dtex(vec2(x+w,y+h), tex_handle.transform_uv(&vec2(1., 1.))),
vertex2dtex(vec2(x,y+h), tex_handle.transform_uv(&vec2(0., 1.))),
vertex2dtex(vec2(x,y), tex_handle.transform_uv(&vec2(0., 0.))),
]
}
impl ButtonBuilder{
pub fn new<S: StreamT<'static, window::Event>>(name: &str, parameter: Property<'static, ()>, events: S) -> ButtonBuilder{
let pos = Property::new(origin());
let size = Property::new(15.);
let margin = 0.;
ButtonBuilder{
label: name.to_string(),
parameter: parameter,
events: events.rc(),
shape: ControlShape::new(pos, size.clone(), size, margin, margin, WHITE),
texture_enabled: None,
texture_hovered: None,
texture_pressed: None,
texture_hovered_pressed: None,
}
}
pub fn image_enabled(mut self, img: utils::TextureHandle) -> ButtonBuilder{
self.texture_enabled = Some(img);
self
}
pub fn image_hovered(mut self, img: utils::TextureHandle) -> ButtonBuilder{
self.texture_hovered = Some(img);
self
}
pub fn image_pressed(mut self, img: utils::TextureHandle) -> ButtonBuilder{
self.texture_pressed = Some(img);
self
}
pub fn image_hovered_pressed(mut self, img: utils::TextureHandle) -> ButtonBuilder{
self.texture_hovered_pressed = Some(img);
self
}
pub fn create(mut self) -> Button{
let (rendered_sender, rendered_stream) = utils::rendered_stream(self.events.clone().unique());
let rendered = rendered_stream.to_property(false);
let font = self.shape.font().unwrap_or_else(|| ::utils::default_font());
let side = self.shape.height();
let position = self.shape.position();
let rect = position.clone()
.zip(side.clone())
.map(|(pos, side)| Rect{pos, width: side, height: side});
let label_w = if self.shape.label_visible() {
font.bounding_box(&self.label, 0., 0., CoordinateOrigin::TopLeft).width
}else{
0.
};
let h_margin = self.shape.h_margin();
let full_rect = rect.clone().map(move |mut rect| {
rect.width += h_margin + label_w;
rect
});
let over = self.events.clone().mouse().moved()
.to_property(pnt2(0., 0.))
.zip(full_rect.clone().map(|rect| convert(rect)))
.zip(rendered.clone())
.flatten()
.map(move |(pos, rect, rendered)| rendered && pos.inside(&rect));
let full_width = full_rect.map(|r| r.width);
let value = {
let over = over.clone();
self.events.clone().mouse()
.is_pressed(MouseButton::Left)
.map(move |pressed| pressed && *over)
.to_property(false)
};
let over_copy = over.clone();
let non_attended = self.events.clone()
.filter(move |e| {
if let window::Event::MousePressed{..} = e {
!*over_copy
}else{
true
}
});
let filtered_update = self.events.filter_by(rendered).update().rc();
let color = self.shape.color();
let geometry = if self.texture_enabled.is_none() {
let rect = rect.clone();
let value = value.clone();
let over = over.clone();
Some(filtered_update.clone().map(move |_| {
let alpha = if *value && *over {
0.9
} else if *value || *over {
0.7
} else {
0.1
};
(alpha, *rect)
})
.dedup()
.map(move |(alpha, rect)| {
geometry(rect, &rgba!(color, alpha))
}).to_property(vec![]))
}else{
None
};
let texture_geometry = if let Some(texture_enabled) = self.texture_enabled {
let texture_hovered = self.texture_hovered.or(self.texture_enabled).unwrap();
let texture_pressed = self.texture_pressed.unwrap_or(texture_hovered);
let texture_hovered_pressed = self.texture_hovered_pressed.unwrap_or(texture_pressed);
let rect = rect.clone();
let value = value.clone();
Some(filtered_update.clone()
.map(move |_| {
let texture = if *value && *over {
texture_hovered_pressed
} else if *value {
texture_pressed
}else if *over {
texture_hovered
} else {
texture_enabled
};
(texture, *rect)
}).dedup()
.map(|(texture, rect)| texture_geometry(rect, &texture))
.to_property(vec![]))
}else{
None
};
let text_geometry = if self.shape.label_visible(){
let text = format!("{}", self.label);
let font = font.clone();
let line_height = font.line_height();
let descender = font.descender();
let em_height = font.bounding_box("M", 0., 0., CoordinateOrigin::TopLeft).height;
filtered_update.map(move |_| *rect).dedup()
.map(move |rect| font
.mesh(&text, rect.pos.x + 25., rect.pos.y + (rect.height + em_height) * 0.5, CoordinateOrigin::TopLeft)
.into())
.to_property(vec![])
}else{
Property::default()
};
let value = value.stream()
.filter(|v| *v)
.map(|_|())
.connect_to_property(&mut self.parameter)
.rc();
Button{
label: self.label.to_string(),
_parameter: self.parameter.clone(),
font,
value,
color: self.shape.color(),
rendered: rendered_sender,
non_attended: non_attended.rc(),
geometry: geometry,
text_geometry: text_geometry,
texture_geometry,
width: full_width,
height: side,
position,
}
}
}
impl ControlBuilder for ButtonBuilder{
type Control = Button;
fn shape(&mut self) -> &mut ControlShape{
&mut self.shape
}
fn create(self) -> Button{
self.create()
}
fn from_json_style(self, style: &str) -> Self where Self: Sized {
#[derive(Deserialize, Default)]
struct Des{
width: Option<f32>,
height: Option<f32>,
v_margin: Option<f32>,
h_margin: Option<f32>,
color: Option<Rgb<f32>>,
hide_label: Option<bool>,
image_enabled: Option<String>,
image_hovered: Option<String>,
image_pressed: Option<String>,
image_hovered_pressed: Option<String>,
image_enabled_handle: Option<utils::TextureHandle>,
image_hovered_handle: Option<utils::TextureHandle>,
image_pressed_handle: Option<utils::TextureHandle>,
image_hovered_pressed_handle: Option<utils::TextureHandle>,
}
let des: Des = serde_json::from_str(style).unwrap();
let builder = self.width_opt(des.width)
.height_opt(des.height)
.v_margin_opt(des.v_margin)
.h_margin_opt(des.h_margin)
.color_opt(des.color)
.hide_label_opt(des.hide_label);
let builder = if let Some(img) = des.image_enabled {
let img = utils::load_texture(img).unwrap();
builder.image_enabled(img)
}else if let Some(img) = des.image_enabled_handle{
builder.image_enabled(img)
}else{
builder
};
let builder = if let Some(img) = des.image_pressed {
let img = utils::load_texture(img).unwrap();
builder.image_pressed(img)
}else if let Some(img) = des.image_pressed_handle{
builder.image_pressed(img)
}else{
builder
};
let builder = if let Some(img) = des.image_hovered {
let img = utils::load_texture(img).unwrap();
builder.image_hovered(img)
}else if let Some(img) = des.image_hovered_handle{
builder.image_hovered(img)
}else{
builder
};
if let Some(img) = des.image_hovered_pressed {
let img = utils::load_texture(img).unwrap();
builder.image_hovered_pressed(img)
}else if let Some(img) = des.image_hovered_pressed_handle{
builder.image_hovered_pressed(img)
}else{
builder
}
}
}
impl ControlBuilt<Property<'static, ()>> for Button{
type Builder = ButtonBuilder;
}
impl ControlDefaultProperty for Property<'static, ()>{
type Builder = ButtonBuilder;
}
impl BuilderFromProperty<Property<'static, ()>> for ButtonBuilder{
fn from_parameter<S: StreamT<'static, window::Event>>(parameter: Property<'static, ()>, name: &str, events: S) -> ButtonBuilder{
ButtonBuilder::new(name, parameter, events)
}
}
impl Button{
pub fn stream(&self) -> StreamRc<'static, ()>{
self.value.clone()
}
}
impl ControlGeometry for Button{
fn flexible() -> bool{
false
}
fn geometry(&self, _container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DColor>>{
self.rendered.send(());
self.geometry.as_ref().map(|g| g.to_value())
}
fn text_geometry(&self, _container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DTex>>{
Some((*self.text_geometry).clone())
}
fn texture_geometry(&self, _container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DTex>>{
self.texture_geometry.as_ref().map(|g| g.to_value())
}
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 ControlEvents for Button{
fn non_attended(&self) -> StreamRc<'static, window::Event>{
self.non_attended.clone()
}
}
impl Control for Button{
fn name(&self) -> &str{
&self.label
}
}
#[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::Button{
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(|m| mesh(m, 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(geometry) = self.texture_geometry(viewport).map(|m| mesh(m, 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),
]);
utils::gl::with_texture_atlas(&gl, |texture|{
let material = gl::basic_material::Builder::new()
.texture(texture.unwrap())
.create();
gl.draw_mesh_with_material(&geometry, &material);
})
}
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);
}
}
}
}