use traits::*;
use rin::graphics::*;
use rin::events::*;
use rin::math::*;
use rin::color::*;
use rin::color::consts::*;
use rin::window;
use rin::window::events::*;
use scrollbar::{Scrollbar, ScrollbarBuilder};
use std::rc::Rc;
use control_shape::ControlShape;
use utils;
use std::mem;
use fxhash::FxHashMap as HashMap;
pub struct GroupV{
controls: Vec<Box<Control>>,
name: String,
font: Rc<Ttf>,
color: Rgb<f32>,
non_attended: StreamRc<'static, window::Event>,
events: StreamRc<'static, window::Event>,
over: Property<'static, bool>,
geometry: Property<'static, Vec<Vertex2DColor>>,
text_geometry: Property<'static, Vec<Vertex2DTex>>,
rendered: Sender<'static, ()>,
scrollbar: Property<'static, Option<Rc<Scrollbar>>>,
has_scrollbar: Property<'static, bool>,
folded: Property<'static, bool>,
fold: Sender<'static, bool>,
width: Property<'static, f32>,
height: Property<'static, f32>,
position: Property<'static, Pnt2>,
scroll_pos: Property<'static, Pnt2>,
added_controls_height: Property<'static, f32>,
controls_height: Property<'static, f32>,
max_height: Property<'static, Option<f32>>,
controls_width: Property<'static, f32>,
left_margin: f32,
clamped_height: Property<'static, f32>,
h_margin: f32,
v_margin: f32,
controls_index: HashMap<String, usize>,
visible_labels: bool,
}
fn geometry(pos: Pnt2, w: f32, h: f32, color: Rgb<f32>, folded: Option<bool>, transparent: bool, bar_h: f32, has_title: bool) -> Vec<Vertex2DColor>{
let x = pos.x;
let y = pos.y;
let alpha = 0.7;
let bg_color = &rgba!(rgb!(1.0, 1.0, 1.0) - color, alpha);
let bg_bg_color = &rgba!(color, 0.5);
let bar_color = &rgba!(color, alpha);
let arrow = if let Some(folded) = folded {
if folded {
vec![
vertex2dcolor(vec2(x + 8., y + 8.), &color),
vertex2dcolor(vec2(x + 16., y + 12.), &color),
vertex2dcolor(vec2(x + 8., y + 16.), &color),
]
}else{
vec![
vertex2dcolor(vec2(x + 8., y + 8.), &color),
vertex2dcolor(vec2(x + 12., y + 16.), &color),
vertex2dcolor(vec2(x + 16., y + 8.), &color),
]
}
}else{
vec![]
};
let bar = if has_title {
vec![
vertex2dcolor(vec2(x,y), bar_color),
vertex2dcolor(vec2(x + w,y), bar_color),
vertex2dcolor(vec2(x + w,y+bar_h), bar_color),
vertex2dcolor(vec2(x + w,y+bar_h), bar_color),
vertex2dcolor(vec2(x,y+bar_h), bar_color),
vertex2dcolor(vec2(x,y), bar_color),
vertex2dcolor(vec2(x,y), bg_color),
vertex2dcolor(vec2(x + w,y), bg_color),
vertex2dcolor(vec2(x + w,y+bar_h), bg_color),
vertex2dcolor(vec2(x + w,y+bar_h), bg_color),
vertex2dcolor(vec2(x,y+bar_h), bg_color),
vertex2dcolor(vec2(x,y), bg_color),
]
}else{
vec![]
};
let panel = if folded.unwrap_or(false) || transparent {
vec![]
}else{
vec![
vertex2dcolor(vec2(x,y), bg_bg_color),
vertex2dcolor(vec2(x+w,y), bg_bg_color),
vertex2dcolor(vec2(x+w,y+h), bg_bg_color),
vertex2dcolor(vec2(x+w,y+h), bg_bg_color),
vertex2dcolor(vec2(x,y+h), bg_bg_color),
vertex2dcolor(vec2(x,y), bg_bg_color),
vertex2dcolor(vec2(x,y), bg_color),
vertex2dcolor(vec2(x+w,y), bg_color),
vertex2dcolor(vec2(x+w,y+h), bg_color),
vertex2dcolor(vec2(x+w,y+h), bg_color),
vertex2dcolor(vec2(x,y+h), bg_color),
vertex2dcolor(vec2(x,y), bg_color),
]
};
panel.into_iter().chain(bar).chain(arrow).collect()
}
pub enum Overflow{
Auto,
Invisible,
}
pub struct GroupBuilder{
name: String,
events: StreamRc<'static, window::Event>,
max_height: Property<'static, Option<f32>>,
shape: ControlShape,
foldable: bool,
transparent: bool,
title: Option<String>,
overflow: Overflow,
right_margin: bool,
}
impl GroupBuilder{
pub fn new<S: StreamT<'static, window::Event>>(name: &str, pos: Property<'static, Pnt2>, events: S) -> GroupBuilder{
let width = Property::new(200.);
let height = Property::new(0.);
let margin = 10.;
GroupBuilder{
name: name.to_string(),
max_height: Property::new(None),
events: events.rc(),
shape: ControlShape::new(pos, width, height, margin, margin, WHITE),
foldable: false,
transparent: false,
title: None,
overflow: Overflow::Auto,
right_margin: true,
}
}
pub(crate) fn from_control_shape<S: StreamT<'static, window::Event>>(name: &str, shape: ControlShape, events: S) -> GroupBuilder{
GroupBuilder{
name: name.to_string(),
max_height: Property::new(None),
events: events.rc(),
shape,
foldable: false,
transparent: false,
title: None,
overflow: Overflow::Auto,
right_margin: true,
}
}
pub fn max_height(mut self, height: Property<'static, Option<f32>>) -> GroupBuilder{
self.max_height = height;
self
}
pub fn foldable(mut self) -> GroupBuilder{
self.foldable = true;
self
}
pub fn transparent(mut self) -> GroupBuilder{
self.transparent = true;
self
}
pub fn title(mut self, title: String) -> GroupBuilder{
self.title = Some(title);
self
}
pub fn overflow(mut self, overflow: Overflow) -> GroupBuilder{
self.overflow = overflow;
self
}
pub fn no_right_margin(mut self) -> GroupBuilder{
self.right_margin = false;
self
}
}
impl ControlBuilder for GroupBuilder {
type Control = GroupV;
fn shape(&mut self) -> &mut ControlShape {
&mut self.shape
}
fn create(self) -> GroupV{
let (rendered_sender, rendered_stream) = utils::rendered_stream(self.events.clone().unique());
let rendered = rendered_stream.to_property(false);
let width = self.shape.width();
let color = self.shape.color();
let position = self.shape.position();
let font = self.shape.font().unwrap_or_else(|| ::utils::default_font());
let line_height = font.line_height();
let bar_height = if self.foldable {
line_height * 1.5
}else{
0.
};
let bar_rect = position.clone()
.zip(width.clone())
.map(move |(pos, width)| convert(Rect{pos, width, height: bar_height}));
let (fold_tx, fold_rx) = stream();
let folded = if self.foldable{
#[derive(Debug, Clone, Copy)]
enum Fold{
Mouse,
Call(bool),
}
self.events.clone()
.filter_by(rendered.clone())
.mouse()
.pressed(MouseButton::Left)
.filter(move |(pos, _)| pos.inside(&bar_rect))
.map(|_| Fold::Mouse)
.merge(fold_rx.map(|b| Fold::Call(b)))
.fold(false, |folded, fold| {
match fold{
Fold::Mouse => !folded,
Fold::Call(fold) => fold,
}
})
.map(|f| Some(f))
.to_property(Some(false))
}else{
Property::new(None)
};
let controls_height = Property::new(0.);
let v_margin = self.shape.v_margin();
let h_margin = self.shape.h_margin();
let height = controls_height.clone()
.zip(folded.clone())
.map(move |(controls_height, folded)| if folded.unwrap_or(false) {
bar_height
}else{
bar_height + controls_height
});
let clamped_max_height = self.max_height.clone()
.zip(height.clone())
.map(move |(max_height, height)| max_height.map(|max_height| max_height.min(height + v_margin)));
let scroll_area: Property<'static, Rect<f64>> = position.clone()
.zip(width.clone())
.zip(height.clone())
.flatten()
.map(|(pos, width, height)| convert(Rect{pos, width, height}));
let is_over = {
let scroll_area = scroll_area.clone();
self.events.clone().mouse().moved()
.to_property(origin())
.zip(scroll_area)
.zip(rendered.clone())
.flatten()
.map(move |(pos, scroll_area, rendered)| rendered && pos.inside(&scroll_area))
};
let scrollbar_width = width.clone().map(|width| (width * 0.05).min(10.));
let has_scrollbar = self.max_height.clone()
.zip(height.clone())
.map(move |(max_height, height)|{
if let Some(max_height) = max_height {
max_height < height + v_margin
}else{
false
}
});
let left_margin = h_margin;
let do_right_margin = self.right_margin;
let right_margin = Property::new(left_margin)
.zip(has_scrollbar.clone())
.zip(scrollbar_width.clone())
.flatten()
.map(move |(margin, has_scrollbar, scrollbar_width)|{
if has_scrollbar {
margin + scrollbar_width
}else if do_right_margin{
margin
}else{
0.
}
});
let controls_width = width.clone()
.zip(right_margin.clone())
.map(move |(width, right_margin)| width - left_margin - right_margin);
let clamped_height = self.max_height.clone()
.zip(height.clone())
.map(|(max_height, height)| max_height.unwrap_or(height));
let filtered_update = self.events.clone()
.filter_by(rendered.clone())
.update()
.rc();
let scrollbar;
let scroll_pos;
if let Overflow::Auto = self.overflow {
scrollbar = {
let color = self.shape.color();
let events = self.events.clone().map(|e| e).rc();
let position = position.clone();
let height = height.clone();
let width = width.clone();
let clamped_max_height = clamped_max_height.clone();
let has_scrollbar = has_scrollbar.clone();
filtered_update.clone()
.map(move |_| *has_scrollbar)
.dedup()
.scan(None, move |scrollbar, has_scrollbar|{
if has_scrollbar && scrollbar.is_none(){
let scrollbar_height = clamped_max_height.clone().map(|h| h.unwrap_or(0.));
let scrollbar_position = position.clone()
.zip(width.clone())
.zip(scrollbar_width.clone())
.flatten()
.map(move |(pos, width, scroll_width)| pnt2(pos.x + width - scroll_width, pos.y));
let active_area = position.clone()
.zip(width.clone())
.zip(scrollbar_height.clone())
.flatten()
.map(|(pos, width, height)| Rect{
pos: convert(pos),
width: convert(width),
height: convert(height),
});
let new_scrollbar = ScrollbarBuilder::new(active_area, events.clone())
.position_property(scrollbar_position)
.width_property(scrollbar_width.clone())
.height_property(scrollbar_height)
.content_height(height.clone())
.color(color)
.create();
*scrollbar = Some(Rc::new(new_scrollbar));
Some(scrollbar.clone())
}else{
Some(scrollbar.clone())
}
}).to_property(None)
};
let scrolled_pos = {
let position = position.clone();
let height = height.clone();
scrollbar.clone().stream()
.dedup_by(|sc| sc.is_some())
.flat_map(move |scrollbar| if let Some(scrollbar) = scrollbar{
position.clone()
.zip(height.clone())
.zip(scrollbar.scroll_pct())
.flatten()
.zip(clamped_max_height.clone())
.map(move |((mut pos, height, scroll_pct), max_height)| {
let height = height + v_margin;
if let Some(max_height) = max_height{
pos.y -= (height - max_height) * scroll_pct;
}
pos
}).stream().unique()
}else{
position.clone().stream().unique()
})
};
let scroll_y = scrolled_pos.map(move |pos| pos.y + bar_height).to_property(position.y + bar_height);
let scroll_x = position.clone().map(|pos| pos.x);
let scrolled_pos = scroll_x.zip(scroll_y).map(|(x,y)| pnt2(x, y));
scroll_pos = filtered_update.clone().map(move |_| *scrolled_pos)
.throttle(30.)
.dedup()
.to_property(*position);
}else{
scrollbar = Property::new(None);
scroll_pos = position.clone();
};
let has_title = self.title.is_some();
let geometry = {
let position = position.clone();
let width = width.clone();
let height = height.clone();
let folded = folded.clone();
let transparent = self.transparent;
filtered_update.clone()
.map(move |_| (*position, *width, *height, *folded))
.dedup()
.map(move |(pos, width, height, folded)| {
geometry(pos, width, height + v_margin * 2., color, folded, transparent, bar_height, has_title)
})
.to_property(vec![])
};
let text_geometry = {
let font = font.clone();
let position = position.clone();
let xbb = font.bounding_box("x", 0., 0., CoordinateOrigin::TopLeft);
let x_height = xbb.height;
if let Some(text) = self.title {
let shorted_text = {
let font = font.clone();
let width = width.clone();
let total_width = font.bounding_box(&text, 0., 0., CoordinateOrigin::TopLeft).width;
filtered_update.clone()
.map(move |_| *width)
.dedup()
.map(move |w| if total_width < w - 25. {
text.clone()
}else{
utils::shorten(&text, &font, w - 25.).to_owned()
}).to_property(String::new())
};
filtered_update
.map(move |_| (*position, (*shorted_text).clone()))
.dedup()
.map(move |(pos, text)| {
font.mesh(&text,
pos.x + 25.,
pos.y + (bar_height + x_height) * 0.5 + 1.,
CoordinateOrigin::TopLeft)
.into()
})
.to_property(vec![])
}else{
Property::new(vec![])
}
};
let over = is_over.clone();
let non_attended = self.events.clone().filter(move |e|{
if let Event::MousePressed{..} = e {
!*rendered || !*over
}else{
true
}
}).rc();
GroupV{
controls: vec![],
name: self.name.to_string(),
added_controls_height: Property::new(0.),
scroll_pos,
position,
width,
controls_height,
clamped_height,
max_height: self.max_height,
height,
left_margin,
h_margin,
v_margin,
controls_width,
font: font,
color: self.shape.color(),
non_attended,
geometry,
text_geometry,
over: is_over,
rendered: rendered_sender,
scrollbar,
has_scrollbar,
folded: folded.map(|f| f.unwrap_or(false)),
fold: fold_tx,
controls_index: HashMap::default(),
events: self.events,
visible_labels: self.shape.label_visible(),
}
}
}
impl GroupV{
pub fn add<P,S>(&mut self, name: &str, parameter: S) -> &mut GroupV
where S: IntoStyled<Property = P>,
P: ControlDefaultProperty,
{
self.new_control(name, parameter);
self
}
pub fn add_as<C, P>(&mut self, name: &str, parameter: P) -> &mut GroupV
where C: ControlBuilt<P>,
<C as ControlBuilt<P>>::Builder: BuilderFromProperty<P>,
{
self.new_control_as::<C, P>(name, parameter);
self
}
pub fn new_control<S, P>(&mut self, name: &str, parameter: S) -> &mut <<P as ControlDefaultProperty>::Builder as ControlBuilder>::Control
where S: IntoStyled<Property = P>,
P: ControlDefaultProperty,
{
self.create_control::<P, P::Builder>(name, parameter.into_styled())
}
pub fn new_control_as<C, P>(&mut self, name: &str, parameter: P) -> &mut C
where C: ControlBuilt<P>,
<C as ControlBuilt<P>>::Builder: BuilderFromProperty<P>,
{
self.create_control::<P, C::Builder>(name, styled(parameter, "{}"))
}
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 fold(&mut self){
self.fold.send(true)
}
pub fn unfold(&mut self){
self.fold.send(false)
}
pub fn is_folded(&self) -> bool {
*self.folded
}
fn create_control<P, B>(&mut self, name: &str, styled: Styled<P>) -> &mut B::Control
where P: IntoControlBuilder<B>,
B: ControlBuilder
{
let previous_controls_height = mem::replace(&mut self.added_controls_height, Property::new(0.));
let v_margin = self.v_margin;
let left_margin = self.left_margin;
let pos = self.scroll_pos.clone()
.zip(previous_controls_height.clone())
.map(move |(pos, prev_h)| pnt2(pos.x + left_margin, pos.y + prev_h + v_margin));
let control = styled.parameter.into_builder(name, self.events.clone())
.position_property(pos)
.width_property(self.controls_width.clone())
.h_margin(self.h_margin)
.v_margin(self.v_margin)
.color(self.color)
.font(self.font.clone())
.hide_label_opt(Some(!self.visible_labels))
.from_json_style(&styled.style)
.create();
self.added_controls_height = previous_controls_height
.zip(control.height().clone())
.map(move |(prev, h)| prev + h + v_margin);
let controls_height_sender = self.added_controls_height.clone().stream();
self.controls_height.swap_parent(controls_height_sender);
self.controls_height.set(*self.added_controls_height);
self.controls_index.insert(name.to_owned(), self.controls.len());
self.controls.push(Box::new(control));
self.controls.last_mut().unwrap().downcast_mut().unwrap()
}
pub fn new_group(&mut self, name: &str) -> &mut GroupV{
let previous_controls_height = mem::replace(&mut self.added_controls_height, Property::new(0.));
let v_margin = self.v_margin;
let left_margin = self.left_margin;
let pos = self.scroll_pos.clone()
.zip(previous_controls_height.clone())
.map(move |(pos, prev_h)| pnt2(pos.x + left_margin, pos.y + prev_h + v_margin * 2.));
let control = GroupBuilder::new(name, pos, self.events.clone())
.width_property(self.controls_width.clone())
.h_margin(self.h_margin)
.v_margin(self.v_margin)
.color(self.color)
.font(self.font.clone())
.create();
self.added_controls_height = previous_controls_height
.zip(control.height().clone())
.map(move |(prev, h)| prev + h + v_margin * 2.);
let controls_height_sender = self.added_controls_height.clone().stream();
self.controls_height.swap_parent(controls_height_sender);
self.controls_height.set(*self.added_controls_height);
self.controls_index.insert(name.to_owned(), self.controls.len());
self.controls.push(Box::new(control));
self.controls.last_mut().unwrap().downcast_mut().unwrap()
}
pub fn new_foldable_group(&mut self, name: &str, title: String) -> &mut GroupV{
let previous_controls_height = mem::replace(&mut self.added_controls_height, Property::new(0.));
let v_margin = self.v_margin;
let left_margin = self.left_margin;
let pos = self.scroll_pos.clone()
.zip(previous_controls_height.clone())
.map(move |(pos, prev_h)| pnt2(pos.x + left_margin, pos.y + prev_h + v_margin * 2.));
let control = GroupBuilder::new(name, pos, self.events.clone())
.width_property(self.controls_width.clone())
.h_margin(self.h_margin)
.v_margin(self.v_margin)
.color(self.color)
.font(self.font.clone())
.foldable()
.no_right_margin()
.title(title)
.create();
self.added_controls_height = previous_controls_height
.zip(control.height().clone())
.map(move |(prev, h)| prev + h + v_margin * 2.);
let controls_height_sender = self.added_controls_height.clone().stream();
self.controls_height.swap_parent(controls_height_sender);
self.controls_height.set(*self.added_controls_height);
self.controls_index.insert(name.to_owned(), self.controls.len());
self.controls.push(Box::new(control));
self.controls.last_mut().unwrap().downcast_mut().unwrap()
}
pub fn new_group_title(&mut self, name: &str, title: String) -> &mut GroupV{
let previous_controls_height = mem::replace(&mut self.added_controls_height, Property::new(0.));
let v_margin = self.v_margin;
let left_margin = self.left_margin;
let pos = self.scroll_pos.clone()
.zip(previous_controls_height.clone())
.map(move |(pos, prev_h)| pnt2(pos.x + left_margin, pos.y + prev_h + v_margin * 2.));
let control = GroupBuilder::new(name, pos, self.events.clone())
.width_property(self.controls_width.clone())
.h_margin(self.h_margin)
.v_margin(self.v_margin)
.color(self.color)
.font(self.font.clone())
.title(title)
.no_right_margin()
.create();
self.added_controls_height = previous_controls_height
.zip(control.height().clone())
.map(move |(prev, h)| prev + h + v_margin * 2.);
let controls_height_sender = self.added_controls_height.clone().stream();
self.controls_height.swap_parent(controls_height_sender);
self.controls_height.set(*self.added_controls_height);
self.controls_index.insert(name.to_owned(), self.controls.len());
self.controls.push(Box::new(control));
self.controls.last_mut().unwrap().downcast_mut().unwrap()
}
pub fn control<T:Control>(&self, name: &str) -> Option<&T>{
self.controls_index.get(name).and_then(|idx| self.controls[*idx].downcast_ref())
}
pub fn control_mut<T:Control>(&mut self, name: &str) -> Option<&mut T>{
let idx = *self.controls_index.get(name)?;
self.controls[idx].downcast_mut()
}
pub fn control_or_create<T, P, B>(&mut self, name: &str, parameter: P) -> Option<&mut T>
where T: Control,
P: IntoControlBuilder<B>,
B: ControlBuilder<Control = T>
{
let idx = *self.controls_index.get(name)?;
let has_control = {
let c: Option<&mut T> = self.controls[idx].downcast_mut();
c.is_some()
};
if !has_control{
self.create_control(name, styled(parameter, "{}"));
self.controls.last_mut().and_then(|c| c.downcast_mut())
}else{
self.controls[idx].downcast_mut()
}
}
pub fn group_or_create(&mut self, name: &str) -> Option<&mut GroupV>{
let idx = *self.controls_index.get(name)?;
let has_control = {
let c: Option<&mut GroupV> = self.controls[idx].downcast_mut();
c.is_some()
};
if !has_control{
self.new_group(name);
self.controls.last_mut().and_then(|c| c.downcast_mut())
}else{
self.controls[idx].downcast_mut()
}
}
pub fn group_or_create_title(&mut self, name: &str, title: String) -> Option<&mut GroupV>{
let idx = *self.controls_index.get(name)?;
let has_control = {
let c: Option<&mut GroupV> = self.controls[idx].downcast_mut();
c.is_some()
};
if !has_control{
self.new_group_title(name, title);
self.controls.last_mut().and_then(|c| c.downcast_mut())
}else{
self.controls[idx].downcast_mut()
}
}
pub fn group_or_create_foldable(&mut self, name: &str, title: String) -> Option<&mut GroupV>{
let idx = self.controls_index.get(name).cloned();
let has_control = idx.map(|idx| {
let c: Option<&mut GroupV> = self.controls[idx].downcast_mut();
c.is_some()
}).unwrap_or(false);
if !has_control{
let group = self.new_foldable_group(name, title);
group.fold();
Some(group)
}else{
self.controls[self.controls_index[name]].downcast_mut()
}
}
}
impl ControlGeometry for GroupV{
fn flexible() -> bool{
true
}
fn geometry(&self, container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DColor>>{
self.rendered.send(());
let mut geometry = self.geometry.to_value();
if !*self.folded {
let rect = self.rect();
let rect = container.map(|container| container.intersection(&rect)).unwrap_or(rect);
let iter = self.controls.iter()
.skip_while(|control| !control.rect().overlaps(&rect))
.take_while(|control| control.rect().overlaps(&rect))
.filter_map(|control| control.geometry(Some(&rect)))
.flat_map(|geometry| geometry.into_iter());
if *self.has_scrollbar{
if let Some(scrollbar) = self.scrollbar.as_ref(){
geometry.extend(iter.chain(scrollbar.geometry(Some(&rect)).unwrap()))
}else{
geometry.extend(iter)
}
}else{
geometry.extend(iter)
}
}
Some(geometry)
}
fn text_geometry(&self, container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DTex>>{
if !*self.folded {
let rect = self.rect();
let rect = container.map(|container| container.intersection(&rect)).unwrap_or(rect);
let geometry = self.controls.iter()
.skip_while(|control| !control.rect().overlaps(&rect))
.take_while(|control| control.rect().overlaps(&rect))
.filter_map(|control| control.text_geometry(Some(&rect)))
.flat_map(|geometry| geometry.into_iter())
.chain(self.text_geometry.to_value())
.collect::<Vec<_>>();
if geometry.is_empty(){
None
}else{
Some(geometry)
}
}else{
Some(self.text_geometry.to_value())
}
}
fn texture_geometry(&self, container: Option<&Rect<f32>>) -> Option<Vec<Vertex2DTex>>{
if !*self.folded {
let rect = self.rect();
let rect = container.map(|container| container.intersection(&rect)).unwrap_or(rect);
let geometry = self.controls.iter()
.skip_while(|control| !control.rect().overlaps(&rect))
.take_while(|control| control.rect().overlaps(&rect))
.filter_map(|control| control.texture_geometry(Some(&rect)))
.flat_map(|geometry| geometry.into_iter())
.collect::<Vec<_>>();
if geometry.is_empty(){
None
}else{
Some(geometry)
}
}else{
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.clamped_height
}
}
impl ControlEvents for GroupV{
fn non_attended(&self) -> StreamRc<'static, window::Event>{
let over = self.over.clone();
let has_scrollbar = self.has_scrollbar.clone();
self.non_attended.clone()
.filter(move |e| !e.is_mouse_event() || !*over || (e.is_scroll_event() && !*has_scrollbar))
.rc()
}
}
impl Control for GroupV{
fn name(&self) -> &str{
&self.name
}
}
pub struct Tuple<T>{
param: T,
names: &'static [&'static str],
}
impl<T> Tuple<T>{
pub fn new(param: T, names: &'static [&'static str]) -> Tuple<T>{
Tuple{
param,
names
}
}
}
pub struct TupleBuilder<T>{
group: Tuple<T>,
builder: GroupBuilder,
}
macro_rules! impl_group {
($($p: ident),*) => {
impl<$($p),*> $crate::ControlBuilder for $crate::group_v::TupleBuilder<($($p),*)>
where $($p: $crate::IntoStyled,)*
$(<$p as $crate::IntoStyled>::Property: ControlDefaultProperty),*
{
type Control = $crate::GroupV;
fn shape(&mut self) -> &mut ControlShape {
self.builder.shape()
}
fn create(self) -> $crate::GroupV {
let mut group = self.builder
.v_margin(0.)
.h_margin(0.)
.overflow(Overflow::Invisible)
.transparent()
.hide_label()
.create();
#[allow(non_snake_case)]
let ($($p),*) = self.group.param;
let mut i = 0;
$(
group.add(self.group.names[i], $p);
#[allow(unused_assignments)]
{
i += 1;
}
)*
group
}
}
impl<$($p),*> $crate::ControlDefaultProperty for $crate::group_v::Tuple<($($p),*)>
where $($p: $crate::IntoStyled,)*
$(<$p as $crate::IntoStyled>::Property: ControlDefaultProperty),*
{
type Builder = $crate::group_v::TupleBuilder<($($p),*)>;
}
impl<$($p),*> $crate::BuilderFromProperty<$crate::group_v::Tuple<($($p),*)>> for $crate::group_v::TupleBuilder<($($p),*)>
where $($p: $crate::IntoStyled,)*
$(<$p as $crate::IntoStyled>::Property: ControlDefaultProperty),*
{
fn from_parameter<S>(group: $crate::group_v::Tuple<($($p),*)>, name: &str, events: S) -> $crate::group_v::TupleBuilder<($($p),*)>
where S: StreamT<'static, window::Event>
{
$crate::group_v::TupleBuilder{
builder: $crate::GroupBuilder::new(name, Property::new(origin()), events),
group,
}
}
}
};
}
impl_group!(A, B);
impl_group!(A, B, C);
impl_group!(A, B, C, D);
impl_group!(A, B, C, D, E);
impl_group!(A, B, C, D, E, F);
impl_group!(A, B, C, D, E, F, G);
impl_group!(A, B, C, D, E, F, G, H);
impl_group!(A, B, C, D, E, F, G, H, I);
impl_group!(A, B, C, D, E, F, G, H, I, J);
impl_group!(A, B, C, D, E, F, G, H, I, J, K);
impl_group!(A, B, C, D, E, F, G, H, I, J, K, L);
impl_group!(A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_group!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_group!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_group!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
#[macro_export]
macro_rules! group_v {
($p1: expr, $($p: expr),*) => {
$crate::group_v::Tuple::new(($p1, $($p),*), &[stringify!($p1), $(stringify!($p)),*])
};
}
#[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::GroupV{
fn render<R: RenderSurface>(&self, gl: &gl::Renderer<R>){
let viewport = gl.context().state().viewport();
let container: Rect<i32> = (*viewport).into();
let container: Rect<f32> = convert(container);
let container = Some(&container);
let max_height = self.max_height.unwrap_or(*self.height);
{
let width = *self.width;
let gl = gl.with_properties(&[
gl::Property::Scissor(Some(gl::Rect{
bottom: (viewport.height - max_height as i32 - self.position.y as i32) as u32,
left: self.position.x as u32,
width: width as u32,
height: max_height as u32})),
gl::Property::Blend(true),
gl::Property::BlendEquation(gl::FUNC_ADD),
gl::Property::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA),
]);
if let Some(geometry) = self.geometry(container).map(|g| mesh(g, PrimitiveType::Triangles)) {
gl.draw_mesh(&geometry);
}
if let Some(geometry) = self.texture_geometry(container).map(|m| mesh(m, PrimitiveType::Triangles)){
utils::gl::with_texture_atlas(&gl, |texture|{
if let Some(texture) = texture {
let material = gl::basic_material::Builder::new()
.texture(texture)
.create();
gl.draw_mesh_with_material(&geometry, &material);
}
})
}
}
let text_geometry = self.text_geometry(container);
let font = self.font();
if let (Some(text_geometry), Some(font)) = (text_geometry, font){
let width = *self.controls_width + self.v_margin;
let gl = gl.with_properties(&[
gl::Property::Scissor(Some(gl::Rect{
bottom: (viewport.height - max_height as i32 - self.position.y as i32) as u32,
left: self.position.x as u32,
width: width as u32,
height: max_height as u32})),
]);
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);
}
}
}
}