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

Propagate events to hidden widgets in Either #1351

Merged
merged 6 commits into from
Oct 31, 2020
Merged
Show file tree
Hide file tree
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ You can find its changes [documented below](#060---2020-06-01).
- Widgets can specify a baseline, flex rows can align baselines ([#1295] by [@cmyr])
- `TextBox::with_text_color` and `TextBox::set_text_color` ([#1320] by [@cmyr])
- `Checkbox::set_text` to update the label. ([#1346] by [@finnerale])
- `Event::should_propagate_to_hidden` and `Lifecycle::should_propagate_to_hidden` to determine whether an event should be sent to hidden widgets (e.g. in `Tabs` or `Either`). ([#1351] by [@andrewhickman])

### Changed

Expand Down Expand Up @@ -114,6 +115,7 @@ You can find its changes [documented below](#060---2020-06-01).
- Fix `widget::Either` using the wrong paint insets ([#1299] by [@andrewhickman])
- Various fixes to cross-platform menus ([#1306] by [@raphlinus])
- Improve Windows 7 DXGI compatibility ([#1311] by [@raphlinus])
- Fixed `Either` not passing events to its hidden child correctly. ([#1351] by [@andrewhickman])
- Don't drop events while showing file dialogs ([#1302], [#1328] by [@jneem])

### Visual
Expand Down Expand Up @@ -520,6 +522,7 @@ Last release without a changelog :(
[#1326]: https://github.com/linebender/druid/pull/1326
[#1328]: https://github.com/linebender/druid/pull/1328
[#1346]: https://github.com/linebender/druid/pull/1346
[#1351]: https://github.com/linebender/druid/pull/1351

[Unreleased]: https://github.com/linebender/druid/compare/v0.6.0...master
[0.6.0]: https://github.com/linebender/druid/compare/v0.5.0...v0.6.0
Expand Down
22 changes: 7 additions & 15 deletions druid/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,21 +550,13 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
}

// log if we seem not to be laid out when we should be
if self.state.layout_rect.is_none() {
match event {
Event::Internal(_) => (),
Event::Timer(_) => (),
Event::WindowConnected => (),
Event::WindowSize(_) => (),
_ => {
log::warn!(
"Widget '{}' received an event ({:?}) without having been laid out. \
This likely indicates a missed call to set_layout_rect.",
self.inner.type_name(),
event,
);
}
}
if self.state.layout_rect.is_none() && !event.should_propagate_to_hidden() {
log::warn!(
"Widget '{}' received an event ({:?}) without having been laid out. \
This likely indicates a missed call to set_layout_rect.",
self.inner.type_name(),
event,
);
}

// TODO: factor as much logic as possible into monomorphic functions.
Expand Down
32 changes: 32 additions & 0 deletions druid/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,38 @@ impl Event {
_ => Some(self.clone()),
}
}

/// Whether this event should be sent to widgets which are currently not visible
/// (for example the hidden tabs in a tabs widget).
pub fn should_propagate_to_hidden(&self) -> bool {
match self {
Event::WindowConnected
| Event::WindowSize(_)
| Event::Timer(_)
| Event::AnimFrame(_)
| Event::Command(_)
| Event::Internal(_) => true,
Event::MouseDown(_)
| Event::MouseUp(_)
| Event::MouseMove(_)
| Event::Wheel(_)
| Event::KeyDown(_)
| Event::KeyUp(_)
| Event::Paste(_)
| Event::Zoom(_) => false,
}
}
}

impl LifeCycle {
/// Whether this event should be sent to widgets which are currently not visible
/// (for example the hidden tabs in a tabs widget).
pub fn should_propagate_to_hidden(&self) -> bool {
match self {
LifeCycle::WidgetAdded | LifeCycle::Internal(_) => true,
LifeCycle::Size(_) | LifeCycle::HotChanged(_) | LifeCycle::FocusChanged(_) => false,
}
}
}

#[cfg(test)]
Expand Down
50 changes: 25 additions & 25 deletions druid/src/widget/either.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,25 @@ impl<T> Either<T> {

impl<T: Data> Widget<T> for Either<T> {
fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
if self.current {
self.true_branch.event(ctx, event, data, env)
if event.should_propagate_to_hidden() {
self.true_branch.event(ctx, event, data, env);
self.false_branch.event(ctx, event, data, env);
} else {
self.false_branch.event(ctx, event, data, env)
self.current_widget().event(ctx, event, data, env)
}
}

fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) {
if let LifeCycle::WidgetAdded = event {
self.current = (self.closure)(data, env);
}
self.true_branch.lifecycle(ctx, event, data, env);
self.false_branch.lifecycle(ctx, event, data, env);

if event.should_propagate_to_hidden() {
self.true_branch.lifecycle(ctx, event, data, env);
self.false_branch.lifecycle(ctx, event, data, env);
} else {
self.current_widget().lifecycle(ctx, event, data, env)
}
}

fn update(&mut self, ctx: &mut UpdateCtx, _old_data: &T, data: &T, env: &Env) {
Expand All @@ -67,34 +73,28 @@ impl<T: Data> Widget<T> for Either<T> {
self.current = current;
ctx.request_layout();
}
if self.current {
self.true_branch.update(ctx, data, env);
} else {
self.false_branch.update(ctx, data, env);
}
self.current_widget().update(ctx, data, env)
}

fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size {
if self.current {
let size = self.true_branch.layout(ctx, bc, data, env);
self.true_branch
.set_layout_rect(ctx, data, env, size.to_rect());
ctx.set_paint_insets(self.true_branch.paint_insets());
size
} else {
let size = self.false_branch.layout(ctx, bc, data, env);
self.false_branch
.set_layout_rect(ctx, data, env, size.to_rect());
ctx.set_paint_insets(self.false_branch.paint_insets());
size
}
let current_widget = self.current_widget();
let size = current_widget.layout(ctx, bc, data, env);
current_widget.set_layout_rect(ctx, data, env, size.to_rect());
ctx.set_paint_insets(current_widget.paint_insets());
size
}

fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
self.current_widget().paint(ctx, data, env)
}
}

impl<T> Either<T> {
fn current_widget(&mut self) -> &mut WidgetPod<T, Box<dyn Widget<T>>> {
if self.current {
self.true_branch.paint_raw(ctx, data, env);
&mut self.true_branch
} else {
self.false_branch.paint_raw(ctx, data, env);
&mut self.false_branch
}
}
}
32 changes: 2 additions & 30 deletions druid/src/widget/tabs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,37 +550,9 @@ impl<TP: TabsPolicy> TabsBody<TP> {
}
}

/// Possibly should be moved to Event
fn hidden_should_receive_event(evt: &Event) -> bool {
match evt {
Event::WindowConnected
| Event::WindowSize(_)
| Event::Timer(_)
| Event::AnimFrame(_)
| Event::Command(_)
| Event::Internal(_) => true,
Event::MouseDown(_)
| Event::MouseUp(_)
| Event::MouseMove(_)
| Event::Wheel(_)
| Event::KeyDown(_)
| Event::KeyUp(_)
| Event::Paste(_)
| Event::Zoom(_) => false,
}
}

/// Possibly should be moved to Lifecycle.
fn hidden_should_receive_lifecycle(lc: &LifeCycle) -> bool {
match lc {
LifeCycle::WidgetAdded | LifeCycle::Internal(_) => true,
LifeCycle::Size(_) | LifeCycle::HotChanged(_) | LifeCycle::FocusChanged(_) => false,
}
}

impl<TP: TabsPolicy> Widget<TabsState<TP>> for TabsBody<TP> {
fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut TabsState<TP>, env: &Env) {
if hidden_should_receive_event(event) {
if event.should_propagate_to_hidden() {
for child in self.child_pods() {
child.event(ctx, event, &mut data.inner, env);
}
Expand Down Expand Up @@ -612,7 +584,7 @@ impl<TP: TabsPolicy> Widget<TabsState<TP>> for TabsBody<TP> {
ctx.request_layout();
}

if hidden_should_receive_lifecycle(event) {
if event.should_propagate_to_hidden() {
for child in self.child_pods() {
child.lifecycle(ctx, event, &data.inner, env);
}
Expand Down