Pickable doesn't work how I would expect
There are a couple of issues with the Pickable component.
The first is that setting should_block_lower doesn't seem to allow dragging when clicking on a child element. That is, suppose I have an element A with a child B, where A has Pickable set to:
Pickable {
should_block_lower: true,
should_emit_events: true,
}
Now, I have a handler on A which listens to On::<Pointer<Drag>>. If I pick on the part of A that is not covered by B, I get drag events - that is, I am able to drag A normally. However, if I pick on the part of A which contains B, I only get a single Drag event on first click, and no drag events afterwards.
You can repro this in https://github.com/viridia/quill/commits/main by running the example "inset_view", and dragging the splitter widget on the left side of the window. Clicking on the decorative thumb in the middle does not allow dragging, but clicking anywhere else on the splitter bar does.
The other issue is that I'm a bit confused about what should_emit_events does. The reason is that disabling picking for an item ought to have other effects besides disabling events - for example, it should also prevent that item from being the current hover or focus item (not sure what term you use). I'm not sure, by the name, whether this option only inhibits events, or whether it does these other thing as well. In CSS, if you disable picking for an element, it acts like the element does not exist at all in the picking hierarchy.
In my style system, I use a different set of picking options which more closely resembles the way CSS pointer events works:
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PointerEvents {
/// No pointer events for this entity, or its children
None,
/// Pointer events from children only
Children,
/// Pointer events from attached entity but not child elements
SelfOnly,
/// Pointer events from both self and children
All,
}
This is presuming a model of picking which may not match what bevy_mod_picking is doing. When you disable picking of an element in CSS via pointer-events, the relative z-index of the items doesn't matter - the only thing that matters is what level the items are in the DOM hierarchy. So a parent which has a child can disable picking, and that affects both the parent and the child, even if the child is at a higher z-index than the parent. It has no effect on elements that happen to lie under the parent (so long as they are not part of the parent's hierarchy), except that with picking disabled on the parent, the pointer can now shoot "through" the parent and reach those items.
Turns out that part of my problem was on my end, now fixed. However, I'm still getting odd behavior with move in/out, and the second part of the OP is still relevant.
I'll need to take some time to review this in more depth.
The other issue is that I'm a bit confused about what
should_emit_eventsdoes.
I've attempted to document it here: https://docs.rs/bevy_mod_picking/latest/bevy_mod_picking/backend/prelude/struct.Pickable.html#structfield.should_emit_events
The idea is that there are two axes for picking behavior:
- Should this entity block entities below it?
- If this entity is hit, should it emit any picking events, e.g. should it appear "pickable" to users of the API?
should_emit_events = true |
should_emit_events = false |
|
|---|---|---|
should_block_lower = true |
Default picking behavior | Entity is not pickable (no events), but it also blocks entities below it from being picked |
should_block_lower = false |
Entity emits picking events like normal, but it also allows picks to pass through to lower entities | Equivalent to Pickable::IGNORE; entity does not participate in picking at all |
OK, now that I've had a chance to dive into this deeper, I think I can explain my confusion more clearly.
Does should_emit_events, in addition to disabling picking events, also prevent the item from being inserted into the HoverMap? It should: the HoverMap should always be consistent with the history of In & Over events.
Or to phrase it another way: as a user, I need a way to disable picking on an entity. This should have the following effects:
- Prevents that entity from emitting picking events.
- Prevents that entity from ever being inserted into the HoverMap.
- Prevents that entity from displacing other entities already in the HoverMap.
If this is not the case, then should_emit_events is not doing what I need. If it is the case, then the name is kind of confusing, since it's an incomplete description of what it does.
@viridia
Does should_emit_events, in addition to disabling picking events, also prevent the item from being inserted into the HoverMap?
Yes. You can see how that happens here: https://github.com/aevyrie/bevy_mod_picking/blob/1471a9f83435f3fdd43829b90391e6ab965e0e73/crates/bevy_picking_core/src/focus.rs#L140
Or to phrase it another way: as a user, I need a way to disable picking on an entity.
You can use Pickable::IGNORE.
As pointed out by @bonsairobo, I think the desired feature is already available. Perhaps the issue here is naming? Events are derived from the hover map, which is the source of truth for all events.
I guess I am reading more into the naming than I should. Setting should_emit_events doesn't merely suppress events from being emitted: it also prevents the state changes that drive those events. If you're polling states, like I am, and not relying on events, then suppressing events doesn't matter, but the state changes do.
Also, in my UI framework which is built on top of bevy_mod_picking, I don't make a distinction between the two flags you have provided. That is, they are either both set to true, or both set to false. There's no use case that I know of for setting one to true and setting the other to false.
Bottom line is that I am able to work with the system as-is; the purpose of this bug report is to try and clarify, not to ask for a change in behavior.
The first use case that comes to mind is a modal overlay. You don't want the tinted overlay to have any events, but you do want it to block anything underneath it.
Or, maybe you have the inverse - you want a sort of "sensor" that emits events, but allows picks to pass through and also hit things beneath it. We need something like this for CAD-like applications. You want an invisible overlay that can sense where the cursor is, maybe to draw on, but you don't want to prevent clicking on objects in the scene beneath it. This is kinda like bubbling, except it works outside of the entity hierarchy.
I'm updating the docs and field naming in the next release in an attempt to address this feedback, thanks!