mirror of https://github.com/linebender/xilem
Remove register methods (#636)
Replace with `Widget::accepts_xxx()` methods Tweak accessibility code Remove `BuildFocusChain` event Remove `WidgetState::is_portal`, `register_as_portal()`, and overall remove the idea of a first-class "portal" concept.
This commit is contained in:
parent
a26bf11119
commit
4ad4506bc0
|
@ -335,9 +335,19 @@ impl_context_method!(
|
|||
self.widget_state.has_focus
|
||||
}
|
||||
|
||||
/// Whether this specific widget is in the focus chain.
|
||||
pub fn is_in_focus_chain(&self) -> bool {
|
||||
self.widget_state.in_focus_chain
|
||||
/// Whether this widget gets pointer events and hovered status.
|
||||
pub fn accepts_pointer_interaction(&self) -> bool {
|
||||
self.widget_state.accepts_pointer_interaction
|
||||
}
|
||||
|
||||
/// Whether this widget gets text focus.
|
||||
pub fn accepts_focus(&self) -> bool {
|
||||
self.widget_state.accepts_focus
|
||||
}
|
||||
|
||||
/// Whether this widget gets IME events.
|
||||
pub fn accepts_text_input(&self) -> bool {
|
||||
self.widget_state.accepts_text_input
|
||||
}
|
||||
|
||||
/// The disabled state of a widget.
|
||||
|
@ -791,34 +801,6 @@ impl RegisterCtx<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl LifeCycleCtx<'_> {
|
||||
/// Register this widget to be eligile to accept focus automatically.
|
||||
///
|
||||
/// This should only be called in response to a [`LifeCycle::BuildFocusChain`] event.
|
||||
///
|
||||
/// See [`EventCtx::is_focused`](Self::is_focused) for more information about focus.
|
||||
///
|
||||
/// [`LifeCycle::BuildFocusChain`]: crate::LifeCycle::BuildFocusChain
|
||||
pub fn register_for_focus(&mut self) {
|
||||
trace!("register_for_focus");
|
||||
self.widget_state.focus_chain.push(self.widget_id());
|
||||
self.widget_state.in_focus_chain = true;
|
||||
}
|
||||
|
||||
/// Register this widget as accepting text input.
|
||||
pub fn register_as_text_input(&mut self) {
|
||||
self.widget_state.is_text_input = true;
|
||||
}
|
||||
|
||||
// TODO - remove - See issue https://github.com/linebender/xilem/issues/366
|
||||
/// Register this widget as a portal.
|
||||
///
|
||||
/// This should only be used by scroll areas.
|
||||
pub fn register_as_portal(&mut self) {
|
||||
self.widget_state.is_portal = true;
|
||||
}
|
||||
}
|
||||
|
||||
// --- MARK: UPDATE LAYOUT ---
|
||||
impl LayoutCtx<'_> {
|
||||
#[track_caller]
|
||||
|
|
|
@ -292,16 +292,6 @@ pub enum LifeCycle {
|
|||
/// [`is_stashed`]: crate::EventCtx::is_stashed
|
||||
/// [`set_stashed`]: crate::EventCtx::set_stashed
|
||||
StashedChanged(bool),
|
||||
/// Called when the widget tree changes and Masonry wants to rebuild the
|
||||
/// Focus-chain.
|
||||
///
|
||||
/// It is the only place from which [`register_for_focus`] should be called.
|
||||
/// By doing so the widget can get focused by other widgets using [`focus_next`] or [`focus_prev`].
|
||||
///
|
||||
/// [`register_for_focus`]: crate::LifeCycleCtx::register_for_focus
|
||||
/// [`focus_next`]: crate::EventCtx::focus_next
|
||||
/// [`focus_prev`]: crate::EventCtx::focus_prev
|
||||
BuildFocusChain,
|
||||
|
||||
/// Called when a child widgets uses
|
||||
/// [`EventCtx::request_pan_to_this`](crate::EventCtx::request_pan_to_this).
|
||||
|
@ -488,26 +478,6 @@ impl PointerState {
|
|||
}
|
||||
|
||||
impl LifeCycle {
|
||||
// TODO - link this to documentation of stashed widgets - See issue https://github.com/linebender/xilem/issues/372
|
||||
/// Whether this event should be sent to widgets which are currently not visible and not
|
||||
/// accessible.
|
||||
///
|
||||
/// If a widget changes which children are `hidden` it must call [`children_changed`].
|
||||
/// For a more detailed explanation of the `hidden` state, see [`Event::should_propagate_to_hidden`].
|
||||
///
|
||||
/// [`children_changed`]: crate::EventCtx::children_changed
|
||||
/// [`Event::should_propagate_to_hidden`]: Event::should_propagate_to_hidden
|
||||
pub fn should_propagate_to_hidden(&self) -> bool {
|
||||
match self {
|
||||
LifeCycle::WidgetAdded => true,
|
||||
LifeCycle::AnimFrame(_) => true,
|
||||
LifeCycle::DisabledChanged(_) => true,
|
||||
LifeCycle::StashedChanged(_) => true,
|
||||
LifeCycle::BuildFocusChain => false,
|
||||
LifeCycle::RequestPanToChild(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Short name, for debug logging.
|
||||
///
|
||||
/// Essentially returns the enum variant name.
|
||||
|
@ -517,7 +487,6 @@ impl LifeCycle {
|
|||
LifeCycle::AnimFrame(_) => "AnimFrame",
|
||||
LifeCycle::DisabledChanged(_) => "DisabledChanged",
|
||||
LifeCycle::StashedChanged(_) => "StashedChanged",
|
||||
LifeCycle::BuildFocusChain => "BuildFocusChain",
|
||||
LifeCycle::RequestPanToChild(_) => "RequestPanToChild",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ fn build_access_node(widget: &mut dyn Widget, ctx: &mut AccessCtx) -> NodeBuilde
|
|||
if ctx.widget_state.clip.is_some() {
|
||||
node.set_clips_children();
|
||||
}
|
||||
if ctx.is_in_focus_chain() && !ctx.is_disabled() {
|
||||
if ctx.accepts_focus() && !ctx.is_disabled() && !ctx.is_stashed() {
|
||||
node.add_action(accesskit::Action::Focus);
|
||||
}
|
||||
if ctx.is_focused() {
|
||||
|
|
|
@ -234,7 +234,7 @@ pub(crate) fn run_layout_inner<W: Widget>(
|
|||
|
||||
// TODO - This check might be redundant with the code updating local_paint_rect
|
||||
let child_rect = child_state.paint_rect();
|
||||
if !rect_contains(&state.local_paint_rect, &child_rect) && !state.is_portal {
|
||||
if !rect_contains(&state.local_paint_rect, &child_rect) && state.clip.is_none() {
|
||||
debug_panic!(
|
||||
"Error in '{}' {}: paint_rect {:?} doesn't contain paint_rect {:?} of child widget '{}' {}",
|
||||
widget.short_type_name(),
|
||||
|
|
|
@ -248,7 +248,7 @@ pub(crate) fn run_update_focus_pass(root: &mut RenderRoot) {
|
|||
if prev_focused != next_focused {
|
||||
let was_ime_active = root.state.is_ime_active;
|
||||
let is_ime_active = if let Some(id) = next_focused {
|
||||
root.widget_arena.get_state(id).item.is_text_input
|
||||
root.widget_arena.get_state(id).item.accepts_text_input
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
@ -551,8 +551,11 @@ fn update_widget_tree(
|
|||
"{} received LifeCycle::WidgetAdded",
|
||||
widget.item.short_type_name()
|
||||
);
|
||||
state.item.accepts_pointer_interaction = widget.item.accepts_pointer_interaction();
|
||||
state.item.accepts_focus = widget.item.accepts_focus();
|
||||
state.item.accepts_text_input = widget.item.accepts_text_input();
|
||||
state.item.is_new = false;
|
||||
}
|
||||
state.item.is_new = false;
|
||||
|
||||
// We can recurse on this widget's children, because they have already been added
|
||||
// to the arena above.
|
||||
|
@ -598,7 +601,7 @@ pub(crate) fn run_update_widget_tree_pass(root: &mut RenderRoot) {
|
|||
fn update_focus_chain_for_widget(
|
||||
global_state: &mut RenderRootState,
|
||||
mut widget: ArenaMut<'_, Box<dyn Widget>>,
|
||||
mut state: ArenaMut<'_, WidgetState>,
|
||||
state: ArenaMut<'_, WidgetState>,
|
||||
parent_focus_chain: &mut Vec<WidgetId>,
|
||||
) {
|
||||
let _span = widget.item.make_trace_span().entered();
|
||||
|
@ -612,16 +615,9 @@ fn update_focus_chain_for_widget(
|
|||
state.item.has_focus = global_state.focused_widget == Some(id);
|
||||
let had_focus = state.item.has_focus;
|
||||
|
||||
state.item.in_focus_chain = false;
|
||||
state.item.focus_chain.clear();
|
||||
{
|
||||
let mut ctx = LifeCycleCtx {
|
||||
global_state,
|
||||
widget_state: state.item,
|
||||
widget_state_children: state.children.reborrow_mut(),
|
||||
widget_children: widget.children.reborrow_mut(),
|
||||
};
|
||||
widget.item.lifecycle(&mut ctx, &LifeCycle::BuildFocusChain);
|
||||
if state.item.accepts_focus {
|
||||
state.item.focus_chain.push(id);
|
||||
}
|
||||
state.item.update_focus_chain = false;
|
||||
|
||||
|
|
|
@ -45,6 +45,9 @@ pub const REPLACE_CHILD: Selector = Selector::new("masonry-test.replace-child");
|
|||
/// This widget is generic over its state, which is passed in at construction time.
|
||||
pub struct ModularWidget<S> {
|
||||
state: S,
|
||||
accepts_pointer_interaction: bool,
|
||||
accepts_focus: bool,
|
||||
accepts_text_input: bool,
|
||||
on_pointer_event: Option<Box<PointerEventFn<S>>>,
|
||||
on_text_event: Option<Box<TextEventFn<S>>>,
|
||||
on_access_event: Option<Box<AccessEventFn<S>>>,
|
||||
|
@ -128,6 +131,9 @@ impl<S> ModularWidget<S> {
|
|||
pub fn new(state: S) -> Self {
|
||||
ModularWidget {
|
||||
state,
|
||||
accepts_pointer_interaction: true,
|
||||
accepts_focus: false,
|
||||
accepts_text_input: false,
|
||||
on_pointer_event: None,
|
||||
on_text_event: None,
|
||||
on_access_event: None,
|
||||
|
@ -143,6 +149,21 @@ impl<S> ModularWidget<S> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn accepts_pointer_interaction(mut self, flag: bool) -> Self {
|
||||
self.accepts_pointer_interaction = flag;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn accepts_focus(mut self, flag: bool) -> Self {
|
||||
self.accepts_focus = flag;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn accepts_text_input(mut self, flag: bool) -> Self {
|
||||
self.accepts_text_input = flag;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn pointer_event_fn(
|
||||
mut self,
|
||||
f: impl FnMut(&mut S, &mut EventCtx, &PointerEvent) + 'static,
|
||||
|
@ -315,6 +336,18 @@ impl<S: 'static> Widget for ModularWidget<S> {
|
|||
SmallVec::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn accepts_pointer_interaction(&self) -> bool {
|
||||
self.accepts_pointer_interaction
|
||||
}
|
||||
|
||||
fn accepts_focus(&self) -> bool {
|
||||
self.accepts_focus
|
||||
}
|
||||
|
||||
fn accepts_text_input(&self) -> bool {
|
||||
self.accepts_text_input
|
||||
}
|
||||
}
|
||||
|
||||
impl ReplaceChild {
|
||||
|
|
|
@ -58,7 +58,7 @@ impl Button {
|
|||
/// ```
|
||||
pub fn from_label(label: Label) -> Button {
|
||||
Button {
|
||||
label: WidgetPod::new(label.with_skip_pointer(true)),
|
||||
label: WidgetPod::new(label.with_pointer_interaction(false)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ impl Checkbox {
|
|||
pub fn new(checked: bool, text: impl Into<ArcStr>) -> Checkbox {
|
||||
Checkbox {
|
||||
checked,
|
||||
label: WidgetPod::new(Label::new(text).with_skip_pointer(true)),
|
||||
label: WidgetPod::new(Label::new(text).with_pointer_interaction(false)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ impl Checkbox {
|
|||
pub fn from_label(checked: bool, label: Label) -> Checkbox {
|
||||
Checkbox {
|
||||
checked,
|
||||
label: WidgetPod::new(label.with_skip_pointer(true)),
|
||||
label: WidgetPod::new(label.with_pointer_interaction(false)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ pub struct Label {
|
|||
line_break_mode: LineBreaking,
|
||||
show_disabled: bool,
|
||||
brush: TextBrush,
|
||||
skip_pointer: bool,
|
||||
interactive: bool,
|
||||
}
|
||||
|
||||
// --- MARK: BUILDERS ---
|
||||
|
@ -55,14 +55,15 @@ impl Label {
|
|||
line_break_mode: LineBreaking::Overflow,
|
||||
show_disabled: true,
|
||||
brush: crate::theme::TEXT_COLOR.into(),
|
||||
skip_pointer: false,
|
||||
interactive: true,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO - Rename
|
||||
// TODO - Document
|
||||
pub fn with_skip_pointer(mut self, skip_pointer: bool) -> Self {
|
||||
self.skip_pointer = skip_pointer;
|
||||
/// Sets the value returned by [`accepts_pointer_interaction`].
|
||||
///
|
||||
/// True by default.
|
||||
pub fn with_pointer_interaction(mut self, interactive: bool) -> Self {
|
||||
self.interactive = interactive;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -204,7 +205,6 @@ impl Widget for Label {
|
|||
// TODO: Parley seems to require a relayout when colours change
|
||||
ctx.request_layout();
|
||||
}
|
||||
LifeCycle::BuildFocusChain => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -258,14 +258,14 @@ impl Widget for Label {
|
|||
node.set_name(self.text().as_ref().to_string());
|
||||
}
|
||||
|
||||
fn skip_pointer(&self) -> bool {
|
||||
self.skip_pointer
|
||||
}
|
||||
|
||||
fn children_ids(&self) -> SmallVec<[WidgetId; 16]> {
|
||||
SmallVec::new()
|
||||
}
|
||||
|
||||
fn accepts_pointer_interaction(&self) -> bool {
|
||||
self.interactive
|
||||
}
|
||||
|
||||
fn make_trace_span(&self) -> Span {
|
||||
trace_span!("Label")
|
||||
}
|
||||
|
|
|
@ -329,9 +329,6 @@ impl<W: Widget> Widget for Portal<W> {
|
|||
|
||||
fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle) {
|
||||
match event {
|
||||
LifeCycle::WidgetAdded => {
|
||||
ctx.register_as_portal();
|
||||
}
|
||||
LifeCycle::RequestPanToChild(target) => {
|
||||
let portal_size = ctx.size();
|
||||
let content_size = ctx.get_raw_ref(&mut self.child).ctx().layout_rect().size();
|
||||
|
|
|
@ -225,9 +225,6 @@ impl Widget for Prose {
|
|||
// TODO: Parley seems to require a relayout when colours change
|
||||
ctx.request_layout();
|
||||
}
|
||||
LifeCycle::BuildFocusChain => {
|
||||
// When we add links to `Prose`, they will probably need to be handled here.
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,11 +118,7 @@ fn check_pointer_capture_outside_pointer_down() {
|
|||
fn check_pointer_capture_text_event() {
|
||||
let id = WidgetId::next();
|
||||
let widget = ModularWidget::new(())
|
||||
.lifecycle_fn(|_, ctx, event| {
|
||||
if let LifeCycle::WidgetAdded = event {
|
||||
ctx.register_for_focus();
|
||||
}
|
||||
})
|
||||
.accepts_focus(true)
|
||||
.text_event_fn(|_, ctx, _event| {
|
||||
ctx.capture_pointer();
|
||||
})
|
||||
|
|
|
@ -8,9 +8,6 @@ expression: record
|
|||
L(
|
||||
WidgetAdded,
|
||||
),
|
||||
L(
|
||||
BuildFocusChain,
|
||||
),
|
||||
Layout(
|
||||
0.0W×0.0H,
|
||||
),
|
||||
|
|
|
@ -244,9 +244,6 @@ impl Widget for Textbox {
|
|||
|
||||
fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle) {
|
||||
match event {
|
||||
LifeCycle::WidgetAdded => {
|
||||
ctx.register_as_text_input();
|
||||
}
|
||||
LifeCycle::DisabledChanged(disabled) => {
|
||||
if self.show_disabled {
|
||||
if *disabled {
|
||||
|
@ -258,9 +255,6 @@ impl Widget for Textbox {
|
|||
// TODO: Parley seems to require a relayout when colours change
|
||||
ctx.request_layout();
|
||||
}
|
||||
LifeCycle::BuildFocusChain => {
|
||||
ctx.register_for_focus();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -340,6 +334,14 @@ impl Widget for Textbox {
|
|||
SmallVec::new()
|
||||
}
|
||||
|
||||
fn accepts_focus(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn accepts_text_input(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn make_trace_span(&self) -> Span {
|
||||
trace_span!("Textbox")
|
||||
}
|
||||
|
|
|
@ -151,9 +151,31 @@ pub trait Widget: AsAny {
|
|||
/// responsible for visiting all their children during `layout` and `register_children`.
|
||||
fn children_ids(&self) -> SmallVec<[WidgetId; 16]>;
|
||||
|
||||
// TODO - Rename
|
||||
// TODO - Document
|
||||
fn skip_pointer(&self) -> bool {
|
||||
/// Whether this widget gets pointer events and hovered status. True by default.
|
||||
///
|
||||
/// If false, the widget will be treated as "transparent" for the pointer, meaning
|
||||
/// that the pointer will be considered as hovering whatever is under this widget.
|
||||
///
|
||||
/// **Note:** The value returned by this method is cached at widget creation and can't be changed.
|
||||
fn accepts_pointer_interaction(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Whether this widget gets text focus. False by default.
|
||||
///
|
||||
/// If true, pressing Tab can focus this widget.
|
||||
///
|
||||
/// **Note:** The value returned by this method is cached at widget creation and can't be changed.
|
||||
fn accepts_focus(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Whether this widget gets IME events. False by default.
|
||||
///
|
||||
/// If true, focusing this widget will start an IME session.
|
||||
///
|
||||
/// **Note:** The value returned by this method is cached at widget creation and can't be changed.
|
||||
fn accepts_text_input(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
@ -220,7 +242,7 @@ pub trait Widget: AsAny {
|
|||
// The position must be inside the child's layout and inside the child's clip path (if
|
||||
// any).
|
||||
if !child.ctx().is_stashed()
|
||||
&& !child.widget.skip_pointer()
|
||||
&& child.ctx().accepts_pointer_interaction()
|
||||
&& child.ctx().window_layout_rect().contains(pos)
|
||||
{
|
||||
return Some(child);
|
||||
|
@ -397,8 +419,16 @@ impl Widget for Box<dyn Widget> {
|
|||
self.deref().children_ids()
|
||||
}
|
||||
|
||||
fn skip_pointer(&self) -> bool {
|
||||
self.deref().skip_pointer()
|
||||
fn accepts_pointer_interaction(&self) -> bool {
|
||||
self.deref().accepts_pointer_interaction()
|
||||
}
|
||||
|
||||
fn accepts_focus(&self) -> bool {
|
||||
self.deref().accepts_focus()
|
||||
}
|
||||
|
||||
fn accepts_text_input(&self) -> bool {
|
||||
self.deref().accepts_text_input()
|
||||
}
|
||||
|
||||
fn make_trace_span(&self) -> Span {
|
||||
|
|
|
@ -62,12 +62,17 @@ pub(crate) struct WidgetState {
|
|||
/// the baseline. Widgets that contain text or controls that expect to be
|
||||
/// laid out alongside text can set this as appropriate.
|
||||
pub(crate) baseline_offset: f64,
|
||||
// TODO - Remove
|
||||
pub(crate) is_portal: bool,
|
||||
|
||||
/// Tracks whether widget gets pointer events.
|
||||
/// Should be immutable after `WidgetAdded` event.
|
||||
pub(crate) accepts_pointer_interaction: bool,
|
||||
/// Tracks whether widget gets text focus.
|
||||
/// Should be immutable after `WidgetAdded` event.
|
||||
pub(crate) accepts_focus: bool,
|
||||
|
||||
/// Tracks whether widget is eligible for IME events.
|
||||
/// Should be immutable after `WidgetAdded` event.
|
||||
pub(crate) is_text_input: bool,
|
||||
pub(crate) accepts_text_input: bool,
|
||||
/// The area of the widget that is being edited by
|
||||
/// an IME, in local coordinates.
|
||||
pub(crate) ime_area: Option<Rect>,
|
||||
|
@ -145,9 +150,6 @@ pub(crate) struct WidgetState {
|
|||
/// Descendants of the focused widget are not in the focused path.
|
||||
pub(crate) has_focus: bool,
|
||||
|
||||
/// Whether this specific widget is in the focus chain.
|
||||
pub(crate) in_focus_chain: bool,
|
||||
|
||||
// --- DEBUG INFO ---
|
||||
// Used in event/lifecycle/etc methods that are expected to be called recursively
|
||||
// on a widget's children, to make sure each child was visited.
|
||||
|
@ -173,8 +175,9 @@ impl WidgetState {
|
|||
is_expecting_place_child_call: false,
|
||||
paint_insets: Insets::ZERO,
|
||||
local_paint_rect: Rect::ZERO,
|
||||
is_portal: false,
|
||||
is_text_input: false,
|
||||
accepts_pointer_interaction: true,
|
||||
accepts_focus: false,
|
||||
accepts_text_input: false,
|
||||
ime_area: None,
|
||||
clip: Default::default(),
|
||||
translation: Vec2::ZERO,
|
||||
|
@ -195,7 +198,6 @@ impl WidgetState {
|
|||
request_accessibility: true,
|
||||
needs_accessibility: true,
|
||||
has_focus: false,
|
||||
in_focus_chain: false,
|
||||
request_anim: true,
|
||||
needs_anim: true,
|
||||
needs_update_disabled: true,
|
||||
|
|
Loading…
Reference in New Issue