use tabs::{TabsBuilder, Tabs};
use rin::window::{self, WindowT, Window, Events, MouseEvents, Cursor, MouseEvent, MouseButton};
use rin::math::{Vec2, Pnt2, Rect, vec2, origin, pnt2, clamp};
use rin::events::{StreamRc, StreamT, Property, IterAsync};
use std::mem;
use traits::{ControlEvents, ControlBuilder};
pub type Width = f32;
pub type Height = f32;
pub type Pos = Pnt2;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Mode{
Left(Width),
Right(Width),
Top(Height),
Bottom(Height),
Free(Pos, Width, Height)
}
pub struct ResizableContainer<C>{
control: C,
cursor: IterAsync<'static, Cursor>,
non_attended: StreamRc<'static, window::Event>,
}
impl<C> ResizableContainer<C>{
pub fn control(&self) -> &C{
&self.control
}
pub fn control_mut(&mut self) -> &mut C{
&mut self.control
}
}
impl ResizableContainer<Tabs>{
pub fn new_tabs<S>(mode: Mode, events: S, viewport: Property<'static, Rect<i32>>) -> ResizableContainer<Tabs>
where S: StreamT<'static, window::Event>,
{
let events = events.rc();
let width;
let height;
match mode {
Mode::Left(w) | Mode::Right(w) => {
width = Property::new(w);
height = viewport.clone().map(|r| r.height as f32);
}
Mode::Top(h) | Mode::Bottom(h) => {
width = viewport.clone().map(|r| r.width as f32);
height = Property::new(h);
}
Mode::Free(_, w, h) => {
width = Property::new(w);
height = Property::new(h);
}
}
let pos = match mode {
Mode::Left(_) | Mode::Top(_) => Property::new(pnt2(0., 0.)),
Mode::Right(_) => viewport.clone()
.map(|r| r.width)
.zip(width.clone()).map(|(vw, w)| pnt2(vw as f32 - w, 0.)),
Mode::Bottom(_) => viewport.clone()
.map(|r| r.height)
.zip(height.clone()).map(|(vh, h)| pnt2(0., vh as f32 - h)),
Mode::Free(pos,_,_) => Property::new(pos),
};
let on_resize_area = {
let width = width.clone();
let height = height.clone();
let control_pos = pos.clone();
events.clone().mouse().moved().map(move |pos| match mode{
Mode::Left(_) => pos.x as f32 > *width - 1. && (pos.x as f32) < *width + 5.,
Mode::Right(_) => pos.x as f32 > control_pos.x - 5. && (pos.x as f32) < control_pos.x + 1.,
Mode::Top(_) => pos.y as f32 > *height -1. && (pos.y as f32) < *width + 5.,
Mode::Bottom(_) => pos.y as f32 > control_pos.y - 5. && (pos.y as f32) < control_pos.y + 1.,
Mode::Free(_,_,_) => false,
}).to_property(false)
};
#[derive(Clone, Debug, Copy, PartialEq)]
enum State{
Disabled,
OnResizeArea,
Resizing(Vec2<f32>, Pnt2<f64>),
}
let w = *width;
let h = *height;
let size = {
let on_resize_area = on_resize_area.clone();
let mut control_pos = pos.clone();
events.clone().mouse().fold((width, height, State::Disabled), move |(mut width, mut height, mut state), mouse_event|{
match mouse_event{
MouseEvent::Pressed{pos, button: MouseButton::Left, ..} => if *on_resize_area{
state = State::Resizing(vec2!(*width, *height), pos);
}
MouseEvent::Released{button: MouseButton::Left, ..} => state = State::Disabled,
MouseEvent::Moved{pos} => {
match state{
State::Resizing(start_size, start_pos) => match mode{
Mode::Left(_) => {
let diff = pos.x - start_pos.x;
let w = clamp(start_size.x + diff as f32, 0., viewport.width as f32);
width.set(w);
},
Mode::Right(_) => {
let diff = start_pos.x - pos.x;
let w = clamp(start_size.x + diff as f32, 0., viewport.width as f32);
width.set(w);
let x = clamp((start_pos.x - diff) as f32, 0., viewport.width as f32);
let y = control_pos.y;
control_pos.set(pnt2(x, y));
}
Mode::Top(_) => {
let diff = pos.y - start_pos.y;
let h = clamp(start_size.y + diff as f32, 0., viewport.height as f32);
height.set(h);
}
Mode::Bottom(_) => {
let diff = start_pos.y - pos.y;
let h = clamp(start_size.x + diff as f32, 0., viewport.height as f32);
height.set(h);
let x = control_pos.x;
let y = clamp((start_pos.y - diff) as f32, 0., viewport.height as f32);
control_pos.set(pnt2(x, y));
}
Mode::Free(_,_,_) => {
},
},
_ => if *on_resize_area{
state = State::OnResizeArea;
}else{
state = State::Disabled;
},
}
}
_ => (),
}
(width, height, state)
}).map(|(w,h,s)| (*w, *h, s)).rc()
};
let state = size.clone().map(|(_,_,s)| s).dedup().rc();
let width = size.clone().map(|(w,_,_)| w).to_property(w);
let widthh = width.clone();
let width = events.clone().update().map(move |_| *widthh)
.dedup()
.throttle(30.)
.merge(state.clone().filter_map(move |state| if state == State::Disabled{
Some(*width)
}else{
None
}))
.to_property(w);
let height = size.clone().map(|(_,h,_)| h).to_property(h);
let heightt = height.clone();
let height = events.clone().update().map(move |_| *heightt)
.dedup()
.throttle(30.)
.merge(state.clone().filter_map(move |state| if state == State::Disabled{
Some(*height)
}else{
None
}))
.to_property(h);
let cursor = state.clone().map(move |state|{
match state{
State::Disabled => Cursor::Arrow,
State::OnResizeArea | State::Resizing(_,_) => match mode{
Mode::Left(_) | Mode::Right(_) => Cursor::HResize,
Mode::Top(_) | Mode::Bottom(_) => Cursor::VResize,
Mode::Free(_,_,_) => Cursor::Arrow,
}
}
}).dedup().iter_async();
let tabs = TabsBuilder::new("", pos, events)
.width_property(width.clone())
.max_height(height.clone().map(|h| Some(h)))
.create();
let non_attended = {
let state = state.to_property(State::Disabled);
tabs.non_attended().filter(move |e|{
if let window::Event::MousePressed{..} = e {
return !*on_resize_area;
}
mem::discriminant(&*state) != mem::discriminant(&State::Resizing(vec2!(0.), origin()))
}).rc()
};
ResizableContainer{
control: tabs,
cursor,
non_attended,
}
}
pub fn update(&mut self, window: &mut Window){
if let Some(cursor) = self.cursor.by_ref().last(){
window.set_cursor(cursor);
}
}
pub fn non_attended(&self) -> StreamRc<'static, window::Event>{
self.non_attended.clone()
}
}