linebender / linebender/druid

Documentation on when to send WidgetAdded events to children

Open
#1,329 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

docs
Dominant language
Rust
Stars
9.7k
Forks
565
PR merge metrics
No merged PRs in 30d

Description

When I replace the child of a `Controller` and call `ctx.children_changed()` the new child never receives a `WidgetAdded` event (first version of the code below). If I've understood the code correctly the fix is to listen for `InternalLifeCycle::RouteWidgetAdded` lifecycle events in the controller and route them to the child (i.e. the modifications in the final code block).

Assuming this is the intended behavior, it should probably be documented somewhere, perhaps in the documentation of `Controller`. It would also be nice if the error message included this as a potential cause (but I'm not sure if that's possible to fix generally for all widgets). The current error message is as follows:

```
thread 'main' panicked at 'TextLayout::layout_metrics called without rebuilding layout object. Text was ''', /home/greg/apps/druid/druid/src/text/layout.rs:214:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```

If I can generally get a confirmation that the documentation I'm suggesting would be a good idea, and that the updated version of the code is legal and reasonable idiomatic, I'd be happy to submit a pr (or I'd be equally happy if someone else updated it).

A bit of an aside, but #1259 unfortunately does not fix this issue despite being closely related. Running on #1259 merged into master I get the same error message as above. Running on on #1259 directly instead of crashing entirely I get the following warning and no text appearing

```
WARN [druid::widget::label] Label text changed without call to update. See LabelAdapter::set_text for information.
```

Test code:

```rust
use druid::*;
use druid::widget::*;

const CHANGE_VIEW: Selector<()> = Selector::new("change_view");

fn controlled_widget() -> ControllerHost, ViewController> {
ControllerHost::new(Label::new("Original"), ViewController)
}

struct Delegate;
impl AppDelegate for Delegate {
fn event(&mut self, ctx: &mut DelegateCtx, window_id: WindowId, event: Event, _: &mut u32, _: &Env) -> Option {
if let Event::KeyDown(_) = event {
ctx.submit_command(Command::new(CHANGE_VIEW, (), window_id));
None
}
else {
Some(event)
}
}
}

struct ViewController;
impl Controller> for ViewController {
fn event(&mut self, child: &mut Label, ctx: &mut EventCtx, event: &Event, data: &mut u32, env: &Env) {
if let Event::Command(ref cmd) = *event {
if let Some(()) = cmd.get(CHANGE_VIEW) {
*child =Label::new("New Label");
ctx.children_changed();
return;
}
}

child.event(ctx, event, data, env)
}
}

fn main() -> Result<(), PlatformError> {
AppLauncher::with_window(WindowDesc::new(controlled_widget))
.delegate(Delegate)
.use_simple_logger()
.launch(0)
}
```

Replacing `ViewController` with the following fixes the error

```rust
struct ViewController {
child_replaced: bool
}
impl Controller> for ViewController {
fn event(&mut self, child: &mut Label, ctx: &mut EventCtx, event: &Event, data: &mut u32, env: &Env) {
if let Event::Command(ref cmd) = *event {
if let Some(()) = cmd.get(CHANGE_VIEW) {
*child = Label::new("New Label");
self.child_replaced = true;
ctx.children_changed();
return;
}
}

child.event(ctx, event, data, env)
}

fn lifecycle(&mut self, child: &mut Label, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &u32, env: &Env) {
if self.child_replaced {
if let LifeCycle::Internal(InternalLifeCycle::RouteWidgetAdded) = event {
self.child_replaced = false;
child.lifecycle(ctx, &LifeCycle::WidgetAdded, data, env);
}
}

child.lifecycle(ctx, event, data, env);
}
}
...

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the Controller documentation and the lifecycle handling described in the issue; the reproduction and druid/src/text/layout.rs provide context for the observed failure. Confirm the intended WidgetAdded behavior and determine where Controller guidance belongs. Done means the replacement-child lifecycle behavior is documented, with any error-message change treated as a separate scope decision.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
documentation
Issue type
Documentation
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.