use std::time::{Duration, Instant};
use druid::kurbo::RoundedRect;
use druid::widget::{Button, CrossAxisAlignment, Flex, WidgetId};
use druid::{
AppLauncher, BoxConstraints, Color, Command, Data, Env, Event, EventCtx, LayoutCtx, Lens,
LifeCycle, LifeCycleCtx, LocalizedString, PaintCtx, Rect, RenderContext, Selector, Size,
TimerToken, UpdateCtx, Widget, WidgetExt, WindowDesc,
};
const CYCLE_DURATION: Duration = Duration::from_millis(100);
const FREEZE_COLOR: Selector = Selector::new("identity-example.freeze-color");
const UNFREEZE_COLOR: Selector = Selector::new("identity-example.unfreeze-color");
#[derive(Debug, Clone, Data, Lens)]
struct OurData {
#[data(same_fn = "color_eq")]
color: Color,
}
fn color_eq(one: &Color, two: &Color) -> bool {
one.as_rgba_u32() == two.as_rgba_u32()
}
struct ColorWell {
randomize: bool,
token: TimerToken,
start: Instant,
frozen: Option<Color>,
}
impl ColorWell {
pub fn new(randomize: bool) -> Self {
let frozen = if randomize {
None
} else {
Some(Color::rgba(0., 0., 0., 0.2))
};
ColorWell {
randomize,
token: TimerToken::INVALID,
start: Instant::now(),
frozen,
}
}
}
impl Widget<OurData> for ColorWell {
fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut OurData, _env: &Env) {
match event {
Event::Timer(t) if t == &self.token => {
let time_since_start = Instant::now() - self.start;
let bits = (time_since_start.as_nanos() % (0xFFFFFF)) as u32;
let mask = 0x924924;
let red = bits & mask;
let red = (red >> 16 | red >> 8 | red) & 0xFF;
let green = bits & mask >> 1;
let green = (green >> 16 | green >> 8 | green) & 0xFF;
let blue = bits & mask >> 2;
let blue = (blue >> 16 | blue >> 8 | blue) & 0xFF;
data.color = Color::rgb8(red as u8, green as u8, blue as u8);
self.token = ctx.request_timer(Instant::now() + CYCLE_DURATION);
ctx.request_paint();
}
Event::WindowConnected if self.randomize => {
self.token = ctx.request_timer(Instant::now() + CYCLE_DURATION);
}
Event::Command(cmd) if cmd.selector == FREEZE_COLOR => {
self.frozen = cmd
.get_object::<Color>()
.ok()
.cloned()
.expect("payload is always a Color")
.into();
}
Event::Command(cmd) if cmd.selector == UNFREEZE_COLOR => self.frozen = None,
_ => (),
}
}
fn lifecycle(&mut self, _ctx: &mut LifeCycleCtx, _event: &LifeCycle, _data: &OurData, _: &Env) {
}
fn update(&mut self, ctx: &mut UpdateCtx, old_data: &OurData, data: &OurData, _: &Env) {
if !old_data.same(data) {
ctx.request_paint()
}
}
fn layout(&mut self, _: &mut LayoutCtx, bc: &BoxConstraints, _: &OurData, _: &Env) -> Size {
bc.max()
}
fn paint(&mut self, ctx: &mut PaintCtx, data: &OurData, _env: &Env) {
let rect = Rect::ZERO.with_size(ctx.size());
let rect = RoundedRect::from_rect(rect, 5.0);
let color = self.frozen.as_ref().unwrap_or(&data.color);
ctx.fill(rect, color);
}
}
fn main() {
let window = WindowDesc::new(make_ui).title(
LocalizedString::new("identity-demo-window-title").with_placeholder("Color Freezing Fun"),
);
AppLauncher::with_window(window)
.use_simple_logger()
.launch(OurData {
color: Color::BLACK,
})
.expect("launch failed");
}
const ID_ONE: WidgetId = WidgetId::reserved(1);
fn make_ui() -> impl Widget<OurData> {
let id_two = WidgetId::next();
let id_three = WidgetId::next();
Flex::column()
.with_flex_child(ColorWell::new(true), 1.0)
.with_spacer(10.0)
.with_flex_child(
Flex::row()
.cross_axis_alignment(CrossAxisAlignment::Center)
.with_flex_child(ColorWell::new(false).with_id(ID_ONE), 1.0)
.with_spacer(10.0)
.with_child(
Button::<OurData>::new("freeze").on_click(move |ctx, data, _env| {
ctx.submit_command(Command::new(FREEZE_COLOR, data.color.clone()), ID_ONE)
}),
)
.with_spacer(10.0)
.with_child(
Button::<OurData>::new("unfreeze")
.on_click(move |ctx, _, _env| ctx.submit_command(UNFREEZE_COLOR, ID_ONE)),
),
0.5,
)
.with_spacer(10.0)
.with_flex_child(
Flex::row()
.cross_axis_alignment(CrossAxisAlignment::Center)
.with_flex_child(ColorWell::new(false).with_id(id_two), 1.)
.with_spacer(10.0)
.with_child(
Button::<OurData>::new("freeze").on_click(move |ctx, data, _env| {
ctx.submit_command(Command::new(FREEZE_COLOR, data.color.clone()), id_two)
}),
)
.with_spacer(10.0)
.with_child(
Button::<OurData>::new("unfreeze")
.on_click(move |ctx, _, _env| ctx.submit_command(UNFREEZE_COLOR, id_two)),
),
0.5,
)
.with_spacer(10.0)
.with_flex_child(
Flex::row()
.cross_axis_alignment(CrossAxisAlignment::Center)
.with_flex_child(ColorWell::new(false).with_id(id_three), 1.)
.with_spacer(10.0)
.with_child(
Button::<OurData>::new("freeze").on_click(move |ctx, data, _env| {
ctx.submit_command(Command::new(FREEZE_COLOR, data.color.clone()), id_three)
}),
)
.with_spacer(10.0)
.with_child(
Button::<OurData>::new("unfreeze")
.on_click(move |ctx, _, _env| ctx.submit_command(UNFREEZE_COLOR, id_three)),
),
0.5,
)
.padding(10.)
}