1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
use crate::background::*;
use zaplib::*;
/// Popover that can be used for menus, tooltips, etc.
///
/// For more general information about popovers, see
/// <https://uxdesign.cc/pop-up-popover-or-popper-a-quick-look-into-ui-terms-cb4114fca2a>.
///
/// TODO(JP): This currently only supports showing a popover _above_ the current
/// box position. To make this more useful we should add different ways
/// of positioning this.
///
/// TODO(JP): This currently assumes you draw this on top of everything. That is
/// not always practical (e.g. if you want to show a tooltip in a deeply nested
/// widget), and it also doesn't break out of [`View`]s currently, so we might
/// need to use some combination of [`View::is_overlay`] and z-depth. To show
/// tooltips on top of everything while using the scroll position of containing
/// [`View`]s we might need some framework changes.
///
/// TODO(JP): Aligning the popover doesn't actually work for nested [`View`]s yet,
/// like [`crate::TextInput`] or [`crate::TextEditor`]. See [`Cx::do_align_x`] and [`Cx::do_align_y`].
#[derive(Default)]
pub struct Popover {
background: Background,
}
impl Popover {
/// Handle events for the [`Popover`] widget.
///
/// Always marks events like [`PointerDownEvent`] and [`PointerHoverEvent`] as "handled".
/// This way, we don't leak those events to whatever is sitting in the background.
/// This does require this function to get called before any other event handlers!
///
/// TODO(JP): This might actually be a place where our eventing system breaks
/// down a bit. If you instantiate a [`Popover`] deep inside your application
/// inside some widget, then any events (e.g. [`PointerMoveEvent`]) might already
/// get handled earlier in the application. There are a few ways we can fix
/// this:
/// - We could do all of this in user space, by having some notion of a
/// "popover manager" that adds a [`View`] at the top level of the application
/// and can listen to "signal" events or so if you want to instantiate
/// a popover. Otherwise it might be tricky to communicate from inside
/// a widget to this "popover manager", or you'd have to pass a reference
/// to every widget! If we want to make this better, we can also consider
/// adding a function to get access to the application state through
/// [`Cx`], since we already pass that everywhere.
/// - We could also make such a "popover manager" or "popover layer" part of
/// the framework itself. Rik Arends told me that that has been on his
/// TODO list, so there indeed isn't proper support for this yet. I think
/// it might be better to start in user space though, since that's more
/// flexible, and then we can "graduate" it when we're happy with it.
pub fn handle(&mut self, _cx: &mut Cx, _event: &mut Event) {
// event.hits_pointer(cx, self.component_id, self.background.area().get_rect_for_first_instance(cx));
}
/// Draw the popover.
pub fn begin_draw(&mut self, cx: &mut Cx, width: Width, height: Height, color: Vec4) {
// TODO(JP): This feels like a bit of a hack; using [`Layout::align`] like this. It might be
// nicer to have an API that is like "move everything over by this dx/dy".
let popover_y_bottom = cx.get_draw_pos().y;
cx.begin_absolute_box();
cx.begin_column(Width::Fill, Height::Fix(popover_y_bottom));
cx.begin_bottom_box();
self.background.begin_draw(cx, width, height, color);
}
/// Ends the corresponding [`Popover::begin_draw`], using its final [`Rect`] to
/// draw and position the [`Popover::background`].
pub fn end_draw(&mut self, cx: &mut Cx) {
self.background.end_draw(cx);
cx.end_bottom_box();
cx.end_column();
cx.end_absolute_box();
}
}