#[macro_use] extern crate conrod;
extern crate find_folder;
extern crate piston_window;
use conrod::{
Button,
Canvas,
Circle,
Color,
Colorable,
DropDownList,
EnvelopeEditor,
Frameable,
Labelable,
NumberDialer,
Point,
Positionable,
Slider,
Sizeable,
Text,
TextBox,
Theme,
Toggle,
Widget,
WidgetMatrix,
XYPad,
};
use conrod::color::{self, rgb};
use piston_window::{EventLoop, Glyphs, PistonWindow, UpdateEvent, WindowSettings};
use std::sync::mpsc;
type Ui = conrod::Ui<Glyphs>;
struct DemoApp {
bg_color: Color,
show_button: bool,
toggle_label: String,
title_pad: f64,
v_slider_height: f64,
frame_width: f64,
bool_matrix: [[bool; 8]; 8],
ddl_colors: Vec<String>,
ddl_color: Color,
selected_idx: Option<usize>,
circle_pos: Point,
envelopes: Vec<(Vec<Point>, String)>,
elem_sender: mpsc::Sender<(usize, usize, bool)>,
elem_receiver: mpsc::Receiver<(usize, usize, bool)>,
}
impl DemoApp {
fn new() -> DemoApp {
let (elem_sender, elem_receiver) = mpsc::channel();
DemoApp {
bg_color: rgb(0.2, 0.35, 0.45),
show_button: false,
toggle_label: "OFF".to_string(),
title_pad: 350.0,
v_slider_height: 230.0,
frame_width: 1.0,
bool_matrix: [ [true, true, true, true, true, true, true, true],
[true, false, false, false, false, false, false, true],
[true, false, true, false, true, true, true, true],
[true, false, true, false, true, true, true, true],
[true, false, false, false, true, true, true, true],
[true, true, true, true, true, true, true, true],
[true, true, false, true, false, false, false, true],
[true, true, true, true, true, true, true, true] ],
ddl_colors: vec!["Black".to_string(),
"White".to_string(),
"Red".to_string(),
"Green".to_string(),
"Blue".to_string()],
ddl_color: color::PURPLE,
selected_idx: None,
circle_pos: [-50.0, 110.0],
envelopes: vec![(vec![ [0.0, 0.0],
[0.1, 17000.0],
[0.25, 8000.0],
[0.5, 2000.0],
[1.0, 0.0], ], "Envelope A".to_string()),
(vec![ [0.0, 0.85],
[0.3, 0.2],
[0.6, 0.6],
[1.0, 0.0], ], "Envelope B".to_string())],
elem_sender: elem_sender,
elem_receiver: elem_receiver,
}
}
}
fn main() {
let window: PistonWindow =
WindowSettings::new("All The Widgets!", [1100, 560])
.exit_on_esc(true).vsync(true).build().unwrap();
let mut ui = {
let assets = find_folder::Search::KidsThenParents(3, 5)
.for_folder("assets").unwrap();
let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf");
let theme = Theme::default();
let glyph_cache = Glyphs::new(&font_path, window.factory.borrow().clone());
Ui::new(glyph_cache.unwrap(), theme)
};
let mut app = DemoApp::new();
for event in window.ups(60) {
ui.handle_event(&event);
event.update(|_| ui.set_widgets(|ui| set_widgets(ui, &mut app)));
event.draw_2d(|c, g| ui.draw_if_changed(c, g));
}
}
fn set_widgets(ui: &mut Ui, app: &mut DemoApp) {
Canvas::new()
.frame(app.frame_width)
.pad(30.0)
.color(app.bg_color)
.scroll_kids()
.set(CANVAS, ui);
Text::new("Widget Demonstration")
.top_left_with_margins_on(CANVAS, 0.0, app.title_pad)
.font_size(32)
.color(app.bg_color.plain_contrast())
.set(TITLE, ui);
if app.show_button {
Button::new()
.w_h(200.0, 50.0)
.mid_left_of(CANVAS)
.down_from(TITLE, 45.0)
.rgb(0.4, 0.75, 0.6)
.frame(app.frame_width)
.label("PRESS")
.react(|| app.bg_color = color::random())
.set(BUTTON, ui)
}
else {
let pad = app.title_pad as i16;
let pad_string = pad.to_string();
let label = {
let mut text = "Padding: ".to_string();
text.push_str(&pad_string);
text
};
Slider::new(pad as f32, 30.0, 700.0)
.w_h(200.0, 50.0)
.mid_left_of(CANVAS)
.down_from(TITLE, 45.0)
.rgb(0.5, 0.3, 0.6)
.frame(app.frame_width)
.label(&label)
.label_color(color::WHITE)
.react(|new_pad: f32| app.title_pad = new_pad as f64)
.set(TITLE_PAD_SLIDER, ui);
}
let label = app.toggle_label.clone();
let shown_widget = if app.show_button { BUTTON } else { TITLE_PAD_SLIDER };
Toggle::new(app.show_button)
.w_h(75.0, 75.0)
.down(20.0)
.rgb(0.6, 0.25, 0.75)
.frame(app.frame_width)
.label(&label)
.label_color(color::WHITE)
.react(|value| {
app.show_button = value;
app.toggle_label = match value {
true => "ON".to_string(),
false => "OFF".to_string()
}
})
.set(TOGGLE, ui);
for i in 0..3 {
let color = match i {
0 => rgb(0.75, 0.3, 0.3),
1 => rgb(0.3, 0.75, 0.3),
_ => rgb(0.3, 0.3, 0.75),
};
let value = match i {
0 => app.bg_color.red(),
1 => app.bg_color.green(),
_ => app.bg_color.blue(),
};
let label = format!("{:.*}", 2, value);
if i == 0 { Slider::new(value, 0.0, 1.0).down(25.0) }
else { Slider::new(value, 0.0, 1.0).right(20.0) }
.w_h(40.0, app.v_slider_height)
.color(color)
.frame(app.frame_width)
.label(&label)
.label_color(color::WHITE)
.react(|color| match i {
0 => app.bg_color.set_red(color),
1 => app.bg_color.set_green(color),
_ => app.bg_color.set_blue(color),
})
.set(COLOR_SLIDER + i, ui);
}
NumberDialer::new(app.v_slider_height, 25.0, 250.0, 1)
.w_h(260.0, 60.0)
.right_from(shown_widget, 30.0)
.color(app.bg_color.invert())
.frame(app.frame_width)
.label("Height (px)")
.label_color(app.bg_color.invert().plain_contrast())
.react(|new_height| app.v_slider_height = new_height)
.set(SLIDER_HEIGHT, ui);
NumberDialer::new(app.frame_width, 0.0, 15.0, 2)
.w_h(260.0, 60.0)
.down(20.0)
.color(app.bg_color.invert().plain_contrast())
.frame(app.frame_width)
.frame_color(app.bg_color.plain_contrast())
.label("Frame Width (px)")
.label_color(app.bg_color.plain_contrast())
.react(|new_width| app.frame_width = new_width)
.set(FRAME_WIDTH, ui);
let (cols, rows) = (8, 8);
WidgetMatrix::new(cols, rows)
.down(20.0)
.w_h(260.0, 260.0) .each_widget(|_n, col: usize, row: usize| {
let (r, g, b, a) = (
0.5 + (col as f32 / cols as f32) / 2.0,
0.75,
1.0 - (row as f32 / rows as f32) / 2.0,
1.0
);
let elem = app.bool_matrix[col][row];
let elem_sender = app.elem_sender.clone();
Toggle::new(elem)
.rgba(r, g, b, a)
.frame(app.frame_width)
.react(move |new_val: bool| elem_sender.send((col, row, new_val)).unwrap())
})
.set(TOGGLE_MATRIX, ui);
while let Ok((col, row, value)) = app.elem_receiver.try_recv() {
app.bool_matrix[col][row] = value;
}
let mut ddl_color = app.ddl_color;
DropDownList::new(&mut app.ddl_colors, &mut app.selected_idx)
.w_h(150.0, 40.0)
.right_from(SLIDER_HEIGHT, 30.0) .max_visible_items(3)
.color(ddl_color)
.frame(app.frame_width)
.frame_color(ddl_color.plain_contrast())
.label("Colors")
.label_color(ddl_color.plain_contrast())
.react(|selected_idx: &mut Option<usize>, new_idx, string: &str| {
*selected_idx = Some(new_idx);
ddl_color = match string {
"Black" => color::BLACK,
"White" => color::WHITE,
"Red" => color::RED,
"Green" => color::GREEN,
"Blue" => color::BLUE,
_ => color::PURPLE,
};
})
.set(COLOR_SELECT, ui);
app.ddl_color = ddl_color;
XYPad::new(app.circle_pos[0], -75.0, 75.0, app.circle_pos[1], 95.0, 245.0) .w_h(150.0, 150.0)
.right_from(TOGGLE_MATRIX, 30.0)
.align_bottom_of(TOGGLE_MATRIX) .color(ddl_color)
.frame(app.frame_width)
.frame_color(color::WHITE)
.label("Circle Position")
.label_color(ddl_color.plain_contrast().alpha(0.5))
.line_thickness(2.0)
.react(|new_x, new_y| {
app.circle_pos[0] = new_x;
app.circle_pos[1] = new_y;
})
.set(CIRCLE_POSITION, ui);
Circle::fill(15.0)
.xy_relative_to(CIRCLE_POSITION, app.circle_pos)
.color(app.ddl_color)
.set(CIRCLE, ui);
for i in 0..2 {
let &mut (ref mut env, ref mut text) = &mut app.envelopes[i];
if i == 0 { TextBox::new(text).right_from(COLOR_SELECT, 30.0) }
else { TextBox::new(text) }
.font_size(20)
.w_h(320.0, 40.0)
.frame(app.frame_width)
.frame_color(app.bg_color.invert().plain_contrast())
.color(app.bg_color.invert())
.react(|_string: &mut String|{})
.set(ENVELOPE_EDITOR + (i * 2), ui);
let env_y_max = match i { 0 => 20_000.0, _ => 1.0 };
let env_skew_y = match i { 0 => 3.0, _ => 1.0 };
EnvelopeEditor::new(env, 0.0, 1.0, 0.0, env_y_max)
.down(10.0)
.w_h(320.0, 150.0)
.skew_y(env_skew_y)
.color(app.bg_color.invert())
.frame(app.frame_width)
.frame_color(app.bg_color.invert().plain_contrast())
.label(&text)
.label_color(app.bg_color.invert().plain_contrast().alpha(0.5))
.point_radius(6.0)
.line_thickness(2.0)
.react(|_points: &mut Vec<Point>, _idx: usize|{})
.set(ENVELOPE_EDITOR + (i * 2) + 1, ui);
}
}
widget_ids! {
CANVAS,
TITLE,
BUTTON,
TITLE_PAD_SLIDER,
TOGGLE,
COLOR_SLIDER with 3,
SLIDER_HEIGHT,
FRAME_WIDTH,
TOGGLE_MATRIX,
COLOR_SELECT,
CIRCLE_POSITION,
CIRCLE,
ENVELOPE_EDITOR with 4
}