use traits::*;
use group_v::*;
use control_shape::ControlShape;
use rin::graphics::*;
use rin::events::*;
use rin::math::*;
use rin::color::*;
use rin::color::consts::*;
use rin::window;
use rin::window::events::*;
use std::rc::Rc;
use parameter_group::ParameterGroup;
use utils;
pub struct Tabs{
name: String,
font: Rc<Ttf>,
group_names: Property<'static, Vec<String>>,
geometry: Property<'static, Vec<Vertex2DColor>>,
text_geometry: Property<'static, Vec<Vertex2DTex>>,
groups: Vec<GroupV>,
color: Rgb<f32>,
active_group: Property<'static, usize>,
non_attended_events: StreamRc<'static, window::Event>,
groups_events: StreamRc<'static, window::Event>,
content_y: f32,
h_margin: f32,
v_margin: f32,
height_streams: Vec<Stream<'static, f32>>,
rendered_sender: Sender<'static, ()>,
width: Property<'static, f32>,
height: Property<'static, f32>,
position: Property<'static, Pnt2>,
max_height: Property<'static, Option<f32>>,
}
fn geometry(group_names: &[String], active: usize, hoover: Option<usize>, pos: &Pnt2, w: f32, line_height: f32, margin: f32, color: Rgb<f32>) -> Vec<Vertex2DColor>{
let tab_color = &rgba!(WHITE.to_rgb() - color, 0.3);
let hoover_color = &rgba!(WHITE.to_rgb() - color, 0.5);
let active_color = &rgba!(WHITE.to_rgb() - color, 0.7);
let bg_color = &rgba!(color, 0.5);
let tab_w = w / group_names.len() as f32;
let tab_h = line_height * 1.2 + margin;
let tabs: Vec<Vertex2DColor> = group_names.iter().enumerate().flat_map(|(idx, _name)|{
let color = if idx == active{
active_color
}else if hoover.is_some() && hoover.unwrap() == idx{
hoover_color
}else{
tab_color
};
let pos = pos + vec2(tab_w * idx as f32, 0.);
let radius = 5.;
let resolution = 7;
let center = pos + vec2(radius, radius);
let left_corner: Vec<Vec2<f32>> = arc_iter(center.to_vec(), radius, radius, Deg(90.), Deg(90.), resolution).collect();
let left_corner = left_corner.windows(2)
.flat_map(|arc_v|{
vec![
vertex2dcolor(arc_v[0], color),
vertex2dcolor(center.to_vec(), color),
vertex2dcolor(arc_v[1], color),
]
});
let center = pos + vec2(tab_w-radius, radius);
let right_corner: Vec<Vec2<f32>> = arc_iter(center.to_vec(), radius, radius, Deg(0.), Deg(90.), resolution).collect();
let right_corner = right_corner.windows(2)
.flat_map(|arc_v|{
vec![
vertex2dcolor(arc_v[0], color),
vertex2dcolor(center.to_vec(), color),
vertex2dcolor(arc_v[1], color),
]
});
let pos_down = (pos + vec2(0., radius)).to_vec();
let pos_up = (pos + vec2(radius, 0.)).to_vec();
let mut quad = vec![
vertex2dcolor(pos_down, color),
vertex2dcolor(pos_down + vec2(tab_w, 0.), color),
vertex2dcolor(pos_down + vec2(tab_w, tab_h - radius), color),
vertex2dcolor(pos_down + vec2(tab_w, tab_h - radius), color),
vertex2dcolor(pos_down + vec2(0., tab_h - radius), color),
vertex2dcolor(pos_down, color),
vertex2dcolor(pos_up, color),
vertex2dcolor(pos_up + vec2(tab_w - radius*2., 0.), color),
vertex2dcolor(pos_up + vec2(tab_w - radius*2., radius), color),
vertex2dcolor(pos_up + vec2(tab_w - radius*2., radius), color),
vertex2dcolor(pos_up + vec2(0., radius), color),
vertex2dcolor(pos_up, color),
];
quad.extend(left_corner);
quad.extend(right_corner);
quad
}).collect();
tabs.iter()
.map(|v| vertex2dcolor(v.position, bg_color))
.chain(tabs.iter().map(|v| *v))
.collect()
}
fn text_geometry(font: &Ttf, group_names: &[String], _active: usize, pos: &Pnt2, w: f32, line_height: f32, h_margin: f32, v_margin: f32) -> Vec<Vertex2DTex>{
let x = pos.x;
let y = pos.y;
let tab_w = w / group_names.len() as f32;
let tab_h = line_height * 1.2 + v_margin;
group_names.iter().enumerate().flat_map(|(idx, name)|{
let text = utils::shorten(&name, font, tab_w - h_margin*2.);
let width = font.bounding_box(&text, 0., 0., CoordinateOrigin::TopLeft).width;
let pos = vec2(x + tab_w * idx as f32 + (tab_w - width) / 2., y + tab_h / 2. + line_height / 3.);
font.mesh(&text, pos.x, pos.y, CoordinateOrigin::TopLeft).into_iter()
}).collect()
}
pub struct TabsBuilder{
name: String,
shape: ControlShape,
max_height: Property<'static, Option<f32>>,
color: Rgb<f32>,
events: StreamRc<'static, window::Event>,
font: Option<Rc<Ttf>>,
}
impl TabsBuilder{
pub fn new<S: StreamT<'static, window::Event>>(name: &str, pos: Property<'static, Pnt2>, events: S) -> TabsBuilder{
let width = Property::new(200.);
let height = Property::new(0.);
let margin = 10.;
TabsBuilder{
name: name.to_string(),
shape: ControlShape::new(pos, width, height, margin, margin, WHITE),
max_height: Property::new(None),
color: WHITE.to_rgb(),
events: events.rc(),
font: None,
}
}
pub fn max_height(mut self, height: Property<'static, Option<f32>>) -> TabsBuilder{
self.max_height = height;
self
}
}
impl ControlBuilder for TabsBuilder{
type Control = Tabs;
fn shape(&mut self) -> &mut ControlShape {
&mut self.shape
}
fn create(self) -> Tabs{
let font = self.font.clone().unwrap_or_else(|| ::utils::default_font());
let (rendered_sender, rendered_stream) = utils::rendered_stream(self.events.clone().unique());
let rendered = rendered_stream.to_property(false);
let line_height = font.line_height();
let h_margin = self.shape.h_margin();
let v_margin = self.shape.v_margin();
let color = self.color;
let tab_h = line_height * 1.2 + v_margin;
let position = self.shape.position();
let width = self.shape.width();
let height = Property::new(tab_h);
let rect: Property<'_, Rect<f64>> = position.clone()
.zip(width.clone())
.zip(height.clone())
.flatten()
.map(|(pos, width, height)| convert(Rect{pos, width, height}));
let group_names = Property::new(vec![]);
let pressed = self.events.clone().mouse().pressed(window::MouseButton::Left).rc();
let released = self.events.clone().mouse().released(window::MouseButton::Left);
let is_over = {
let rect = rect.clone();
self.events.clone().mouse().moved()
.filter_by(rendered.clone())
.map(move |m| m.inside(&rect))
.rc()
};
let over_and_pressed = is_over.clone()
.and_then(pressed.clone())
.until(released)
.dedup();
let group_names_copy = group_names.clone();
let hoovered_tab = {
let is_over = is_over.clone().to_property(false);
let control_pos = position.clone();
let width = width.clone();
self.events.clone()
.mouse()
.moved()
.map(move |mouse_pos|{
if *is_over && !group_names_copy.is_empty(){
let num_groups = group_names_copy.len();
let tab_w = *width / num_groups as f32;
(*group_names_copy).iter()
.enumerate()
.position(|(idx, _name)| {
let tab_pos = *control_pos + vec2(idx as f32 * tab_w, 0.);
let tab_rect = Rect{pos: tab_pos, width: tab_w, height: tab_h};
mouse_pos.inside(&convert(tab_rect))
})
}else{
None
}
})
.dedup()
.to_property(None)
};
let hoovered_tab_cpy = hoovered_tab.clone();
let active_group = self.events.clone()
.mouse()
.pressed(window::MouseButton::Left)
.filter_map(move |_| hoovered_tab_cpy.to_value())
.to_property(0);
let filtered_update = self.events.clone().filter_by(rendered).update().rc();
let geometry = {
let group_names = group_names.clone();
let active_group = active_group.clone();
let position = position.clone();
let width = width.clone();
filtered_update.clone()
.map(move |_| ((*group_names).clone(), *active_group, *hoovered_tab, *position, *width))
.dedup()
.map(move |(group_names, active_group, hoovered_tab, control_pos, width)|{
geometry(&group_names, active_group, hoovered_tab, &control_pos, width, line_height, v_margin, color)
}).to_property(vec![])
};
let text_geometry = {
let font = font.clone();
let group_names = group_names.clone();
let active_group = active_group.clone();
let position = position.clone();
let width = width.clone();
filtered_update
.map(move |_| ((*group_names).clone(), *active_group, *position, *width))
.dedup()
.map(move |(group_names, active_group, control_pos, width)|{
text_geometry(&font, &group_names, active_group, &control_pos, width, line_height, h_margin, v_margin)
}).to_property(vec![])
};
let is_over = is_over.to_property(false);
let non_attended_events = self.events.clone()
.filter_by(over_and_pressed.not().to_property(true))
.filter(move |e| if let window::Event::Scroll{..} = e { !*is_over } else { true })
.rc();
let groups_events = self.events.clone().rc();
Tabs{
name: self.name,
font: font,
geometry: geometry,
text_geometry: text_geometry,
group_names: group_names,
groups: vec![],
color: color,
max_height: self.max_height,
active_group: active_group,
non_attended_events,
groups_events,
content_y: tab_h,
h_margin,
v_margin,
height_streams: vec![],
rendered_sender,
width,
height,
position,
}
}
}
impl Tabs{
pub fn new<S: StreamT<'static, window::Event>>(pos: Pnt2, width: f32, events: S) -> Tabs{
TabsBuilder::new("", Property::new(pos), events)
.width(width)
.create()
}
fn _new_group(&mut self, name: &str, foldable: bool) -> &mut GroupV{
let group_id = self.groups.len();
let active_group = self.active_group.clone().map(move |active| active == group_id);
let events = self.groups_events.clone().filter_by(active_group.clone());
let group_y = self.content_y;
let group_pos = self.position.clone().map(move |pos| pos + vec2(0., group_y));
let group = GroupBuilder::new(name, group_pos, events)
.width_property(self.width.clone())
.max_height(self.max_height.clone().map(move |h| h.map(|h| h - group_y)))
.font(self.font.clone())
.color(self.color)
.h_margin(self.h_margin)
.v_margin(self.v_margin);
let group = if foldable {
group.foldable().title(name.to_owned()).create()
}else{
group.create()
};
let content_y = self.content_y;
let group_height = group.height()
.clone()
.map(move |h| h + content_y);
let group_height_cpy = group_height.clone();
let current_height = active_group
.clone()
.stream()
.filter(|g| *g)
.map(move |_| *group_height_cpy)
.merge(group_height.stream().filter_by(active_group))
.connect_to_property(&mut self.height);
self.height_streams.push(current_height);
self.groups.push(group);
let group_names = self.groups.iter().map(|group| group.name().to_string()).collect();
self.group_names.set(group_names);
self.groups.last_mut().unwrap()
}
pub fn new_group(&mut self, name: &str) -> &mut GroupV{
self._new_group(name, false)
}
pub fn new_foldable_group(&mut self, name: &str) -> &mut GroupV{
self._new_group(name, true)
}
pub fn group_from_parameters<PG: ParameterGroup>(&mut self, name: &str, parameters: &PG) -> &mut GroupV{
let group = self.new_group(name);
parameters.add_parameters(group);
group
}
pub fn foldable_group_from_parameters<PG: ParameterGroup>(&mut self, name: &str, parameters: &PG) -> &mut GroupV{
let group = self.new_foldable_group(name);
parameters.add_parameters(group);
group
}
pub fn group(&mut self, name: &str) -> Option<&GroupV>{
self.groups.iter().find(|group| group.name() == name)
}
pub fn group_mut(&mut self, name: &str) -> Option<&mut GroupV>{
self.groups.iter_mut().find(|group| group.name() == name)
}
pub fn active_group(&mut self) -> Option<&GroupV>{
self.groups.get(*self.active_group)
}
pub fn active_group_mut(&mut self) -> Option<&mut GroupV>{
self.groups.get_mut(*self.active_group)
}
pub fn set_position(&mut self, pos: &Pnt2) {
self.position.set(*pos)
}
pub fn set_max_height(&mut self, height: f32) {
if Some(height) != *self.max_height{
self.max_height.set(Some(height));
}
}
pub fn set_max_width(&mut self, width: f32){
if width != *self.width{
self.width.set(width);
}
}
pub fn max_height(&self) -> f32{
self.max_height.unwrap_or(*self.height)
}
pub fn max_width(&self) -> f32{
*self.width
}
}
impl ControlGeometry for Tabs{
fn flexible() -> bool{
true
}
fn geometry(&self, container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DColor>>{
self.rendered_sender.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 ControlEvents for Tabs{
fn non_attended(&self) -> StreamRc<'static, window::Event>{
self.non_attended_events.clone()
}
}
impl Control for Tabs{
fn name(&self) -> &str{
&self.name
}
}
#[cfg(any(feature = "gl", feature = "gles", feature="webgl"))]
mod gl{
use rin::gl::{self, Renderer2d, Renderer3d};
use rin::graphics::*;
use traits::ControlGeometry;
use utils;
use rin::gl::RenderSurface;
use rin::math::{convert, Rect};
impl gl::Render3d for super::Tabs{
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);
}
if *self.active_group < self.groups.len(){
gl.draw(&self.groups[*self.active_group]);
}
}
}
}