Skip to content

Commit a0aa0a6

Browse files
committed
Add documentation, e.g. around bloom usage.
1 parent b9748fc commit a0aa0a6

File tree

8 files changed

+83
-64
lines changed

8 files changed

+83
-64
lines changed

druid-shell/src/common_util.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl<F: FnOnce(&dyn Any) + Send> IdleCallback for F {
5555
///
5656
/// This can be used safely from multiple threads.
5757
///
58-
/// The counter will overflow if `next()` iscalled 2^64 - 2 times.
58+
/// The counter will overflow if `next()` is called 2^64 - 2 times.
5959
/// If this is possible for your application, and reuse would be undesirable,
6060
/// use something else.
6161
pub struct Counter(AtomicU64);

druid/src/bloom.rs

+14-12
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,12 @@ impl<T: ?Sized + Hash> Bloom<T> {
7171
self.entry_count += 1;
7272
}
7373

74-
/// Return whether an item exists in the filter.
74+
/// Returns `true` if the item may have been added to the filter.
7575
///
7676
/// This can return false positives, but never false negatives.
77-
pub fn contains(&self, item: &T) -> bool {
77+
/// Thus `true` means that the item may have been added - or not,
78+
/// while `false` means that the item has definitely not been added.
79+
pub fn may_contain(&self, item: &T) -> bool {
7880
let mask = self.make_bit_mask(item);
7981
self.bits & mask == mask
8082
}
@@ -134,11 +136,11 @@ mod tests {
134136
let mut bloom = Bloom::default();
135137
for i in 0..100 {
136138
bloom.add(&i);
137-
assert!(bloom.contains(&i));
139+
assert!(bloom.may_contain(&i));
138140
}
139141
bloom.clear();
140142
for i in 0..100 {
141-
assert!(!bloom.contains(&i));
143+
assert!(!bloom.may_contain(&i));
142144
}
143145
}
144146

@@ -147,18 +149,18 @@ mod tests {
147149
let mut bloom1 = Bloom::default();
148150
bloom1.add(&0);
149151
bloom1.add(&1);
150-
assert!(!bloom1.contains(&2));
151-
assert!(!bloom1.contains(&3));
152+
assert!(!bloom1.may_contain(&2));
153+
assert!(!bloom1.may_contain(&3));
152154
let mut bloom2 = Bloom::default();
153155
bloom2.add(&2);
154156
bloom2.add(&3);
155-
assert!(!bloom2.contains(&0));
156-
assert!(!bloom2.contains(&1));
157+
assert!(!bloom2.may_contain(&0));
158+
assert!(!bloom2.may_contain(&1));
157159

158160
let bloom3 = bloom1.union(bloom2);
159-
assert!(bloom3.contains(&0));
160-
assert!(bloom3.contains(&1));
161-
assert!(bloom3.contains(&2));
162-
assert!(bloom3.contains(&3));
161+
assert!(bloom3.may_contain(&0));
162+
assert!(bloom3.may_contain(&1));
163+
assert!(bloom3.may_contain(&2));
164+
assert!(bloom3.may_contain(&3));
163165
}
164166
}

druid/src/contexts.rs

+21-17
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ impl<'a> EventCtx<'a> {
238238
self.is_handled
239239
}
240240

241-
/// The focus status of a widget.
241+
/// The (tree) focus status of a widget.
242242
///
243243
/// Focus means that the widget receives keyboard events.
244244
///
@@ -251,20 +251,21 @@ impl<'a> EventCtx<'a> {
251251
/// hierarchy, all ancestors of that leaf widget are also invoked with
252252
/// `FocusChanged(true)`.
253253
///
254-
/// Discussion question: is "is_focused" a better name?
254+
/// Discussion question: is "may_have_focus" a better name?
255255
///
256256
/// [`request_focus`]: struct.EventCtx.html#method.request_focus
257257
pub fn has_focus(&self) -> bool {
258+
// The bloom filter we're checking can return false positives.
258259
let is_child = self
259260
.focus_widget
260-
.map(|id| self.base_state.children.contains(&id))
261+
.map(|id| self.base_state.children.may_contain(&id))
261262
.unwrap_or(false);
262263
is_child || self.focus_widget == Some(self.widget_id())
263264
}
264265

265-
/// The (leaf) focus status of a widget. See [`has_focus`].
266+
/// The (leaf) focus status of a widget.
266267
///
267-
/// [`has_focus`]: struct.EventCtx.html#method.has_focus
268+
/// See [`has_focus`](struct.EventCtx.html#method.has_focus).
268269
pub fn is_focused(&self) -> bool {
269270
self.focus_widget == Some(self.widget_id())
270271
}
@@ -282,7 +283,7 @@ impl<'a> EventCtx<'a> {
282283
///
283284
/// This should only be called by a widget that currently has focus.
284285
pub fn focus_next(&mut self) {
285-
if self.focus_widget == Some(self.widget_id()) {
286+
if self.is_focused() {
286287
self.base_state.request_focus = Some(FocusChange::Next);
287288
} else {
288289
log::warn!("focus_next can only be called by the currently focused widget");
@@ -293,7 +294,7 @@ impl<'a> EventCtx<'a> {
293294
///
294295
/// This should only be called by a widget that currently has focus.
295296
pub fn focus_prev(&mut self) {
296-
if self.focus_widget == Some(self.widget_id()) {
297+
if self.is_focused() {
297298
self.base_state.request_focus = Some(FocusChange::Previous);
298299
} else {
299300
log::warn!("focus_prev can only be called by the currently focused widget");
@@ -304,7 +305,7 @@ impl<'a> EventCtx<'a> {
304305
///
305306
/// This should only be called by a widget that currently has focus.
306307
pub fn resign_focus(&mut self) {
307-
if self.focus_widget == Some(self.widget_id()) {
308+
if self.is_focused() {
308309
self.base_state.request_focus = Some(FocusChange::Resign);
309310
} else {
310311
log::warn!("resign_focus can only be called by the currently focused widget");
@@ -414,6 +415,8 @@ impl<'a> LifeCycleCtx<'a> {
414415
}
415416

416417
/// Register this widget to be eligile to accept focus automatically.
418+
///
419+
/// This should only be called in response to a `LifeCycle::WidgetAdded` event.
417420
pub fn register_for_focus(&mut self) {
418421
self.base_state.focus_chain.push(self.widget_id());
419422
}
@@ -557,24 +560,25 @@ impl<'a, 'b: 'a> PaintCtx<'a, 'b> {
557560
self.base_state.size()
558561
}
559562

560-
/// Query the focus state of the widget.
561-
///
562-
/// This is true only if this widget has focus.
563-
pub fn is_focused(&self) -> bool {
564-
self.focus_widget == Some(self.widget_id())
565-
}
566-
567-
/// The focus status of a widget.
563+
/// The (tree) focus status of a widget.
568564
///
569565
/// See [`has_focus`](struct.EventCtx.html#method.has_focus).
570566
pub fn has_focus(&self) -> bool {
567+
// The bloom filter we're checking can return false positives.
571568
let is_child = self
572569
.focus_widget
573-
.map(|id| self.base_state.children.contains(&id))
570+
.map(|id| self.base_state.children.may_contain(&id))
574571
.unwrap_or(false);
575572
is_child || self.focus_widget == Some(self.widget_id())
576573
}
577574

575+
/// The (leaf) focus status of a widget.
576+
///
577+
/// See [`has_focus`](struct.EventCtx.html#method.has_focus).
578+
pub fn is_focused(&self) -> bool {
579+
self.focus_widget == Some(self.widget_id())
580+
}
581+
578582
/// Returns the currently visible [`Region`].
579583
///
580584
/// [`Region`]: struct.Region.html

druid/src/core.rs

+19-15
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
369369
let mut child_ctx = EventCtx {
370370
cursor: ctx.cursor,
371371
command_queue: ctx.command_queue,
372-
window: &ctx.window,
372+
window: ctx.window,
373373
window_id: ctx.window_id,
374374
base_state: &mut self.state,
375375
had_active,
@@ -446,7 +446,9 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
446446
Target::Window(_) => Event::Command(cmd.clone()),
447447
Target::Widget(id) if *id == child_ctx.widget_id() => Event::Command(cmd.clone()),
448448
Target::Widget(id) => {
449-
recurse = child_ctx.base_state.children.contains(id);
449+
// Recurse when the target widget could be our descendant.
450+
// The bloom filter we're checking can return false positives.
451+
recurse = child_ctx.base_state.children.may_contain(id);
450452
Event::TargetedCommand(*target, cmd.clone())
451453
}
452454
Target::Global => panic!("Target::Global should be converted before WidgetPod"),
@@ -496,7 +498,6 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
496498
self.state.children.clear();
497499
self.state.focus_chain.clear();
498500
}
499-
500501
self.state.children_changed
501502
}
502503
}
@@ -517,8 +518,10 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
517518
self.inner.lifecycle(ctx, &event, data, env);
518519
false
519520
} else {
520-
old.map(|id| self.state.children.contains(&id))
521-
.or_else(|| new.map(|id| self.state.children.contains(&id)))
521+
// Recurse when the target widgets could be our descendants.
522+
// The bloom filter we're checking can return false positives.
523+
old.map(|id| self.state.children.may_contain(&id))
524+
.or_else(|| new.map(|id| self.state.children.may_contain(&id)))
522525
.unwrap_or(false)
523526
}
524527
}
@@ -532,7 +535,9 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
532535
state_cell.set(self.state.clone());
533536
false
534537
} else {
535-
self.state.children.contains(&widget)
538+
// Recurse when the target widget could be our descendant.
539+
// The bloom filter we're checking can return false positives.
540+
self.state.children.may_contain(&widget)
536541
}
537542
}
538543
#[cfg(test)]
@@ -542,13 +547,12 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
542547
}
543548
};
544549

545-
let mut child_ctx = LifeCycleCtx {
546-
command_queue: ctx.command_queue,
547-
base_state: &mut self.state,
548-
window_id: ctx.window_id,
549-
};
550-
551550
if recurse {
551+
let mut child_ctx = LifeCycleCtx {
552+
command_queue: ctx.command_queue,
553+
base_state: &mut self.state,
554+
window_id: ctx.window_id,
555+
};
552556
self.inner.lifecycle(&mut child_ctx, event, data, env);
553557
}
554558

@@ -691,9 +695,9 @@ mod tests {
691695
let env = Env::default();
692696

693697
widget.lifecycle(&mut ctx, &LifeCycle::WidgetAdded, &None, &env);
694-
assert!(ctx.base_state.children.contains(&ID_1));
695-
assert!(ctx.base_state.children.contains(&ID_2));
696-
assert!(ctx.base_state.children.contains(&ID_3));
698+
assert!(ctx.base_state.children.may_contain(&ID_1));
699+
assert!(ctx.base_state.children.may_contain(&ID_2));
700+
assert!(ctx.base_state.children.may_contain(&ID_3));
697701
assert_eq!(ctx.base_state.children.entry_count(), 7);
698702
}
699703
}

druid/src/tests/mod.rs

+13-13
Original file line numberDiff line numberDiff line change
@@ -275,13 +275,13 @@ fn child_tracking() {
275275
harness.send_initial_events();
276276
let root = harness.get_state(id_4);
277277
assert_eq!(root.children.entry_count(), 3);
278-
assert!(root.children.contains(&id_1));
279-
assert!(root.children.contains(&id_2));
280-
assert!(root.children.contains(&id_3));
278+
assert!(root.children.may_contain(&id_1));
279+
assert!(root.children.may_contain(&id_2));
280+
assert!(root.children.may_contain(&id_3));
281281

282282
let split = harness.get_state(id_3);
283-
assert!(split.children.contains(&id_1));
284-
assert!(split.children.contains(&id_2));
283+
assert!(split.children.may_contain(&id_1));
284+
assert!(split.children.may_contain(&id_2));
285285
assert_eq!(split.children.entry_count(), 2);
286286
});
287287
}
@@ -302,18 +302,18 @@ fn register_after_adding_child() {
302302
Harness::create(String::new(), widget, |harness| {
303303
harness.send_initial_events();
304304

305-
assert!(harness.get_state(id_5).children.contains(&id_6));
306-
assert!(harness.get_state(id_5).children.contains(&id_1));
307-
assert!(harness.get_state(id_5).children.contains(&id_4));
305+
assert!(harness.get_state(id_5).children.may_contain(&id_6));
306+
assert!(harness.get_state(id_5).children.may_contain(&id_1));
307+
assert!(harness.get_state(id_5).children.may_contain(&id_4));
308308
assert_eq!(harness.get_state(id_5).children.entry_count(), 3);
309309

310310
harness.submit_command(REPLACE_CHILD, None);
311311

312-
assert!(harness.get_state(id_5).children.contains(&id_6));
313-
assert!(harness.get_state(id_5).children.contains(&id_4));
314-
assert!(harness.get_state(id_5).children.contains(&id_7));
315-
assert!(harness.get_state(id_5).children.contains(&id_2));
316-
assert!(harness.get_state(id_5).children.contains(&id_3));
312+
assert!(harness.get_state(id_5).children.may_contain(&id_6));
313+
assert!(harness.get_state(id_5).children.may_contain(&id_4));
314+
assert!(harness.get_state(id_5).children.may_contain(&id_7));
315+
assert!(harness.get_state(id_5).children.may_contain(&id_2));
316+
assert!(harness.get_state(id_5).children.may_contain(&id_3));
317317
assert_eq!(harness.get_state(id_5).children.entry_count(), 5);
318318
})
319319
}

druid/src/widget/textbox.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ impl Widget<String> for TextBox {
240240

241241
match event {
242242
Event::MouseDown(mouse) => {
243-
if !ctx.has_focus() {
243+
if !ctx.is_focused() {
244244
ctx.request_focus();
245245
}
246246
ctx.set_active(true);
@@ -281,7 +281,7 @@ impl Widget<String> for TextBox {
281281
}
282282
}
283283
Event::Command(ref cmd)
284-
if ctx.has_focus()
284+
if ctx.is_focused()
285285
&& (cmd.selector == crate::commands::COPY
286286
|| cmd.selector == crate::commands::CUT) =>
287287
{
@@ -392,9 +392,9 @@ impl Widget<String> for TextBox {
392392
let placeholder_color = env.get(theme::PLACEHOLDER_COLOR);
393393
let cursor_color = env.get(theme::CURSOR_COLOR);
394394

395-
let has_focus = ctx.has_focus();
395+
let is_focused = ctx.is_focused();
396396

397-
let border_color = if has_focus {
397+
let border_color = if is_focused {
398398
env.get(theme::PRIMARY_LIGHT)
399399
} else {
400400
env.get(theme::BORDER_DARK)
@@ -449,7 +449,7 @@ impl Widget<String> for TextBox {
449449
rc.draw_text(&text_layout, text_pos, color);
450450

451451
// Paint the cursor if focused and there's no selection
452-
if has_focus && self.cursor_on && self.selection.is_caret() {
452+
if is_focused && self.cursor_on && self.selection.is_caret() {
453453
let cursor_x = self.x_for_offset(&text_layout, self.cursor());
454454
let xy = text_pos + Vec2::new(cursor_x, 2. - font_size);
455455
let x2y2 = xy + Vec2::new(0., font_size + 2.);

druid/src/widget/widget.rs

+5
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ use super::prelude::*;
4646
/// [`LifeCycleCtx::widget_id`]: struct.LifeCycleCtx.html#method.widget_id
4747
/// [`WidgetExt::with_id`]: trait.WidgetExt.html#method.with_id
4848
/// [`IdentityWrapper`]: widget/struct.IdentityWrapper.html
49+
//
50+
// The widget id does not necessarily identify only a single widget
51+
// in the strictest sense. Internally WidgetPod uses its child's id,
52+
// and the window base state uses the root widget's id.
53+
//
4954
// this is NonZeroU64 because we regularly store Option<WidgetId>
5055
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
5156
pub struct WidgetId(NonZeroU64);

druid/src/window.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,12 @@ impl<T: Data> Window<T> {
7272
&self.root.state().focus_chain
7373
}
7474

75+
/// Returns `true` if the provided widget may be in this window,
76+
/// but it may also be a false positive.
77+
/// However when this returns `false` the widget is definitely not in this window.
7578
pub(crate) fn may_contain_widget(&self, widget_id: WidgetId) -> bool {
76-
self.root.state().children.contains(&widget_id)
79+
// The bloom filter we're checking can return false positives.
80+
self.root.state().children.may_contain(&widget_id)
7781
}
7882

7983
pub(crate) fn set_menu(&mut self, mut menu: MenuDesc<T>, data: &T, env: &Env) {

0 commit comments

Comments
 (0)