1
0
Fork 0

Add the X-Y pad tooltip

With some fancy positioning
This commit is contained in:
Robbert van der Helm 2022-11-12 00:43:28 +01:00
parent cf40eb208d
commit 8b8cbf1952
2 changed files with 85 additions and 2 deletions

View file

@ -36,6 +36,24 @@ xy-pad {
overflow: hidden; overflow: hidden;
} }
xy-pad .xy-pad__tooltip {
opacity: 0;
transition: opacity 0.1 0;
background-color: #e5e5e5;
border-color: #0a0a0a;
border-width: 1px;
width: auto;
height: auto;
child-right: 5px;
child-left: 5px;
}
xy-pad:hover .xy-pad__tooltip {
opacity: 1;
transition: opacity 0.1 0;
}
xy-pad__handle { xy-pad__handle {
background-color: #e5e5e5; background-color: #e5e5e5;
border-color: #0a0a0a; border-color: #0a0a0a;

View file

@ -33,6 +33,7 @@ const HANDLE_WIDTH_PX: f32 = 20.0;
// //
// TODO: Text entry for the x-parameter // TODO: Text entry for the x-parameter
// TODO: Tooltip // TODO: Tooltip
#[derive(Lens)]
pub struct XyPad { pub struct XyPad {
x_param_base: ParamWidgetBase, x_param_base: ParamWidgetBase,
y_param_base: ParamWidgetBase, y_param_base: ParamWidgetBase,
@ -43,13 +44,19 @@ pub struct XyPad {
/// This keeps track of whether the user has pressed shift and a granular drag is active. This /// This keeps track of whether the user has pressed shift and a granular drag is active. This
/// works exactly the same as in `ParamSlider`. /// works exactly the same as in `ParamSlider`.
granular_drag_status: Option<GranularDragStatus>, granular_drag_status: Option<GranularDragStatus>,
// Used to position the tooltip so it's anchored to the mouse cursor, set in the `MouseMove`
// event handler so the tooltip stays at the top right of the mouse cursor.
tooltip_pos_x: Units,
tooltip_pos_y: Units,
} }
/// The [`XyPad`]'s handle. This is a separate eleemnt to allow easier positioning. /// The [`XyPad`]'s handle. This is a separate eleemnt to allow easier positioning.
struct XyPadHandle; struct XyPadHandle;
// TODO: Vizia's derive macro requires this to be pub
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
struct GranularDragStatus { pub struct GranularDragStatus {
/// The mouse's X-coordinate when the granular drag was started. /// The mouse's X-coordinate when the granular drag was started.
pub starting_x_coordinate: f32, pub starting_x_coordinate: f32,
/// The normalized value when the granular drag was started for the X-parameter. /// The normalized value when the granular drag was started for the X-parameter.
@ -84,6 +91,9 @@ impl XyPad {
drag_active: false, drag_active: false,
granular_drag_status: None, granular_drag_status: None,
tooltip_pos_x: Pixels(0.0),
tooltip_pos_y: Pixels(0.0),
} }
.build( .build(
cx, cx,
@ -107,14 +117,46 @@ impl XyPad {
Percentage((1.0 - param.unmodulated_normalized_value()) * 100.0) Percentage((1.0 - param.unmodulated_normalized_value()) * 100.0)
}); });
// Can't use `.to_string()` here as that would include the modulation.
let x_display_value_lens = x_param_data.make_lens(|param| {
param.normalized_value_to_string(
param.unmodulated_normalized_value(),
true,
)
});
let y_display_value_lens = y_param_data.make_lens(|param| {
param.normalized_value_to_string(
param.unmodulated_normalized_value(),
true,
)
});
XyPadHandle::new(cx) XyPadHandle::new(cx)
.position_type(PositionType::SelfDirected)
.top(y_position_lens) .top(y_position_lens)
.left(x_position_lens) .left(x_position_lens)
// TODO: It would be much nicer if this could be set in the // TODO: It would be much nicer if this could be set in the
// stylesheet, but Vizia doesn't support that right now // stylesheet, but Vizia doesn't support that right now
.translate((-(HANDLE_WIDTH_PX / 2.0), -(HANDLE_WIDTH_PX / 2.0))) .translate((-(HANDLE_WIDTH_PX / 2.0), -(HANDLE_WIDTH_PX / 2.0)))
.width(Pixels(HANDLE_WIDTH_PX)) .width(Pixels(HANDLE_WIDTH_PX))
.height(Pixels(HANDLE_WIDTH_PX)); .height(Pixels(HANDLE_WIDTH_PX))
.hoverable(false);
// The stylesheet makes the tooltip visible when hovering over the X-Y
// pad. Its position is set to the mouse coordinate in the event
// handler. If there's enough space, the tooltip is drawn at the top
// right of the mouse cursor.
VStack::new(cx, move |cx| {
// The X-parameter is the 'important' one, so we'll display that at
// the bottom since it's closer to the mouse cursor
Label::new(cx, y_display_value_lens);
Label::new(cx, x_display_value_lens);
})
.class("xy-pad__tooltip")
.left(XyPad::tooltip_pos_x)
.top(XyPad::tooltip_pos_y)
.position_type(PositionType::SelfDirected)
.hoverable(false);
}, },
); );
}, },
@ -243,6 +285,29 @@ impl View for XyPad {
} }
} }
WindowEvent::MouseMove(x, y) => { WindowEvent::MouseMove(x, y) => {
// This is used to position the tooltip to the top right of the mouse cursor
let bounds = cx.cache.get_bounds(cx.current());
let relative_x = x - bounds.x;
let relative_y = y - bounds.y;
// If there's not enough space at the top right, we'll move the tooltip to the
// bottom and/or the left
let tooltip_entity = cx
.tree
.get_last_child(cx.current())
.expect("Missing child view in X-Y pad");
let tooltip_bounds = cx.cache.get_bounds(tooltip_entity);
self.tooltip_pos_x = if (relative_x + tooltip_bounds.w + 4.0) >= bounds.w {
Pixels(relative_x - 2.0 - tooltip_bounds.w)
} else {
Pixels(relative_x + 2.0)
};
self.tooltip_pos_y = if (relative_y - tooltip_bounds.h - 4.0) <= 0.0 {
Pixels(relative_y + 2.0)
} else {
Pixels(relative_y - 2.0 - tooltip_bounds.h)
};
if self.drag_active { if self.drag_active {
// If shift is being held then the drag should be more granular instead of // If shift is being held then the drag should be more granular instead of
// absolute // absolute