Checkpoint
This commit is contained in:
@@ -141,14 +141,14 @@ where
|
||||
pub fn compute_style(
|
||||
&self,
|
||||
bounds: Bounds<Pixels>,
|
||||
state: &InteractiveElementState,
|
||||
element_state: &DivState,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Style {
|
||||
let mut computed_style = Style::default();
|
||||
computed_style.refine(&self.base_style);
|
||||
self.focus.refine_style(&mut computed_style, cx);
|
||||
self.interaction
|
||||
.refine_style(&mut computed_style, bounds, state, cx);
|
||||
.refine_style(&mut computed_style, bounds, &element_state.interactive, cx);
|
||||
computed_style
|
||||
}
|
||||
}
|
||||
@@ -157,13 +157,23 @@ impl<V> Div<V, StatefulInteraction<V>, FocusDisabled>
|
||||
where
|
||||
V: 'static + Send + Sync,
|
||||
{
|
||||
pub fn focusable(
|
||||
pub fn focusable(self) -> Div<V, StatefulInteraction<V>, FocusEnabled<V>> {
|
||||
Div {
|
||||
interaction: self.interaction,
|
||||
focus: FocusEnabled::new(),
|
||||
children: self.children,
|
||||
group: self.group,
|
||||
base_style: self.base_style,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn track_focus(
|
||||
self,
|
||||
handle: &FocusHandle,
|
||||
) -> Div<V, StatefulInteraction<V>, FocusEnabled<V>> {
|
||||
Div {
|
||||
interaction: self.interaction,
|
||||
focus: handle.clone().into(),
|
||||
focus: FocusEnabled::tracked(handle),
|
||||
children: self.children,
|
||||
group: self.group,
|
||||
base_style: self.base_style,
|
||||
@@ -175,7 +185,7 @@ impl<V> Div<V, StatelessInteraction<V>, FocusDisabled>
|
||||
where
|
||||
V: 'static + Send + Sync,
|
||||
{
|
||||
pub fn focusable(
|
||||
pub fn track_focus(
|
||||
self,
|
||||
handle: &FocusHandle,
|
||||
) -> Div<V, StatefulInteraction<V>, FocusEnabled<V>> {
|
||||
@@ -198,10 +208,6 @@ where
|
||||
&mut self.focus.focus_listeners
|
||||
}
|
||||
|
||||
fn handle(&self) -> &FocusHandle {
|
||||
&self.focus.focus_handle
|
||||
}
|
||||
|
||||
fn set_focus_style(&mut self, style: StyleRefinement) {
|
||||
self.focus.focus_style = style;
|
||||
}
|
||||
@@ -215,6 +221,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DivState {
|
||||
interactive: InteractiveElementState,
|
||||
focus_handle: Option<FocusHandle>,
|
||||
}
|
||||
|
||||
impl<V, I, F> Element for Div<V, I, F>
|
||||
where
|
||||
I: ElementInteraction<V>,
|
||||
@@ -222,7 +234,7 @@ where
|
||||
V: 'static + Send + Sync,
|
||||
{
|
||||
type ViewState = V;
|
||||
type ElementState = InteractiveElementState;
|
||||
type ElementState = DivState;
|
||||
|
||||
fn id(&self) -> Option<ElementId> {
|
||||
self.interaction
|
||||
@@ -236,14 +248,17 @@ where
|
||||
element_state: Option<Self::ElementState>,
|
||||
cx: &mut ViewContext<Self::ViewState>,
|
||||
) -> Self::ElementState {
|
||||
self.focus.initialize(cx, |focus_handle, cx| {
|
||||
self.interaction
|
||||
.initialize(element_state, focus_handle, cx, |cx| {
|
||||
let mut element_state = element_state.unwrap_or_default();
|
||||
self.focus
|
||||
.initialize(element_state.focus_handle.take(), cx, |focus_handle, cx| {
|
||||
element_state.focus_handle = focus_handle;
|
||||
self.interaction.initialize(cx, |cx| {
|
||||
for child in &mut self.children {
|
||||
child.initialize(view_state, cx);
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
element_state
|
||||
}
|
||||
|
||||
fn layout(
|
||||
@@ -286,7 +301,8 @@ where
|
||||
style.paint(bounds, cx);
|
||||
|
||||
this.focus.paint(bounds, cx);
|
||||
this.interaction.paint(bounds, element_state, cx);
|
||||
this.interaction
|
||||
.paint(bounds, &element_state.interactive, cx);
|
||||
});
|
||||
|
||||
cx.stack(1, |cx| {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use crate::{
|
||||
div, AnyElement, BorrowWindow, Bounds, Div, Element, ElementFocus, ElementId,
|
||||
ElementInteraction, FocusDisabled, FocusEnabled, FocusListeners, Focusable,
|
||||
InteractiveElementState, IntoAnyElement, LayoutId, Pixels, SharedString, StatefulInteraction,
|
||||
StatefulInteractive, StatelessInteraction, StatelessInteractive, StyleRefinement, Styled,
|
||||
ViewContext,
|
||||
div, AnyElement, BorrowWindow, Bounds, Div, DivState, Element, ElementFocus, ElementId,
|
||||
ElementInteraction, FocusDisabled, FocusEnabled, FocusListeners, Focusable, IntoAnyElement,
|
||||
LayoutId, Pixels, SharedString, StatefulInteraction, StatefulInteractive, StatelessInteraction,
|
||||
StatelessInteractive, StyleRefinement, Styled, ViewContext,
|
||||
};
|
||||
use futures::FutureExt;
|
||||
use util::ResultExt;
|
||||
@@ -78,7 +77,7 @@ where
|
||||
F: ElementFocus<V>,
|
||||
{
|
||||
type ViewState = V;
|
||||
type ElementState = InteractiveElementState;
|
||||
type ElementState = DivState;
|
||||
|
||||
fn id(&self) -> Option<crate::ElementId> {
|
||||
self.base.id()
|
||||
@@ -192,8 +191,4 @@ where
|
||||
fn set_in_focus_style(&mut self, style: StyleRefinement) {
|
||||
self.base.set_in_focus_style(style)
|
||||
}
|
||||
|
||||
fn handle(&self) -> &crate::FocusHandle {
|
||||
self.base.handle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
div, AnyElement, Bounds, Div, Element, ElementFocus, ElementId, ElementInteraction,
|
||||
FocusDisabled, FocusEnabled, FocusListeners, Focusable, InteractiveElementState,
|
||||
IntoAnyElement, LayoutId, Pixels, SharedString, StatefulInteraction, StatefulInteractive,
|
||||
StatelessInteraction, StatelessInteractive, StyleRefinement, Styled, ViewContext,
|
||||
div, AnyElement, Bounds, Div, DivState, Element, ElementFocus, ElementId, ElementInteraction,
|
||||
FocusDisabled, FocusEnabled, FocusListeners, Focusable, IntoAnyElement, LayoutId, Pixels,
|
||||
SharedString, StatefulInteraction, StatefulInteractive, StatelessInteraction,
|
||||
StatelessInteractive, StyleRefinement, Styled, ViewContext,
|
||||
};
|
||||
use util::ResultExt;
|
||||
|
||||
@@ -68,7 +68,7 @@ where
|
||||
F: ElementFocus<V>,
|
||||
{
|
||||
type ViewState = V;
|
||||
type ElementState = InteractiveElementState;
|
||||
type ElementState = DivState;
|
||||
|
||||
fn id(&self) -> Option<crate::ElementId> {
|
||||
self.base.id()
|
||||
@@ -165,8 +165,4 @@ where
|
||||
fn set_in_focus_style(&mut self, style: StyleRefinement) {
|
||||
self.base.set_in_focus_style(style)
|
||||
}
|
||||
|
||||
fn handle(&self) -> &crate::FocusHandle {
|
||||
self.base.handle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,14 +9,13 @@ use std::sync::Arc;
|
||||
pub type FocusListeners<V> = SmallVec<[FocusListener<V>; 2]>;
|
||||
|
||||
pub type FocusListener<V> =
|
||||
Arc<dyn Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
|
||||
Arc<dyn Fn(&mut V, &FocusHandle, &FocusEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
|
||||
|
||||
pub trait Focusable: Element {
|
||||
fn focus_listeners(&mut self) -> &mut FocusListeners<Self::ViewState>;
|
||||
fn set_focus_style(&mut self, style: StyleRefinement);
|
||||
fn set_focus_in_style(&mut self, style: StyleRefinement);
|
||||
fn set_in_focus_style(&mut self, style: StyleRefinement);
|
||||
fn handle(&self) -> &FocusHandle;
|
||||
|
||||
fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
|
||||
where
|
||||
@@ -52,10 +51,9 @@ pub trait Focusable: Element {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let handle = self.handle().clone();
|
||||
self.focus_listeners()
|
||||
.push(Arc::new(move |view, event, cx| {
|
||||
if event.focused.as_ref() == Some(&handle) {
|
||||
.push(Arc::new(move |view, focus_handle, event, cx| {
|
||||
if event.focused.as_ref() == Some(focus_handle) {
|
||||
listener(view, event, cx)
|
||||
}
|
||||
}));
|
||||
@@ -72,10 +70,9 @@ pub trait Focusable: Element {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let handle = self.handle().clone();
|
||||
self.focus_listeners()
|
||||
.push(Arc::new(move |view, event, cx| {
|
||||
if event.blurred.as_ref() == Some(&handle) {
|
||||
.push(Arc::new(move |view, focus_handle, event, cx| {
|
||||
if event.blurred.as_ref() == Some(focus_handle) {
|
||||
listener(view, event, cx)
|
||||
}
|
||||
}));
|
||||
@@ -92,17 +89,16 @@ pub trait Focusable: Element {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let handle = self.handle().clone();
|
||||
self.focus_listeners()
|
||||
.push(Arc::new(move |view, event, cx| {
|
||||
.push(Arc::new(move |view, focus_handle, event, cx| {
|
||||
let descendant_blurred = event
|
||||
.blurred
|
||||
.as_ref()
|
||||
.map_or(false, |blurred| handle.contains(blurred, cx));
|
||||
.map_or(false, |blurred| focus_handle.contains(blurred, cx));
|
||||
let descendant_focused = event
|
||||
.focused
|
||||
.as_ref()
|
||||
.map_or(false, |focused| handle.contains(focused, cx));
|
||||
.map_or(false, |focused| focus_handle.contains(focused, cx));
|
||||
|
||||
if !descendant_blurred && descendant_focused {
|
||||
listener(view, event, cx)
|
||||
@@ -121,17 +117,16 @@ pub trait Focusable: Element {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let handle = self.handle().clone();
|
||||
self.focus_listeners()
|
||||
.push(Arc::new(move |view, event, cx| {
|
||||
.push(Arc::new(move |view, focus_handle, event, cx| {
|
||||
let descendant_blurred = event
|
||||
.blurred
|
||||
.as_ref()
|
||||
.map_or(false, |blurred| handle.contains(blurred, cx));
|
||||
.map_or(false, |blurred| focus_handle.contains(blurred, cx));
|
||||
let descendant_focused = event
|
||||
.focused
|
||||
.as_ref()
|
||||
.map_or(false, |focused| handle.contains(focused, cx));
|
||||
.map_or(false, |focused| focus_handle.contains(focused, cx));
|
||||
if descendant_blurred && !descendant_focused {
|
||||
listener(view, event, cx)
|
||||
}
|
||||
@@ -142,17 +137,25 @@ pub trait Focusable: Element {
|
||||
|
||||
pub trait ElementFocus<V: 'static + Send + Sync>: 'static + Send + Sync {
|
||||
fn as_focusable(&self) -> Option<&FocusEnabled<V>>;
|
||||
fn as_focusable_mut(&mut self) -> Option<&mut FocusEnabled<V>>;
|
||||
|
||||
fn initialize<R>(
|
||||
&self,
|
||||
&mut self,
|
||||
focus_handle: Option<FocusHandle>,
|
||||
cx: &mut ViewContext<V>,
|
||||
f: impl FnOnce(Option<FocusHandle>, &mut ViewContext<V>) -> R,
|
||||
) -> R {
|
||||
if let Some(focusable) = self.as_focusable() {
|
||||
if let Some(focusable) = self.as_focusable_mut() {
|
||||
let focus_handle = focusable
|
||||
.focus_handle
|
||||
.get_or_insert_with(|| focus_handle.unwrap_or_else(|| cx.focus_handle()))
|
||||
.clone();
|
||||
for listener in focusable.focus_listeners.iter().cloned() {
|
||||
cx.on_focus_changed(move |view, event, cx| listener(view, event, cx));
|
||||
let focus_handle = focus_handle.clone();
|
||||
cx.on_focus_changed(move |view, event, cx| {
|
||||
listener(view, &focus_handle, event, cx)
|
||||
});
|
||||
}
|
||||
let focus_handle = focusable.focus_handle.clone();
|
||||
cx.with_focus(focus_handle.clone(), |cx| f(Some(focus_handle), cx))
|
||||
} else {
|
||||
f(None, cx)
|
||||
@@ -161,15 +164,19 @@ pub trait ElementFocus<V: 'static + Send + Sync>: 'static + Send + Sync {
|
||||
|
||||
fn refine_style(&self, style: &mut Style, cx: &WindowContext) {
|
||||
if let Some(focusable) = self.as_focusable() {
|
||||
if focusable.focus_handle.contains_focused(cx) {
|
||||
let focus_handle = focusable
|
||||
.focus_handle
|
||||
.as_ref()
|
||||
.expect("must call initialize before refine_style");
|
||||
if focus_handle.contains_focused(cx) {
|
||||
style.refine(&focusable.focus_in_style);
|
||||
}
|
||||
|
||||
if focusable.focus_handle.within_focused(cx) {
|
||||
if focus_handle.within_focused(cx) {
|
||||
style.refine(&focusable.in_focus_style);
|
||||
}
|
||||
|
||||
if focusable.focus_handle.is_focused(cx) {
|
||||
if focus_handle.is_focused(cx) {
|
||||
style.refine(&focusable.focus_style);
|
||||
}
|
||||
}
|
||||
@@ -177,7 +184,10 @@ pub trait ElementFocus<V: 'static + Send + Sync>: 'static + Send + Sync {
|
||||
|
||||
fn paint(&self, bounds: Bounds<Pixels>, cx: &mut WindowContext) {
|
||||
if let Some(focusable) = self.as_focusable() {
|
||||
let focus_handle = focusable.focus_handle.clone();
|
||||
let focus_handle = focusable
|
||||
.focus_handle
|
||||
.clone()
|
||||
.expect("must call initialize before paint");
|
||||
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
|
||||
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
|
||||
if !cx.default_prevented() {
|
||||
@@ -191,13 +201,38 @@ pub trait ElementFocus<V: 'static + Send + Sync>: 'static + Send + Sync {
|
||||
}
|
||||
|
||||
pub struct FocusEnabled<V: 'static + Send + Sync> {
|
||||
pub focus_handle: FocusHandle,
|
||||
pub focus_handle: Option<FocusHandle>,
|
||||
pub focus_listeners: FocusListeners<V>,
|
||||
pub focus_style: StyleRefinement,
|
||||
pub focus_in_style: StyleRefinement,
|
||||
pub in_focus_style: StyleRefinement,
|
||||
}
|
||||
|
||||
impl<V> FocusEnabled<V>
|
||||
where
|
||||
V: 'static + Send + Sync,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
focus_handle: None,
|
||||
focus_listeners: FocusListeners::default(),
|
||||
focus_style: StyleRefinement::default(),
|
||||
focus_in_style: StyleRefinement::default(),
|
||||
in_focus_style: StyleRefinement::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tracked(handle: &FocusHandle) -> Self {
|
||||
Self {
|
||||
focus_handle: Some(handle.clone()),
|
||||
focus_listeners: FocusListeners::default(),
|
||||
focus_style: StyleRefinement::default(),
|
||||
focus_in_style: StyleRefinement::default(),
|
||||
in_focus_style: StyleRefinement::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> ElementFocus<V> for FocusEnabled<V>
|
||||
where
|
||||
V: 'static + Send + Sync,
|
||||
@@ -205,6 +240,10 @@ where
|
||||
fn as_focusable(&self) -> Option<&FocusEnabled<V>> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn as_focusable_mut(&mut self) -> Option<&mut FocusEnabled<V>> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> From<FocusHandle> for FocusEnabled<V>
|
||||
@@ -213,7 +252,7 @@ where
|
||||
{
|
||||
fn from(value: FocusHandle) -> Self {
|
||||
Self {
|
||||
focus_handle: value,
|
||||
focus_handle: Some(value),
|
||||
focus_listeners: FocusListeners::default(),
|
||||
focus_style: StyleRefinement::default(),
|
||||
focus_in_style: StyleRefinement::default(),
|
||||
@@ -231,4 +270,8 @@ where
|
||||
fn as_focusable(&self) -> Option<&FocusEnabled<V>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_focusable_mut(&mut self) -> Option<&mut FocusEnabled<V>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,13 +305,11 @@ pub trait ElementInteraction<V: 'static + Send + Sync>: 'static + Send + Sync {
|
||||
fn as_stateful(&self) -> Option<&StatefulInteraction<V>>;
|
||||
fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>>;
|
||||
|
||||
fn initialize(
|
||||
fn initialize<R>(
|
||||
&mut self,
|
||||
element_state: Option<InteractiveElementState>,
|
||||
focus_handle: Option<FocusHandle>,
|
||||
cx: &mut ViewContext<V>,
|
||||
f: impl FnOnce(&mut ViewContext<V>),
|
||||
) -> InteractiveElementState {
|
||||
f: impl FnOnce(&mut ViewContext<V>) -> R,
|
||||
) -> R {
|
||||
if let Some(stateful) = self.as_stateful_mut() {
|
||||
cx.with_element_id(stateful.id.clone(), |global_id, cx| {
|
||||
stateful.key_listeners.push((
|
||||
@@ -329,19 +327,15 @@ pub trait ElementInteraction<V: 'static + Send + Sync>: 'static + Send + Sync {
|
||||
None
|
||||
}),
|
||||
));
|
||||
let mut element_state = stateful.stateless.initialize(element_state, None, cx, f);
|
||||
element_state.focus_handle = focus_handle
|
||||
.or(element_state.focus_handle.take())
|
||||
.or_else(|| cx.focused());
|
||||
let result = stateful.stateless.initialize(cx, f);
|
||||
stateful.key_listeners.pop();
|
||||
element_state
|
||||
result
|
||||
})
|
||||
} else {
|
||||
let stateless = self.as_stateless();
|
||||
cx.with_key_dispatch_context(stateless.dispatch_context.clone(), |cx| {
|
||||
cx.with_key_listeners(&stateless.key_listeners, f)
|
||||
});
|
||||
element_state.unwrap_or_default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,7 +607,6 @@ impl ActiveState {
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InteractiveElementState {
|
||||
focus_handle: Option<FocusHandle>,
|
||||
active_state: Arc<Mutex<ActiveState>>,
|
||||
pending_click: Arc<Mutex<Option<MouseDownEvent>>>,
|
||||
}
|
||||
|
||||
@@ -76,12 +76,12 @@ impl FocusStory {
|
||||
let color_5 = theme.lowest.variant.default.foreground;
|
||||
let color_6 = theme.highest.negative.default.foreground;
|
||||
|
||||
let parent = cx.focus_handle();
|
||||
let child_1 = cx.focus_handle();
|
||||
let child_2 = cx.focus_handle();
|
||||
view(cx.entity(|cx| ()), move |_, cx| {
|
||||
div()
|
||||
.focusable(&parent)
|
||||
.id("parent")
|
||||
.focusable()
|
||||
.context("parent")
|
||||
.on_action(|_, action: &ActionA, phase, cx| {
|
||||
println!("Action A dispatched on parent during {:?}", phase);
|
||||
@@ -105,7 +105,7 @@ impl FocusStory {
|
||||
.focus_in(|style| style.bg(color_3))
|
||||
.child(
|
||||
div()
|
||||
.focusable(&child_1)
|
||||
.track_focus(&child_1)
|
||||
.context("child-1")
|
||||
.on_action(|_, action: &ActionB, phase, cx| {
|
||||
println!("Action B dispatched on child 1 during {:?}", phase);
|
||||
@@ -129,7 +129,7 @@ impl FocusStory {
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.focusable(&child_2)
|
||||
.track_focus(&child_2)
|
||||
.context("child-2")
|
||||
.on_action(|_, action: &ActionC, phase, cx| {
|
||||
println!("Action C dispatched on child 2 during {:?}", phase);
|
||||
|
||||
Reference in New Issue
Block a user