use {Color, Colorable, FontSize, Borderable, Labelable, Positionable, Sizeable, UiCell, Widget};
use image;
use position::{self, Align, Rect, Scalar};
use text;
use widget;
#[derive(Clone)]
pub struct Button<'a, S> {
common: widget::CommonBuilder,
maybe_label: Option<&'a str>,
pub show: S,
pub style: Style,
enabled: bool,
}
widget_style!{
style Style {
- color: Color { theme.shape_color }
- border: Scalar { theme.border_width }
- border_color: Color { theme.border_color }
- label_color: Color { theme.label_color }
- label_font_size: FontSize { theme.font_size_medium }
- label_font_id: Option<text::font::Id> { theme.font_id }
- label_justify: text::Justify { text::Justify::Center }
- label_x: position::Relative { position::Relative::Align(Align::Middle) }
- label_y: position::Relative { position::Relative::Align(Align::Middle) }
}
}
widget_ids! {
#[allow(missing_docs, missing_copy_implementations)]
pub struct FlatIds {
rectangle,
label,
}
}
widget_ids! {
#[allow(missing_docs, missing_copy_implementations)]
pub struct ImageIds {
image,
label,
}
}
#[derive(Copy, Clone)]
pub struct Flat;
#[derive(Copy, Clone)]
pub struct Image {
pub image_id: image::Id,
pub hover_image_id: Option<image::Id>,
pub press_image_id: Option<image::Id>,
pub color: ImageColor,
pub src_rect: Option<Rect>,
}
#[derive(Copy, Clone, Debug)]
pub enum ImageColor {
Normal(Color),
WithFeedback(Color),
None,
}
#[derive(Copy, Clone)]
enum Interaction { Idle, Hover, Press }
#[derive(Clone, Debug)]
#[allow(missing_copy_implementations)]
pub struct TimesClicked(pub u16);
impl TimesClicked {
pub fn was_clicked(self) -> bool { self.0 > 0 }
}
impl Iterator for TimesClicked {
type Item = ();
fn next(&mut self) -> Option<Self::Item> {
if self.0 > 0 {
self.0 -= 1;
Some(())
} else {
None
}
}
}
impl<'a> Button<'a, Image> {
pub fn image(image_id: image::Id) -> Self {
let image = Image {
image_id: image_id,
hover_image_id: None,
press_image_id: None,
src_rect: None,
color: ImageColor::None,
};
Self::new_internal(image)
}
pub fn source_rectangle(mut self, rect: Rect) -> Self {
self.show.src_rect = Some(rect);
self
}
pub fn image_color(mut self, color: Color) -> Self {
self.show.color = ImageColor::Normal(color);
self
}
pub fn image_color_with_feedback(mut self, color: Color) -> Self {
self.show.color = ImageColor::WithFeedback(color);
self
}
pub fn hover_image(mut self, id: image::Id) -> Self {
self.show.hover_image_id = Some(id);
self
}
pub fn press_image(mut self, id: image::Id) -> Self {
self.show.press_image_id = Some(id);
self
}
}
impl<'a> Button<'a, Flat> {
pub fn new() -> Self {
Self::new_internal(Flat)
}
pub fn with_style(mut self, s: Style) -> Self{
self.style = s;
self
}
}
impl<'a, S> Button<'a, S> {
fn new_internal(show: S) -> Self {
Button {
common: widget::CommonBuilder::new(),
show: show,
maybe_label: None,
style: Style::new(),
enabled: true,
}
}
pub fn label_font_id(mut self, font_id: text::font::Id) -> Self {
self.style.label_font_id = Some(Some(font_id));
self
}
pub fn left_justify_label(mut self) -> Self {
self.style.label_justify = Some(text::Justify::Left);
self
}
pub fn center_justify_label(mut self) -> Self {
self.style.label_justify = Some(text::Justify::Center);
self
}
pub fn right_justify_label(mut self) -> Self {
self.style.label_justify = Some(text::Justify::Right);
self
}
pub fn label_x(mut self, x: position::Relative) -> Self {
self.style.label_x = Some(x);
self
}
pub fn label_y(mut self, y: position::Relative) -> Self {
self.style.label_y = Some(y);
self
}
builder_methods!{
pub enabled { enabled = bool }
}
}
impl<'a> Widget for Button<'a, Flat> {
type State = FlatIds;
type Style = Style;
type Event = TimesClicked;
fn common(&self) -> &widget::CommonBuilder {
&self.common
}
fn common_mut(&mut self) -> &mut widget::CommonBuilder {
&mut self.common
}
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
FlatIds::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 Button { maybe_label, .. } = self;
let (interaction, times_triggered) = interaction_and_times_triggered(id, ui);
let color = color_from_interaction(style.color(&ui.theme), interaction);
bordered_rectangle(id, state.rectangle, rect, color, style, ui);
if let Some(l) = maybe_label {
label(id, state.label, l, style, ui);
}
TimesClicked(times_triggered)
}
}
impl<'a> Widget for Button<'a, Image> {
type State = ImageIds;
type Style = Style;
type Event = TimesClicked;
fn common(&self) -> &widget::CommonBuilder {
&self.common
}
fn common_mut(&mut self) -> &mut widget::CommonBuilder {
&mut self.common
}
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
ImageIds::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 Button { show, maybe_label, .. } = self;
let (interaction, times_triggered) = interaction_and_times_triggered(id, ui);
let Image { image_id, press_image_id, hover_image_id, src_rect, color } = show;
let image_id = match interaction {
Interaction::Idle => image_id,
Interaction::Hover => hover_image_id.unwrap_or(image_id),
Interaction::Press => press_image_id.or(hover_image_id).unwrap_or(image_id),
};
let (x, y, w, h) = rect.x_y_w_h();
let mut image = widget::Image::new(image_id)
.x_y(x, y)
.w_h(w, h)
.parent(id)
.graphics_for(id);
image.src_rect = src_rect;
image.style.maybe_color = match color {
ImageColor::Normal(color) => Some(Some(color)),
ImageColor::WithFeedback(color) =>
ui.widget_input(id).mouse()
.map(|mouse| if mouse.buttons.left().is_down() {
Some(color.clicked())
} else {
Some(color.highlighted())
})
.or(Some(Some(color))),
ImageColor::None => None,
};
image.set(state.image, ui);
if let Some(s) = maybe_label {
label(id, state.label, s, style, ui);
}
TimesClicked(times_triggered)
}
}
fn color_from_interaction(color: Color, interaction: Interaction) -> Color {
match interaction {
Interaction::Idle => color,
Interaction::Hover => color.highlighted(),
Interaction::Press => color.clicked(),
}
}
fn interaction_and_times_triggered(button_id: widget::Id, ui: &UiCell) -> (Interaction, u16) {
let input = ui.widget_input(button_id);
let interaction = input.mouse().map_or(Interaction::Idle, |mouse| {
if mouse.buttons.left().is_down() {
Interaction::Press
} else {
Interaction::Hover
}
});
let times_triggered = (input.clicks().left().count() + input.taps().count()) as u16;
(interaction, times_triggered)
}
fn bordered_rectangle(button_id: widget::Id, rectangle_id: widget::Id,
rect: Rect, color: Color, style: &Style, ui: &mut UiCell)
{
let dim = rect.dim();
let border = style.border(&ui.theme);
let border_color = style.border_color(&ui.theme);
widget::BorderedRectangle::new(dim)
.middle_of(button_id)
.graphics_for(button_id)
.color(color)
.border(border)
.border_color(border_color)
.set(rectangle_id, ui);
}
fn label(button_id: widget::Id, label_id: widget::Id,
label: &str, style: &Style, ui: &mut UiCell)
{
let color = style.label_color(&ui.theme);
let font_size = style.label_font_size(&ui.theme);
let x = style.label_x(&ui.theme);
let y = style.label_y(&ui.theme);
let justify = style.label_justify(&ui.theme);
let font_id = style.label_font_id(&ui.theme).or(ui.fonts.ids().next());
widget::Text::new(label)
.and_then(font_id, widget::Text::font_id)
.x_position_relative_to(button_id, x)
.y_position_relative_to(button_id, y)
.justify(justify)
.parent(button_id)
.graphics_for(button_id)
.color(color)
.font_size(font_size)
.set(label_id, ui);
}
impl<'a, S> Colorable for Button<'a, S> {
builder_method!(color { style.color = Some(Color) });
}
impl<'a, S> Borderable for Button<'a, S> {
builder_methods!{
border { style.border = Some(Scalar) }
border_color { style.border_color = Some(Color) }
}
}
impl<'a, S> Labelable<'a> for Button<'a, S> {
builder_methods!{
label { maybe_label = Some(&'a str) }
label_color { style.label_color = Some(Color) }
label_font_size { style.label_font_size = Some(FontSize) }
}
}