Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hot glow option to multiwin example. #845

Merged
merged 1 commit into from
Apr 16, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 66 additions & 5 deletions druid/examples/multiwin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,25 @@

//! Opening and closing windows and using window and context menus.

use druid::widget::{Align, Button, Flex, Label, Padding};
use druid::widget::prelude::*;
use druid::widget::{Align, BackgroundBrush, Button, Flex, Label, Padding};
use druid::{
commands as sys_cmds, AppDelegate, AppLauncher, Command, ContextMenu, Data, DelegateCtx, Env,
Event, EventCtx, LocalizedString, MenuDesc, MenuItem, Selector, Target, Widget, WindowDesc,
WindowId,
commands as sys_cmds, AppDelegate, AppLauncher, Color, Command, ContextMenu, Data, DelegateCtx,
LocalizedString, MenuDesc, MenuItem, Selector, Target, WidgetPod, WindowDesc, WindowId,
};

use log::info;

const MENU_COUNT_ACTION: Selector = Selector::new("menu-count-action");
const MENU_INCREMENT_ACTION: Selector = Selector::new("menu-increment-action");
const MENU_DECREMENT_ACTION: Selector = Selector::new("menu-decrement-action");
const MENU_SWITCH_GLOW_ACTION: Selector = Selector::new("menu-switch-glow");

#[derive(Debug, Clone, Default, Data)]
struct State {
menu_count: usize,
selected: usize,
glow_hot: bool,
}

pub fn main() {
Expand Down Expand Up @@ -79,7 +81,58 @@ fn ui_builder() -> impl Widget<State> {
row.add_child(Padding::new(5.0, inc_button));
row.add_child(Padding::new(5.0, dec_button));
col.add_flex_child(Align::centered(row), 1.0);
col
Glow::new(col)
}

struct Glow<W> {
inner: WidgetPod<State, W>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if a widget is entirely transparent for the purposes of event handling and layout (it is just passing things through) then WidgetPod is unnecessary. It doesn't hurt in any meaningful way, but it is unnecessary.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This really needs to be written down 😅

}

impl<W: Widget<State>> Glow<W> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a note: I think you could have achieved this using a Painter?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps in some way, but it wasn't super obvious. Like the Painter docs state: If you would like it to repaint at other times (such as when hot or active state changes) you will need to call request_paint further up the tree. There's nothing further up the tree, so it wouldn't just be a Painter at least.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh huh for some reason I thought it did repaint on hot. My mistake!

I wonder if it makes sense to just make 'paint on hot' be an option for painter? but that's a small thing.

pub fn new(inner: W) -> Glow<W> {
Glow {
inner: WidgetPod::new(inner),
}
}
}

impl<W: Widget<State>> Widget<State> for Glow<W> {
fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut State, env: &Env) {
self.inner.event(ctx, event, data, env);
}

fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &State, env: &Env) {
if let LifeCycle::HotChanged(_) = event {
ctx.request_paint();
}
self.inner.lifecycle(ctx, event, data, env);
}

fn update(&mut self, ctx: &mut UpdateCtx, old_data: &State, data: &State, env: &Env) {
if old_data.glow_hot != data.glow_hot {
ctx.request_paint();
}
self.inner.update(ctx, data, env);
}

fn layout(
&mut self,
ctx: &mut LayoutCtx,
bc: &BoxConstraints,
data: &State,
env: &Env,
) -> Size {
let size = self.inner.layout(ctx, bc, data, env);
self.inner.set_layout_rect(ctx, data, env, size.to_rect());
size
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a general note; I think it's good to call bc.constrain(size) anytime you return from layout, as a way of ensuring all returned sizes are appropriate for the constraints.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, my inspiration came from ControllerHost which does not do that, maybe open an Issue to make this consistent for all container?

}

fn paint(&mut self, ctx: &mut PaintCtx, data: &State, env: &Env) {
if data.glow_hot && ctx.is_hot() {
BackgroundBrush::Color(Color::rgb8(200, 55, 55)).paint(ctx, data, env);
}
self.inner.paint(ctx, data, env);
}
}

struct Delegate;
Expand Down Expand Up @@ -144,6 +197,10 @@ impl AppDelegate<State> for Delegate {
ctx.submit_command(cmd, *id);
false
}
(_, &MENU_SWITCH_GLOW_ACTION) => {
data.glow_hot = !data.glow_hot;
false
}
_ => true,
}
}
Expand Down Expand Up @@ -207,4 +264,8 @@ fn make_context_menu<T: Data>() -> MenuDesc<T> {
LocalizedString::new("Decrement"),
MENU_DECREMENT_ACTION,
))
.append(MenuItem::new(
LocalizedString::new("Glow when hot"),
MENU_SWITCH_GLOW_ACTION,
))
}