use {Borderable, Colorable, Labelable, Positionable, Sizeable, Widget};
use {Color, FontSize, Scalar, UiCell};
use position;
use std;
use text;
use widget;
#[derive(Copy, Clone, Debug, WidgetCommon_)]
pub struct CollapsibleArea<'a> {
#[conrod(common_builder)]
common: widget::CommonBuilder,
style: Style,
is_open: bool,
text: &'a str,
}
widget_ids! {
#[allow(missing_docs, missing_copy_implementations)]
pub struct Ids {
button,
triangle,
area,
}
}
pub struct State {
ids: Ids,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, WidgetStyle_)]
pub struct Style {
#[conrod(default = "theme.shape_color")]
pub color: Option<Color>,
#[conrod(default = "theme.border_width")]
pub border: Option<Scalar>,
#[conrod(default = "theme.border_color")]
pub border_color: Option<Color>,
#[conrod(default = "theme.label_color")]
pub label_color: Option<Color>,
#[conrod(default = "None")]
pub label_font_size: Option<Option<FontSize>>,
#[conrod(default = "theme.font_id")]
pub label_font_id: Option<Option<text::font::Id>>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Event {
Open,
Close,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Area {
pub id: widget::Id,
pub collapsible_area_id: widget::Id,
pub width: Scalar
}
impl<'a> CollapsibleArea<'a> {
pub fn new(is_open: bool, text: &'a str) -> Self {
CollapsibleArea {
common: widget::CommonBuilder::default(),
style: Style::default(),
is_open: is_open,
text: text,
}
}
pub fn label_color(mut self, color: Color) -> Self {
self.style.label_color = Some(color);
self
}
pub fn label_font_size(mut self, font_size: FontSize) -> Self {
self.style.label_font_size = Some(Some(font_size));
self
}
pub fn label_font_id(mut self, font_id: text::font::Id) -> Self {
self.style.label_font_id = Some(Some(font_id));
self
}
}
impl<'a> Widget for CollapsibleArea<'a> {
type State = State;
type Style = Style;
type Event = (Option<Area>, Option<Event>);
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
State {
ids: Ids::new(id_gen),
}
}
fn style(&self) -> Style {
self.style.clone()
}
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
let widget::UpdateArgs { id, state, style, rect, ui, .. } = args;
let CollapsibleArea { text, mut is_open, .. } = self;
let (_, _, w, h) = rect.x_y_w_h();
let color = style.color(&ui.theme);
let border = style.border(&ui.theme);
let border_color = style.border_color(&ui.theme);
let label_color = style.label_color(&ui.theme);
let label_font_id = style.label_font_id(&ui.theme).or(ui.fonts.ids().next());
let label_font_size = match style.label_font_size(&ui.theme) {
Some(font_size) => font_size,
None => std::cmp::max((h / 2.5) as FontSize, 10),
};
let triangle_rect = position::Rect {
x: position::Range {
start: rect.x.start,
end: rect.x.start + rect.y.len(),
},
y: rect.y,
};
let event = widget::Button::new()
.w_h(w, h)
.middle_of(id)
.color(color)
.border(border)
.border_color(border_color)
.label(text)
.label_color(label_color)
.label_font_size(label_font_size)
.label_x(position::Relative::Place(position::Place::Start(Some(triangle_rect.w()))))
.and_then(label_font_id, |b, font_id| b.label_font_id(font_id))
.set(state.ids.button, ui)
.next()
.map(|_| {
is_open = !is_open;
if is_open { Event::Open } else { Event::Close }
});
let side_offset = triangle_rect.w() / 10.0;
let point_offset = triangle_rect.h() / 6.0;
let triangle_x = triangle_rect.x();
let triangle_y = triangle_rect.y();
let points = if is_open {
let a = [triangle_x, triangle_y - point_offset];
let b = [triangle_x + side_offset, triangle_y + point_offset];
let c = [triangle_x - side_offset, triangle_y + point_offset];
[a, b, c]
} else {
let a = [triangle_x + point_offset, triangle_y];
let b = [triangle_x - point_offset, triangle_y + side_offset];
let c = [triangle_x - point_offset, triangle_y - side_offset];
[a, b, c]
};
widget::Polygon::fill(points.iter().cloned())
.align_middle_y_of(state.ids.button)
.align_left_of(state.ids.button)
.wh(triangle_rect.dim())
.parent(state.ids.button)
.graphics_for(state.ids.button)
.color(label_color)
.set(state.ids.triangle, ui);
let area = if is_open {
Some(Area {
id: state.ids.area,
collapsible_area_id: id,
width: w,
})
} else {
None
};
(area, event)
}
}
impl<'a> Colorable for CollapsibleArea<'a> {
fn color(mut self, color: Color) -> Self {
self.style.color = Some(color);
self
}
}
impl<'a> Borderable for CollapsibleArea<'a> {
fn border(mut self, border: Scalar) -> Self {
self.style.border = Some(border);
self
}
fn border_color(mut self, color: Color) -> Self {
self.style.border_color = Some(color);
self
}
}
impl Event {
pub fn is_open(&self) -> bool {
match *self {
Event::Open => true,
Event::Close => false,
}
}
}
impl Area {
pub fn set<W>(self, widget: W, ui: &mut UiCell) -> W::Event
where W: Widget,
{
let Area { id, collapsible_area_id, width } = self;
widget
.w(width)
.parent(collapsible_area_id)
.align_middle_x_of(collapsible_area_id)
.down_from(collapsible_area_id, 0.0)
.set(id, ui)
}
}