Skip to content

Commit 155783c

Browse files
authored
Merge branch 'master' into fix-progressbar-rendering
2 parents b93ddf5 + 4af2709 commit 155783c

File tree

9 files changed

+134
-46
lines changed

9 files changed

+134
-46
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@ This means that druid no longer requires cairo on macOS and uses Core Graphics i
4242
- `MouseButtons` to `MouseEvent` to track which buttons are being held down during an event. ([#843] by [@xStrom])
4343
- `Env` and `Key` gained methods for inspecting an `Env` at runtime ([#880] by [@Zarenor])
4444
- `UpdateCtx::request_timer` and `UpdateCtx::request_anim_frame`. ([#898] by [@finnerale])
45+
- `LifeCycleCtx::request_timer`. ([#954] by [@xStrom])
4546
- `UpdateCtx::size` and `LifeCycleCtx::size`. ([#917] by [@jneem])
4647
- `WidgetExt::debug_widget_id`, for displaying widget ids on hover. ([#876] by [@cmyr])
4748
- `im` feature, with `Data` support for the [`im` crate](https://docs.rs/im/) collections. ([#924] by [@cmyr])
4849
- `im::Vector` support for the `List` widget. ([#940] by [@xStrom])
50+
- `LifeCycle::Size` event to inform widgets that their size changed. ([#953] by [@xStrom])
4951

5052
### Changed
5153

@@ -95,6 +97,7 @@ This means that druid no longer requires cairo on macOS and uses Core Graphics i
9597
- X11: Support key and mouse button state. ([#920] by [@jneem])
9698
- Routing `LifeCycle::FocusChanged` to descendant widgets. ([#925] by [@yrns])
9799
- Built-in open and save menu items now show the correct label and submit the right commands. ([#930] by [@finnerale])
100+
- Wheel events now properly update hot state. ([#951] by [@xStrom])
98101

99102
### Visual
100103

@@ -194,6 +197,9 @@ This means that druid no longer requires cairo on macOS and uses Core Graphics i
194197
[#942]: https://github.com/xi-editor/druid/pull/942
195198
[#943]: https://github.com/xi-editor/druid/pull/943
196199
[#949]: https://github.com/xi-editor/druid/pull/949
200+
[#951]: https://github.com/xi-editor/druid/pull/951
201+
[#953]: https://github.com/xi-editor/druid/pull/953
202+
[#954]: https://github.com/xi-editor/druid/pull/954
197203

198204
## [0.5.0] - 2020-04-01
199205

Cargo.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

druid/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ fnv = "1.0.3"
3838
instant = { version = "0.1", features = ["wasm-bindgen"] }
3939

4040
# Optional dependencies
41-
im = { version = "14.0", optional = true }
41+
im = { version = "15.0", optional = true }
4242
usvg = { version = "0.9.0", optional = true }
4343
image = { version = "0.23.2", optional = true }
4444

druid/src/app.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,15 @@ impl<T: Data> AppLauncher<T> {
8585
/// Initialize a minimal logger for printing logs out to stderr.
8686
///
8787
/// Meant for use during development only.
88+
///
89+
/// # Panics
90+
///
91+
/// Panics if the logger fails to initialize.
8892
pub fn use_simple_logger(self) -> Self {
8993
#[cfg(not(target_arch = "wasm32"))]
90-
simple_logger::init().ok();
94+
simple_logger::init().expect("Failed to init simple logger");
9195
#[cfg(target_arch = "wasm32")]
92-
console_log::init_with_level(log::Level::Trace).ok();
96+
console_log::init_with_level(log::Level::Trace).expect("Failed to init simple logger");
9397
self
9498
}
9599

druid/src/contexts.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ pub struct LifeCycleCtx<'a> {
6262
pub(crate) command_queue: &'a mut CommandQueue,
6363
pub(crate) base_state: &'a mut BaseState,
6464
pub(crate) window_id: WindowId,
65+
pub(crate) window: &'a WindowHandle,
6566
}
6667

6768
/// A mutable context provided to data update methods of widgets.
@@ -91,6 +92,7 @@ pub struct LayoutCtx<'a, 'b: 'a> {
9192
pub(crate) base_state: &'a mut BaseState,
9293
pub(crate) text_factory: &'a mut Text<'b>,
9394
pub(crate) window_id: WindowId,
95+
pub(crate) window: &'a WindowHandle,
9496
pub(crate) mouse_pos: Option<Point>,
9597
}
9698

@@ -548,6 +550,17 @@ impl<'a> LifeCycleCtx<'a> {
548550
self.request_paint();
549551
}
550552

553+
/// Request a timer event.
554+
///
555+
/// The return value is a token, which can be used to associate the
556+
/// request with the event.
557+
pub fn request_timer(&mut self, deadline: Duration) -> TimerToken {
558+
self.base_state.request_timer = true;
559+
let timer_token = self.window.request_timer(deadline);
560+
self.base_state.add_timer(timer_token);
561+
timer_token
562+
}
563+
551564
/// The layout size.
552565
///
553566
/// This is the layout size as ultimately determined by the parent

druid/src/core.rs

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::piet::{
2424
use crate::{
2525
BoxConstraints, Color, Command, Data, Env, Event, EventCtx, InternalEvent, InternalLifeCycle,
2626
LayoutCtx, LifeCycle, LifeCycleCtx, PaintCtx, Region, Target, TimerToken, UpdateCtx, Widget,
27-
WidgetId, WindowId,
27+
WidgetId, WindowHandle, WindowId,
2828
};
2929

3030
/// Our queue type
@@ -188,23 +188,53 @@ impl<T, W: Widget<T>> WidgetPod<T, W> {
188188
self.state.id
189189
}
190190

191-
/// Set layout rectangle.
191+
/// Set the layout [`Rect`].
192192
///
193-
/// Intended to be called on child widget in container's `layout`
194-
/// implementation.
193+
/// A container widget should call the [`Widget::layout`] method on its children in
194+
/// its own [`Widget::layout`] implementation, then possibly modify the returned [`Size`], and
195+
/// finally call this `set_layout_rect` method on the child to set the final layout [`Rect`].
196+
///
197+
/// The child will receive the [`LifeCycle::Size`] event informing them of the final [`Size`].
198+
///
199+
/// [`Widget::layout`]: trait.Widget.html#tymethod.layout
200+
/// [`Rect`]: struct.Rect.html
201+
/// [`Size`]: struct.Size.html
202+
/// [`LifeCycle::Size`]: enum.LifeCycle.html#variant.Size
195203
pub fn set_layout_rect(&mut self, ctx: &mut LayoutCtx, data: &T, env: &Env, layout_rect: Rect) {
204+
let mut needs_merge = false;
205+
206+
let old_size = self.state.layout_rect.map(|r| r.size());
207+
let new_size = layout_rect.size();
208+
196209
self.state.layout_rect = Some(layout_rect);
197210

211+
if old_size.is_none() || old_size.unwrap() != new_size {
212+
let mut child_ctx = LifeCycleCtx {
213+
command_queue: ctx.command_queue,
214+
base_state: &mut self.state,
215+
window_id: ctx.window_id,
216+
window: ctx.window,
217+
};
218+
let size_event = LifeCycle::Size(new_size);
219+
self.inner.lifecycle(&mut child_ctx, &size_event, data, env);
220+
needs_merge = true;
221+
}
222+
198223
if WidgetPod::set_hot_state(
199224
&mut self.inner,
200225
ctx.command_queue,
201226
&mut self.state,
202227
ctx.window_id,
228+
ctx.window,
203229
layout_rect,
204230
ctx.mouse_pos,
205231
data,
206232
env,
207233
) {
234+
needs_merge = true;
235+
}
236+
237+
if needs_merge {
208238
ctx.base_state.merge_up(&self.state);
209239
}
210240
}
@@ -215,9 +245,12 @@ impl<T, W: Widget<T>> WidgetPod<T, W> {
215245
self.layout_rect()
216246
}
217247

218-
/// The layout rectangle.
248+
/// Returns the layout [`Rect`].
219249
///
220-
/// This will be same value as set by `set_layout_rect`.
250+
/// This will be the same [`Rect`] that was set by [`set_layout_rect`].
251+
///
252+
/// [`Rect`]: struct.Rect.html
253+
/// [`set_layout_rect`]: #method.set_layout_rect
221254
pub fn layout_rect(&self) -> Rect {
222255
self.state.layout_rect.unwrap_or_default()
223256
}
@@ -303,6 +336,7 @@ impl<T, W: Widget<T>> WidgetPod<T, W> {
303336
command_queue: &mut CommandQueue,
304337
child_state: &mut BaseState,
305338
window_id: WindowId,
339+
window: &WindowHandle,
306340
rect: Rect,
307341
mouse_pos: Option<Point>,
308342
data: &T,
@@ -319,6 +353,7 @@ impl<T, W: Widget<T>> WidgetPod<T, W> {
319353
command_queue,
320354
base_state: child_state,
321355
window_id,
356+
window,
322357
};
323358
child.lifecycle(&mut child_ctx, &hot_changed_event, data, env);
324359
// if hot changes and we're showing widget ids, always repaint
@@ -484,6 +519,7 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
484519
command_queue: ctx.command_queue,
485520
base_state: &mut self.state,
486521
window_id: ctx.window_id,
522+
window: ctx.window,
487523
text_factory: ctx.text_factory,
488524
mouse_pos: child_mouse_pos,
489525
};
@@ -569,6 +605,7 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
569605
child_ctx.command_queue,
570606
child_ctx.base_state,
571607
child_ctx.window_id,
608+
child_ctx.window,
572609
rect,
573610
None,
574611
data,
@@ -616,6 +653,7 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
616653
child_ctx.command_queue,
617654
child_ctx.base_state,
618655
child_ctx.window_id,
656+
child_ctx.window,
619657
rect,
620658
Some(mouse_event.pos),
621659
data,
@@ -632,6 +670,7 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
632670
child_ctx.command_queue,
633671
child_ctx.base_state,
634672
child_ctx.window_id,
673+
child_ctx.window,
635674
rect,
636675
Some(mouse_event.pos),
637676
data,
@@ -648,6 +687,7 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
648687
child_ctx.command_queue,
649688
child_ctx.base_state,
650689
child_ctx.window_id,
690+
child_ctx.window,
651691
rect,
652692
Some(mouse_event.pos),
653693
data,
@@ -658,6 +698,23 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
658698
mouse_event.pos -= rect.origin().to_vec2();
659699
Event::MouseMove(mouse_event)
660700
}
701+
Event::Wheel(mouse_event) => {
702+
WidgetPod::set_hot_state(
703+
&mut self.inner,
704+
child_ctx.command_queue,
705+
child_ctx.base_state,
706+
child_ctx.window_id,
707+
child_ctx.window,
708+
rect,
709+
Some(mouse_event.pos),
710+
data,
711+
env,
712+
);
713+
recurse = had_active || child_ctx.base_state.is_hot;
714+
let mut mouse_event = mouse_event.clone();
715+
mouse_event.pos -= rect.origin().to_vec2();
716+
Event::Wheel(mouse_event)
717+
}
661718
Event::KeyDown(e) => {
662719
recurse = child_ctx.has_focus();
663720
Event::KeyDown(*e)
@@ -670,12 +727,6 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
670727
recurse = child_ctx.has_focus();
671728
Event::Paste(e.clone())
672729
}
673-
Event::Wheel(wheel_event) => {
674-
recurse = had_active || child_ctx.base_state.is_hot;
675-
let mut wheel_event = wheel_event.clone();
676-
wheel_event.pos -= rect.origin().to_vec2();
677-
Event::Wheel(wheel_event)
678-
}
679730
Event::Zoom(zoom) => {
680731
recurse = had_active || child_ctx.base_state.is_hot;
681732
Event::Zoom(*zoom)
@@ -780,6 +831,11 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
780831

781832
true
782833
}
834+
LifeCycle::Size(_) => {
835+
// We are a descendant of a widget that received the Size event.
836+
// This event was meant only for our parent, so don't recurse.
837+
false
838+
}
783839
//NOTE: this is not sent here, but from the special set_hot_state method
784840
LifeCycle::HotChanged(_) => false,
785841
LifeCycle::FocusChanged(_) => {
@@ -793,6 +849,7 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
793849
command_queue: ctx.command_queue,
794850
base_state: &mut self.state,
795851
window_id: ctx.window_id,
852+
window: ctx.window,
796853
};
797854

798855
if recurse {
@@ -959,6 +1016,7 @@ mod tests {
9591016
command_queue: &mut command_queue,
9601017
base_state: &mut state,
9611018
window_id: WindowId::next(),
1019+
window: &WindowHandle::default(),
9621020
};
9631021

9641022
let env = Env::default();

druid/src/data.rs

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -381,27 +381,16 @@ impl Data for piet::Color {
381381
}
382382
}
383383

384-
//FIXME Vector::ptr_eq is not currently reliable; this is a temporary impl?
385384
#[cfg(feature = "im")]
386385
impl<T: Data> Data for im::Vector<T> {
387386
fn same(&self, other: &Self) -> bool {
388-
// for reasons outlined in https://github.com/bodil/im-rs/issues/129,
389-
// ptr_eq always returns false for small collections. This heuristic
390-
// falls back to using equality for collections below some threshold.
391-
// There may be a possibility of this returning false negatives, but
392-
// not false positives; that's an acceptable outcome.
393-
394-
/* this is the impl I expected to use
395-
const INLINE_LEN: usize = 48; // bytes available before first allocation;
396-
let inline_capacity: usize = INLINE_LEN / std::mem::size_of::<T>();
397-
if self.len() == other.len() && self.len() <= inline_capacity {
398-
self.iter().zip(other.iter()).all(|(a, b)| a.same(b))
387+
// if a vec is small enough that it doesn't require an allocation
388+
// it is 'inline'; in this case a pointer comparison is meaningless.
389+
if self.is_inline() {
390+
self.len() == other.len() && self.iter().zip(other.iter()).all(|(a, b)| a.same(b))
399391
} else {
400392
self.ptr_eq(other)
401393
}
402-
*/
403-
404-
self.len() == other.len() && self.iter().zip(other.iter()).all(|(a, b)| a.same(b))
405394
}
406395
}
407396

druid/src/event.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,14 @@ pub enum LifeCycle {
178178
/// [`WidgetPod`]: struct.WidgetPod.html
179179
/// [`LifeCycleCtx::register_for_focus`]: struct.LifeCycleCtx.html#method.register_for_focus
180180
WidgetAdded,
181+
/// Called when the size of the widget changes.
182+
///
183+
/// The [`Size`] is derived from the [`Rect`] that was set with [`WidgetPod::set_layout_rect`].
184+
///
185+
/// [`Size`]: struct.Size.html
186+
/// [`Rect`]: struct.Rect.html
187+
/// [`WidgetPod::set_layout_rect`]: struct.WidgetPod.html#method.set_layout_rect
188+
Size(Size),
181189
/// Called at the beginning of a new animation frame.
182190
///
183191
/// On the first frame when transitioning from idle to animating, `interval`

0 commit comments

Comments
 (0)