diff --git a/CHANGELOG.md b/CHANGELOG.md index fa62f99d3b..a0ad27a4e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ This means that druid no longer requires cairo on macOS and uses Core Graphics i - `WidgetExt::debug_widget_id`, for displaying widget ids on hover. ([#876] by [@cmyr]) - `im` feature, with `Data` support for the [`im` crate](https://docs.rs/im/) collections. ([#924] by [@cmyr]) - `im::Vector` support for the `List` widget. ([#940] by [@xStrom]) +- `LifeCycle::Size` event to inform widgets that their size changed. ([#953] by [@xStrom]) ### Changed @@ -194,6 +195,7 @@ This means that druid no longer requires cairo on macOS and uses Core Graphics i [#942]: https://github.com/xi-editor/druid/pull/942 [#943]: https://github.com/xi-editor/druid/pull/943 [#951]: https://github.com/xi-editor/druid/pull/951 +[#953]: https://github.com/xi-editor/druid/pull/953 ## [0.5.0] - 2020-04-01 diff --git a/druid/src/core.rs b/druid/src/core.rs index f93331b6e6..2c56950d54 100644 --- a/druid/src/core.rs +++ b/druid/src/core.rs @@ -188,13 +188,37 @@ impl> WidgetPod { self.state.id } - /// Set layout rectangle. + /// Set the layout [`Rect`]. /// - /// Intended to be called on child widget in container's `layout` - /// implementation. + /// A container widget should call the [`Widget::layout`] method on its children in + /// its own [`Widget::layout`] implementation, then possibly modify the returned [`Size`], and + /// finally call this `set_layout_rect` method on the child to set the final layout [`Rect`]. + /// + /// The child will receive the [`LifeCycle::Size`] event informing them of the final [`Size`]. + /// + /// [`Widget::layout`]: trait.Widget.html#tymethod.layout + /// [`Rect`]: struct.Rect.html + /// [`Size`]: struct.Size.html + /// [`LifeCycle::Size`]: enum.LifeCycle.html#variant.Size pub fn set_layout_rect(&mut self, ctx: &mut LayoutCtx, data: &T, env: &Env, layout_rect: Rect) { + let mut needs_merge = false; + + let old_size = self.state.layout_rect.map(|r| r.size()); + let new_size = layout_rect.size(); + self.state.layout_rect = Some(layout_rect); + if old_size.is_none() || old_size.unwrap() != new_size { + let mut child_ctx = LifeCycleCtx { + command_queue: ctx.command_queue, + base_state: &mut self.state, + window_id: ctx.window_id, + }; + let size_event = LifeCycle::Size(new_size); + self.inner.lifecycle(&mut child_ctx, &size_event, data, env); + needs_merge = true; + } + if WidgetPod::set_hot_state( &mut self.inner, ctx.command_queue, @@ -205,6 +229,10 @@ impl> WidgetPod { data, env, ) { + needs_merge = true; + } + + if needs_merge { ctx.base_state.merge_up(&self.state); } } @@ -215,9 +243,12 @@ impl> WidgetPod { self.layout_rect() } - /// The layout rectangle. + /// Returns the layout [`Rect`]. + /// + /// This will be the same [`Rect`] that was set by [`set_layout_rect`]. /// - /// This will be same value as set by `set_layout_rect`. + /// [`Rect`]: struct.Rect.html + /// [`set_layout_rect`]: #method.set_layout_rect pub fn layout_rect(&self) -> Rect { self.state.layout_rect.unwrap_or_default() } @@ -790,6 +821,11 @@ impl> WidgetPod { true } + LifeCycle::Size(_) => { + // We are a descendant of a widget that received the Size event. + // This event was meant only for our parent, so don't recurse. + false + } //NOTE: this is not sent here, but from the special set_hot_state method LifeCycle::HotChanged(_) => false, LifeCycle::FocusChanged(_) => { diff --git a/druid/src/event.rs b/druid/src/event.rs index 356db85ada..4e0f76d82c 100644 --- a/druid/src/event.rs +++ b/druid/src/event.rs @@ -178,6 +178,14 @@ pub enum LifeCycle { /// [`WidgetPod`]: struct.WidgetPod.html /// [`LifeCycleCtx::register_for_focus`]: struct.LifeCycleCtx.html#method.register_for_focus WidgetAdded, + /// Called when the size of the widget changes. + /// + /// The [`Size`] is derived from the [`Rect`] that was set with [`WidgetPod::set_layout_rect`]. + /// + /// [`Size`]: struct.Size.html + /// [`Rect`]: struct.Rect.html + /// [`WidgetPod::set_layout_rect`]: struct.WidgetPod.html#method.set_layout_rect + Size(Size), /// Called at the beginning of a new animation frame. /// /// On the first frame when transitioning from idle to animating, `interval`