mirror of https://github.com/linebender/xilem
Remove `WidgetState::Cursor` (#710)
This commit is contained in:
parent
f322894e50
commit
30dba40300
|
@ -16,9 +16,7 @@ use crate::render_root::{MutateCallback, RenderRootSignal, RenderRootState};
|
|||
use crate::text::TextBrush;
|
||||
use crate::tree_arena::{ArenaMutChildren, ArenaRefChildren};
|
||||
use crate::widget::{WidgetMut, WidgetRef, WidgetState};
|
||||
use crate::{
|
||||
AllowRawMut, BoxConstraints, CursorIcon, Insets, Point, Rect, Size, Widget, WidgetId, WidgetPod,
|
||||
};
|
||||
use crate::{AllowRawMut, BoxConstraints, Insets, Point, Rect, Size, Widget, WidgetId, WidgetPod};
|
||||
|
||||
// Note - Most methods defined in this file revolve around `WidgetState` fields.
|
||||
// Consider reading `WidgetState` documentation (especially the documented naming scheme)
|
||||
|
@ -371,32 +369,13 @@ impl_context_method!(
|
|||
// --- MARK: CURSOR ---
|
||||
// Cursor-related impls.
|
||||
impl_context_method!(EventCtx<'_>, {
|
||||
// TODO - Rewrite doc
|
||||
/// Set the cursor icon.
|
||||
/// Notifies Masonry that the cursor returned by [`Widget::get_cursor`] has changed.
|
||||
///
|
||||
/// This setting will be retained until [`clear_cursor`] is called, but it will only take
|
||||
/// effect when this widget [`is_hovered`] and/or [`has_pointer_capture`]. If a child widget also
|
||||
/// sets a cursor, the child widget's cursor will take precedence. (If that isn't what you
|
||||
/// want, use [`override_cursor`] instead.)
|
||||
///
|
||||
/// [`clear_cursor`]: EventCtx::clear_cursor
|
||||
/// [`override_cursor`]: EventCtx::override_cursor
|
||||
/// [`is_hovered`]: EventCtx::is_hovered
|
||||
/// [`has_pointer_capture`]: EventCtx::has_pointer_capture
|
||||
pub fn set_cursor(&mut self, cursor: &CursorIcon) {
|
||||
trace!("set_cursor {:?}", cursor);
|
||||
self.widget_state.cursor = Some(*cursor);
|
||||
}
|
||||
|
||||
/// Clear the cursor icon.
|
||||
///
|
||||
/// This undoes the effect of [`set_cursor`] and [`override_cursor`].
|
||||
///
|
||||
/// [`override_cursor`]: EventCtx::override_cursor
|
||||
/// [`set_cursor`]: EventCtx::set_cursor
|
||||
pub fn clear_cursor(&mut self) {
|
||||
trace!("clear_cursor");
|
||||
self.widget_state.cursor = None;
|
||||
/// This is mostly meant for cases where the cursor changes even if the pointer doesn't
|
||||
/// move, because the nature of the widget has changed somehow.
|
||||
pub fn cursor_icon_changed(&mut self) {
|
||||
trace!("cursor_icon_changed");
|
||||
self.global_state.needs_pointer_pass = true;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -10,7 +10,9 @@ use crate::passes::event::run_on_pointer_event_pass;
|
|||
use crate::passes::{merge_state_up, recurse_on_children};
|
||||
use crate::render_root::{RenderRoot, RenderRootSignal, RenderRootState};
|
||||
use crate::tree_arena::ArenaMut;
|
||||
use crate::{PointerEvent, RegisterCtx, Update, UpdateCtx, Widget, WidgetId, WidgetState};
|
||||
use crate::{
|
||||
PointerEvent, QueryCtx, RegisterCtx, Update, UpdateCtx, Widget, WidgetId, WidgetState,
|
||||
};
|
||||
|
||||
// --- MARK: HELPERS ---
|
||||
fn get_id_path(root: &RenderRoot, widget_id: Option<WidgetId>) -> Vec<WidgetId> {
|
||||
|
@ -235,6 +237,11 @@ fn update_disabled_for_widget(
|
|||
pub(crate) fn run_update_disabled_pass(root: &mut RenderRoot) {
|
||||
let _span = info_span!("update_disabled").entered();
|
||||
|
||||
// If a widget was enabled or disabled, the pointer icon may need to change.
|
||||
if root.root_state().needs_update_disabled {
|
||||
root.global_state.needs_pointer_pass = true;
|
||||
}
|
||||
|
||||
let (root_widget, root_state) = root.widget_arena.get_pair_mut(root.root.id());
|
||||
update_disabled_for_widget(&mut root.global_state, root_widget, root_state, false);
|
||||
}
|
||||
|
@ -627,9 +634,21 @@ pub(crate) fn run_update_pointer_pass(root: &mut RenderRoot) {
|
|||
.pointer_capture_target
|
||||
.or(next_hovered_widget);
|
||||
|
||||
let new_cursor = if let Some(cursor_source) = cursor_source {
|
||||
let new_cursor = if let (Some(cursor_source), Some(pos)) = (cursor_source, pointer_pos) {
|
||||
let (widget, state) = root.widget_arena.get_pair(cursor_source);
|
||||
state.item.cursor.unwrap_or(widget.item.get_cursor())
|
||||
|
||||
let ctx = QueryCtx {
|
||||
global_state: &root.global_state,
|
||||
widget_state_children: state.children,
|
||||
widget_children: widget.children,
|
||||
widget_state: state.item,
|
||||
};
|
||||
|
||||
if state.item.is_disabled {
|
||||
CursorIcon::Default
|
||||
} else {
|
||||
widget.item.get_cursor(&ctx, pos)
|
||||
}
|
||||
} else {
|
||||
CursorIcon::Default
|
||||
};
|
||||
|
|
|
@ -386,7 +386,7 @@ impl<S: 'static> Widget for ModularWidget<S> {
|
|||
None
|
||||
}
|
||||
|
||||
fn get_cursor(&self) -> CursorIcon {
|
||||
fn get_cursor(&self, _ctx: &QueryCtx, _pos: Point) -> CursorIcon {
|
||||
CursorIcon::Default
|
||||
}
|
||||
|
||||
|
@ -586,8 +586,8 @@ impl<W: Widget> Widget for Recorder<W> {
|
|||
self.child.get_debug_text()
|
||||
}
|
||||
|
||||
fn get_cursor(&self) -> CursorIcon {
|
||||
self.child.get_cursor()
|
||||
fn get_cursor(&self, ctx: &QueryCtx, pos: Point) -> CursorIcon {
|
||||
self.child.get_cursor(ctx, pos)
|
||||
}
|
||||
|
||||
fn get_child_at_pos<'c>(
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::widget::label::LABEL_X_PADDING;
|
|||
use crate::widget::{LineBreaking, WidgetMut};
|
||||
use crate::{
|
||||
AccessCtx, AccessEvent, BoxConstraints, CursorIcon, EventCtx, LayoutCtx, PaintCtx,
|
||||
PointerEvent, RegisterCtx, TextEvent, Update, UpdateCtx, Widget, WidgetId,
|
||||
PointerEvent, QueryCtx, RegisterCtx, TextEvent, Update, UpdateCtx, Widget, WidgetId,
|
||||
};
|
||||
|
||||
/// The prose widget is a widget which displays text which can be
|
||||
|
@ -150,15 +150,12 @@ impl Widget for Prose {
|
|||
}
|
||||
}
|
||||
PointerEvent::PointerMove(state) => {
|
||||
if !ctx.is_disabled() {
|
||||
// TODO: Set cursor if over link
|
||||
ctx.set_cursor(&CursorIcon::Text);
|
||||
if ctx.has_pointer_capture()
|
||||
&& self.text_layout.pointer_move(inner_origin, state)
|
||||
{
|
||||
// We might have changed text colours, so we need to re-request a layout
|
||||
ctx.request_layout();
|
||||
}
|
||||
if !ctx.is_disabled()
|
||||
&& ctx.has_pointer_capture()
|
||||
&& self.text_layout.pointer_move(inner_origin, state)
|
||||
{
|
||||
// We might have changed text colours, so we need to re-request a layout
|
||||
ctx.request_layout();
|
||||
}
|
||||
}
|
||||
PointerEvent::PointerUp(button, state) => {
|
||||
|
@ -265,6 +262,11 @@ impl Widget for Prose {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_cursor(&self, _ctx: &QueryCtx, _pos: Point) -> CursorIcon {
|
||||
// TODO: Set cursor if over link
|
||||
CursorIcon::Text
|
||||
}
|
||||
|
||||
fn accessibility_role(&self) -> Role {
|
||||
Role::Document
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ use smallvec::{smallvec, SmallVec};
|
|||
use tracing::{trace_span, warn, Span};
|
||||
use vello::Scene;
|
||||
|
||||
use crate::dpi::LogicalPosition;
|
||||
use crate::event::PointerButton;
|
||||
use crate::kurbo::Line;
|
||||
use crate::paint_scene_helpers::{fill_color, stroke};
|
||||
|
@ -16,7 +15,7 @@ use crate::widget::flex::Axis;
|
|||
use crate::widget::{WidgetMut, WidgetPod};
|
||||
use crate::{
|
||||
theme, AccessCtx, AccessEvent, BoxConstraints, Color, CursorIcon, EventCtx, LayoutCtx,
|
||||
PaintCtx, Point, PointerEvent, Rect, RegisterCtx, Size, TextEvent, Widget, WidgetId,
|
||||
PaintCtx, Point, PointerEvent, QueryCtx, Rect, RegisterCtx, Size, TextEvent, Widget, WidgetId,
|
||||
};
|
||||
|
||||
// TODO - Have child widget type as generic argument
|
||||
|
@ -31,11 +30,6 @@ pub struct Split {
|
|||
min_bar_area: f64, // Integers only
|
||||
solid: bool,
|
||||
draggable: bool,
|
||||
/// The split bar is hovered by the mouse. This state is locked to `true` if the
|
||||
/// widget is active (the bar is being dragged) to avoid cursor and painting jitter
|
||||
/// if the mouse moves faster than the layout and temporarily gets outside of the
|
||||
/// bar area while still being dragged.
|
||||
is_bar_hover: bool,
|
||||
/// Offset from the split point (bar center) to the actual mouse position when the
|
||||
/// bar was clicked. This is used to ensure a click without mouse move is a no-op,
|
||||
/// instead of re-centering the bar on the mouse.
|
||||
|
@ -60,7 +54,6 @@ impl Split {
|
|||
min_bar_area: 6.0,
|
||||
solid: false,
|
||||
draggable: false,
|
||||
is_bar_hover: false,
|
||||
click_offset: 0.0,
|
||||
child1: WidgetPod::new(child1).boxed(),
|
||||
child2: WidgetPod::new(child2).boxed(),
|
||||
|
@ -199,7 +192,7 @@ impl Split {
|
|||
}
|
||||
|
||||
/// Returns true if the provided mouse position is inside the splitter bar area.
|
||||
fn bar_hit_test(&self, size: Size, mouse_pos: LogicalPosition<f64>) -> bool {
|
||||
fn bar_hit_test(&self, size: Size, mouse_pos: Point) -> bool {
|
||||
let (edge1, edge2) = self.bar_edges(size);
|
||||
match self.split_axis {
|
||||
Axis::Horizontal => mouse_pos.x >= edge1 && mouse_pos.x <= edge2,
|
||||
|
@ -375,7 +368,9 @@ impl Widget for Split {
|
|||
if self.draggable {
|
||||
match event {
|
||||
PointerEvent::PointerDown(PointerButton::Primary, state) => {
|
||||
if self.bar_hit_test(ctx.size(), state.position) {
|
||||
let mouse_pos = Point::new(state.position.x, state.position.y);
|
||||
let local_mouse_pos = mouse_pos - ctx.window_origin().to_vec2();
|
||||
if self.bar_hit_test(ctx.size(), local_mouse_pos) {
|
||||
ctx.set_handled();
|
||||
ctx.capture_pointer();
|
||||
// Save the delta between the mouse click position and the split point
|
||||
|
@ -383,26 +378,6 @@ impl Widget for Split {
|
|||
Axis::Horizontal => state.position.x,
|
||||
Axis::Vertical => state.position.y,
|
||||
} - self.bar_position(ctx.size());
|
||||
// If not already hovering, force and change cursor appropriately
|
||||
if !self.is_bar_hover {
|
||||
self.is_bar_hover = true;
|
||||
match self.split_axis {
|
||||
Axis::Horizontal => ctx.set_cursor(&CursorIcon::EwResize),
|
||||
Axis::Vertical => ctx.set_cursor(&CursorIcon::NsResize),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
PointerEvent::PointerUp(PointerButton::Primary, state) => {
|
||||
if ctx.has_pointer_capture() {
|
||||
ctx.set_handled();
|
||||
// Depending on where the mouse cursor is when the button is released,
|
||||
// the cursor might or might not need to be changed
|
||||
self.is_bar_hover =
|
||||
ctx.is_hovered() && self.bar_hit_test(ctx.size(), state.position);
|
||||
if !self.is_bar_hover {
|
||||
ctx.clear_cursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
PointerEvent::PointerMove(state) => {
|
||||
|
@ -418,21 +393,6 @@ impl Widget for Split {
|
|||
};
|
||||
self.update_split_point(ctx.size(), effective_pos);
|
||||
ctx.request_layout();
|
||||
} else {
|
||||
// If not active, set cursor when hovering state changes
|
||||
let hover =
|
||||
ctx.is_hovered() && self.bar_hit_test(ctx.size(), state.position);
|
||||
if self.is_bar_hover != hover {
|
||||
self.is_bar_hover = hover;
|
||||
if hover {
|
||||
match self.split_axis {
|
||||
Axis::Horizontal => ctx.set_cursor(&CursorIcon::EwResize),
|
||||
Axis::Vertical => ctx.set_cursor(&CursorIcon::NsResize),
|
||||
};
|
||||
} else {
|
||||
ctx.clear_cursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
@ -556,6 +516,20 @@ impl Widget for Split {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_cursor(&self, ctx: &QueryCtx, pos: Point) -> CursorIcon {
|
||||
let local_mouse_pos = pos - ctx.window_origin().to_vec2();
|
||||
let is_bar_hovered = self.bar_hit_test(ctx.size(), local_mouse_pos);
|
||||
|
||||
if ctx.has_pointer_capture() || is_bar_hovered {
|
||||
match self.split_axis {
|
||||
Axis::Horizontal => CursorIcon::EwResize,
|
||||
Axis::Vertical => CursorIcon::NsResize,
|
||||
}
|
||||
} else {
|
||||
CursorIcon::Default
|
||||
}
|
||||
}
|
||||
|
||||
fn accessibility_role(&self) -> Role {
|
||||
Role::Splitter
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::text::{TextBrush, TextEditor, TextWithSelection};
|
|||
use crate::widget::{LineBreaking, WidgetMut};
|
||||
use crate::{
|
||||
AccessCtx, AccessEvent, BoxConstraints, CursorIcon, EventCtx, LayoutCtx, PaintCtx,
|
||||
PointerEvent, RegisterCtx, TextEvent, Update, UpdateCtx, Widget, WidgetId,
|
||||
PointerEvent, QueryCtx, RegisterCtx, TextEvent, Update, UpdateCtx, Widget, WidgetId,
|
||||
};
|
||||
|
||||
const TEXTBOX_PADDING: f64 = 3.0;
|
||||
|
@ -308,7 +308,7 @@ impl Widget for Textbox {
|
|||
);
|
||||
}
|
||||
|
||||
fn get_cursor(&self) -> CursorIcon {
|
||||
fn get_cursor(&self, _ctx: &QueryCtx, _pos: Point) -> CursorIcon {
|
||||
CursorIcon::Text
|
||||
}
|
||||
|
||||
|
|
|
@ -220,9 +220,11 @@ pub trait Widget: AsAny {
|
|||
None
|
||||
}
|
||||
|
||||
// TODO - Document
|
||||
// TODO - Add &UpdateCtx argument
|
||||
fn get_cursor(&self) -> CursorIcon {
|
||||
/// Return the cursor icon for this widget.
|
||||
///
|
||||
/// **pos** - the mouse position in global coordinates (e.g. `(0,0)` is the top-left corner of the
|
||||
/// window).
|
||||
fn get_cursor(&self, ctx: &QueryCtx, pos: Point) -> CursorIcon {
|
||||
CursorIcon::Default
|
||||
}
|
||||
|
||||
|
@ -471,8 +473,8 @@ impl Widget for Box<dyn Widget> {
|
|||
self.deref().get_debug_text()
|
||||
}
|
||||
|
||||
fn get_cursor(&self) -> CursorIcon {
|
||||
self.deref().get_cursor()
|
||||
fn get_cursor(&self, ctx: &QueryCtx, pos: Point) -> CursorIcon {
|
||||
self.deref().get_cursor(ctx, pos)
|
||||
}
|
||||
|
||||
fn get_child_at_pos<'c>(
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
use vello::kurbo::{Insets, Point, Rect, Size, Vec2};
|
||||
|
||||
use crate::{CursorIcon, WidgetId};
|
||||
use crate::WidgetId;
|
||||
|
||||
// TODO - Reduce WidgetState size.
|
||||
// See https://github.com/linebender/xilem/issues/706
|
||||
|
@ -126,9 +126,6 @@ pub(crate) struct WidgetState {
|
|||
|
||||
pub(crate) children_changed: bool,
|
||||
|
||||
// TODO - Remove and handle in WidgetRoot instead
|
||||
pub(crate) cursor: Option<CursorIcon>,
|
||||
|
||||
// --- STATUS ---
|
||||
/// This widget has been disabled.
|
||||
pub(crate) is_explicitly_disabled: bool,
|
||||
|
@ -192,7 +189,6 @@ impl WidgetState {
|
|||
needs_update_stashed: true,
|
||||
focus_chain: Vec::new(),
|
||||
children_changed: true,
|
||||
cursor: None,
|
||||
update_focus_chain: true,
|
||||
#[cfg(debug_assertions)]
|
||||
widget_name,
|
||||
|
|
Loading…
Reference in New Issue