Checkpoint

This commit is contained in:
Nathan Sobo
2023-11-13 16:40:29 -07:00
parent b918c27cf9
commit aec7955ccf
8 changed files with 894 additions and 4 deletions
View File
+101
View File
@@ -0,0 +1,101 @@
# Key Dispatch
GPUI is designed for keyboard-first interactivity.
To expose functionality to the mouse, you render a button with a click handler.
To expose functionality to the keyboard, you bind an *action* in a *key context*.
Actions are similar to framework-level events like `MouseDown`, `KeyDown`, etc, but you can define them yourself:
```rust
mod menu {
#[gpui::action]
struct MoveUp;
#[gpui::action]
struct MoveDown;
}
```
Actions are frequently unit structs, for which we have a macro. The above could also be written:
```rust
mod menu {
actions!(MoveUp, MoveDown);
}
```
Actions can also be more complex types:
```rust
mod menu {
#[gpui::action]
struct Move {
direction: Direction,
select: bool,
}
}
```
To bind actions, chain `on_action` on to your element:
```rust
impl Render for Menu {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component {
div()
.on_action(|this: &mut Menu, move: &MoveUp, cx: &mut ViewContext<Menu>| {
// ...
})
.on_action(|this, move: &MoveDown, cx| {
// ...
})
.children(todo!())
}
}
```
In order to bind keys to actions, you need to declare a *key context* for part of the element tree by calling `key_context`.
```rust
impl Render for Menu {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component {
div()
.key_context("menu")
.on_action(|this: &mut Menu, move: &MoveUp, cx: &mut ViewContext<Menu>| {
// ...
})
.on_action(|this, move: &MoveDown, cx| {
// ...
})
.children(todo!())
}
}
```
Now you can target your context in the keymap. Note how actions are identified in the keymap by their fully-qualified type name.
```json
{
"context": "menu",
"bindings": {
"up": "menu::MoveUp",
"down": "menu::MoveDown"
}
}
```
If you had opted for the more complex type definition, you'd provide the serialized representation of the action alongside the name:
```json
{
"context": "menu",
"bindings": {
"up": ["menu::Move", {direction: "up", select: false}]
"down": ["menu::Move", {direction: "down", select: false}]
"shift-up": ["menu::Move", {direction: "up", select: true}]
"shift-down": ["menu::Move", {direction: "down", select: true}]
}
}
```
+1 -1
View File
@@ -1068,7 +1068,7 @@ impl<G: 'static> DerefMut for GlobalLease<G> {
/// Contains state associated with an active drag operation, started by dragging an element
/// within the window or by dragging into the app from the underlying platform.
pub(crate) struct AnyDrag {
pub struct AnyDrag {
pub view: AnyView,
pub cursor_offset: Point<Pixels>,
}
+1
View File
@@ -1,5 +1,6 @@
mod div;
mod img;
mod node;
mod svg;
mod text;
mod uniform_list;
+2 -2
View File
@@ -252,7 +252,7 @@ where
cx: &mut ViewContext<V>,
) -> LayoutId {
let style = self.compute_style(Bounds::default(), element_state, cx);
style.apply_text_style(cx, |cx| {
style.with_text_style(cx, |cx| {
self.with_element_id(cx, |this, _global_id, cx| {
let layout_ids = this
.children
@@ -318,7 +318,7 @@ where
);
});
cx.with_z_index(1, |cx| {
style.apply_text_style(cx, |cx| {
style.with_text_style(cx, |cx| {
style.apply_overflow(bounds, cx, |cx| {
let scroll_offset = element_state.interactive.scroll_offset();
cx.with_element_offset(scroll_offset, |cx| {
+767
View File
@@ -0,0 +1,767 @@
use crate::{
point, Action, AnyDrag, AnyElement, AnyView, AppContext, BorrowWindow, Bounds, ClickEvent,
DispatchPhase, Element, FocusHandle, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId,
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, Render,
ScrollWheelEvent, SharedString, Style, StyleRefinement, Styled, View, ViewContext, Visibility,
};
use collections::HashMap;
use refineable::Refineable;
use smallvec::SmallVec;
use std::{
any::{Any, TypeId},
marker::PhantomData,
sync::Arc,
};
pub struct GroupStyle {
pub group: SharedString,
pub style: StyleRefinement,
}
pub trait InteractiveComponent<V: 'static> {
fn interactivity(&mut self) -> &mut Interactivity<V>;
fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.interactivity().hover_style = f(StyleRefinement::default());
self
}
fn group_hover(
mut self,
group_name: impl Into<SharedString>,
f: impl FnOnce(StyleRefinement) -> StyleRefinement,
) -> Self
where
Self: Sized,
{
self.interactivity().group_hover_style = Some(GroupStyle {
group: group_name.into(),
style: f(StyleRefinement::default()),
});
self
}
fn on_mouse_down(
mut self,
button: MouseButton,
handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.interactivity().mouse_down_listeners.push(Box::new(
move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble
&& event.button == button
&& bounds.contains_point(&event.position)
{
handler(view, event, cx)
}
},
));
self
}
fn on_mouse_up(
mut self,
button: MouseButton,
handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.interactivity().mouse_up_listeners.push(Box::new(
move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble
&& event.button == button
&& bounds.contains_point(&event.position)
{
handler(view, event, cx)
}
},
));
self
}
fn on_mouse_down_out(
mut self,
handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.interactivity().mouse_down_listeners.push(Box::new(
move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Capture && !bounds.contains_point(&event.position) {
handler(view, event, cx)
}
},
));
self
}
fn on_mouse_up_out(
mut self,
button: MouseButton,
handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.interactivity().mouse_up_listeners.push(Box::new(
move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Capture
&& event.button == button
&& !bounds.contains_point(&event.position)
{
handler(view, event, cx);
}
},
));
self
}
fn on_mouse_move(
mut self,
handler: impl Fn(&mut V, &MouseMoveEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.interactivity().mouse_move_listeners.push(Box::new(
move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
handler(view, event, cx);
}
},
));
self
}
fn on_scroll_wheel(
mut self,
handler: impl Fn(&mut V, &ScrollWheelEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.interactivity().scroll_wheel_listeners.push(Box::new(
move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
handler(view, event, cx);
}
},
));
self
}
/// Capture the given action, fires during the capture phase
fn capture_action<A: Action>(
mut self,
listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.interactivity().action_listeners.push((
TypeId::of::<A>(),
Box::new(move |view, action, phase, cx| {
let action = action.downcast_ref().unwrap();
if phase == DispatchPhase::Capture {
listener(view, action, cx)
}
}),
));
self
}
/// Add a listener for the given action, fires during the bubble event phase
fn on_action<A: Action>(
mut self,
listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.interactivity().action_listeners.push((
TypeId::of::<A>(),
Box::new(move |view, action, phase, cx| {
let action = action.downcast_ref().unwrap();
if phase == DispatchPhase::Bubble {
listener(view, action, cx)
}
}),
));
self
}
fn on_key_down(
mut self,
listener: impl Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.interactivity()
.key_down_listeners
.push(Box::new(move |view, event, phase, cx| {
listener(view, event, phase, cx)
}));
self
}
fn on_key_up(
mut self,
listener: impl Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.interactivity()
.key_up_listeners
.push(Box::new(move |view, event, phase, cx| {
listener(view, event, phase, cx)
}));
self
}
fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.interactivity()
.drag_over_styles
.push((TypeId::of::<S>(), f(StyleRefinement::default())));
self
}
fn group_drag_over<S: 'static>(
mut self,
group_name: impl Into<SharedString>,
f: impl FnOnce(StyleRefinement) -> StyleRefinement,
) -> Self
where
Self: Sized,
{
self.interactivity().group_drag_over_styles.push((
TypeId::of::<S>(),
GroupStyle {
group: group_name.into(),
style: f(StyleRefinement::default()),
},
));
self
}
fn on_drop<W: 'static>(
mut self,
listener: impl Fn(&mut V, View<W>, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.interactivity().drop_listeners.push((
TypeId::of::<W>(),
Box::new(move |view, dragged_view, cx| {
listener(view, dragged_view.downcast().unwrap(), cx);
}),
));
self
}
}
pub trait StatefulInteractiveComponent<V: 'static> {
fn interactivity(&mut self) -> &mut StatefulInteractivity<V>;
fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.interactivity().active_style = f(StyleRefinement::default());
self
}
fn group_active(
mut self,
group_name: impl Into<SharedString>,
f: impl FnOnce(StyleRefinement) -> StyleRefinement,
) -> Self
where
Self: Sized,
{
self.interactivity().group_active_style = Some(GroupStyle {
group: group_name.into(),
style: f(StyleRefinement::default()),
});
self
}
fn on_click(
mut self,
listener: impl Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.interactivity()
.click_listeners
.push(Box::new(move |view, event, cx| listener(view, event, cx)));
self
}
fn on_drag<W>(
mut self,
listener: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
) -> Self
where
Self: Sized,
W: 'static + Render,
{
debug_assert!(
self.interactivity().drag_listener.is_none(),
"calling on_drag more than once on the same element is not supported"
);
self.interactivity().drag_listener =
Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag {
view: listener(view_state, cx).into(),
cursor_offset,
}));
self
}
fn on_hover(mut self, listener: impl 'static + Fn(&mut V, bool, &mut ViewContext<V>)) -> Self
where
Self: Sized,
{
debug_assert!(
self.interactivity().hover_listener.is_none(),
"calling on_hover more than once on the same element is not supported"
);
self.interactivity().hover_listener = Some(Box::new(listener));
self
}
fn tooltip<W>(
mut self,
build_tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
) -> Self
where
Self: Sized,
W: 'static + Render,
{
debug_assert!(
self.interactivity().tooltip_builder.is_none(),
"calling tooltip more than once on the same element is not supported"
);
self.interactivity().tooltip_builder = Some(Arc::new(move |view_state, cx| {
build_tooltip(view_state, cx).into()
}));
self
}
}
pub trait FocusableComponent<V> {
fn focusability(&mut self) -> &mut Focusability<V>;
fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.focusability().focus_style = f(StyleRefinement::default());
self
}
fn focus_in(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.focusability().focus_in_style = f(StyleRefinement::default());
self
}
fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
// self.focusability(). (f(StyleRefinement::default()));
self
}
fn on_focus(
mut self,
listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.focusability()
.focus_listeners
.push(Box::new(move |view, focus_handle, event, cx| {
if event.focused.as_ref() == Some(focus_handle) {
listener(view, event, cx)
}
}));
self
}
fn on_blur(
mut self,
listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.focusability()
.focus_listeners
.push(Box::new(move |view, focus_handle, event, cx| {
if event.blurred.as_ref() == Some(focus_handle) {
listener(view, event, cx)
}
}));
self
}
fn on_focus_in(
mut self,
listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.focusability()
.focus_listeners
.push(Box::new(move |view, focus_handle, event, cx| {
let descendant_blurred = event
.blurred
.as_ref()
.map_or(false, |blurred| focus_handle.contains(blurred, cx));
let descendant_focused = event
.focused
.as_ref()
.map_or(false, |focused| focus_handle.contains(focused, cx));
if !descendant_blurred && descendant_focused {
listener(view, event, cx)
}
}));
self
}
fn on_focus_out(
mut self,
listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.focusability()
.focus_listeners
.push(Box::new(move |view, focus_handle, event, cx| {
let descendant_blurred = event
.blurred
.as_ref()
.map_or(false, |blurred| focus_handle.contains(blurred, cx));
let descendant_focused = event
.focused
.as_ref()
.map_or(false, |focused| focus_handle.contains(focused, cx));
if descendant_blurred && !descendant_focused {
listener(view, event, cx)
}
}));
self
}
}
pub type FocusListeners<V> = SmallVec<[FocusListener<V>; 2]>;
pub type FocusListener<V> =
Box<dyn Fn(&mut V, &FocusHandle, &FocusEvent, &mut ViewContext<V>) + 'static>;
pub type MouseDownListener<V> = Box<
dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
>;
pub type MouseUpListener<V> = Box<
dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
>;
pub type MouseMoveListener<V> = Box<
dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>) + 'static,
>;
pub type ScrollWheelListener<V> = Box<
dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+ 'static,
>;
pub type ClickListener<V> = Box<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static>;
pub type DragListener<V> =
Box<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + 'static>;
type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static;
pub type HoverListener<V> = Box<dyn Fn(&mut V, bool, &mut ViewContext<V>) + 'static>;
pub type TooltipBuilder<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>;
pub type KeyDownListener<V> =
Box<dyn Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
pub type KeyUpListener<V> =
Box<dyn Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
pub type ActionListener<V> =
Box<dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext<V>) + 'static>;
pub struct FocusEvent {
pub blurred: Option<FocusHandle>,
pub focused: Option<FocusHandle>,
}
pub struct Node<V> {
style: StyleRefinement,
key_context: KeyContext,
interactivity: Interactivity<V>,
children: Vec<AnyElement<V>>,
}
pub struct Interactivity<V> {
group: Option<SharedString>,
pub dispatch_context: KeyContext,
pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
pub key_down_listeners: SmallVec<[KeyDownListener<V>; 2]>,
pub key_up_listeners: SmallVec<[KeyUpListener<V>; 2]>,
pub action_listeners: SmallVec<[(TypeId, ActionListener<V>); 8]>,
pub hover_style: StyleRefinement,
pub group_hover_style: Option<GroupStyle>,
drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
scroll_offset: Point<Pixels>,
}
impl<V> Node<V> {
fn compute_style(&self) -> Style {
let mut style = Style::default();
style.refine(&self.style);
style
}
}
impl<V> Styled for Node<V> {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.style
}
}
impl<V: 'static> InteractiveComponent<V> for Node<V> {
fn interactivity(&mut self) -> &mut Interactivity<V> {
&mut self.interactivity
}
}
pub struct NodeState {
child_layout_ids: SmallVec<[LayoutId; 4]>,
}
impl<V: 'static> Element<V> for Node<V> {
type ElementState = NodeState;
fn id(&self) -> Option<crate::ElementId> {
None
}
fn initialize(
&mut self,
view_state: &mut V,
_: Option<Self::ElementState>,
cx: &mut ViewContext<V>,
) -> Self::ElementState {
for child in &mut self.children {
child.initialize(view_state, cx);
}
NodeState {
child_layout_ids: SmallVec::new(),
}
}
fn layout(
&mut self,
view_state: &mut V,
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) -> crate::LayoutId {
let style = self.compute_style();
style.with_text_style(cx, |cx| {
element_state.child_layout_ids = self
.children
.iter_mut()
.map(|child| child.layout(view_state, cx))
.collect::<SmallVec<_>>();
cx.request_layout(&style, element_state.child_layout_ids.iter().copied())
})
}
fn paint(
&mut self,
bounds: Bounds<Pixels>,
view_state: &mut V,
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) {
let style = self.compute_style();
if style.visibility == Visibility::Hidden {
return;
}
if let Some(mouse_cursor) = style.mouse_cursor {
let hovered = bounds.contains_point(&cx.mouse_position());
if hovered {
cx.set_cursor_style(mouse_cursor);
}
}
if let Some(group) = self.interactivity.group.clone() {
GroupBounds::push(group, bounds, cx);
}
let z_index = style.z_index.unwrap_or(0);
let mut child_min = point(Pixels::MAX, Pixels::MAX);
let mut child_max = Point::default();
let content_size = if element_state.child_layout_ids.is_empty() {
bounds.size
} else {
for child_layout_id in &element_state.child_layout_ids {
let child_bounds = cx.layout_bounds(*child_layout_id);
child_min = child_min.min(&child_bounds.origin);
child_max = child_max.max(&child_bounds.lower_right());
}
(child_max - child_min).into()
};
cx.with_z_index(z_index, |cx| {
cx.with_z_index(0, |cx| {
style.paint(bounds, cx);
});
cx.with_z_index(1, |cx| {
style.with_text_style(cx, |cx| {
style.apply_overflow(bounds, cx, |cx| {
let scroll_offset = self.interactivity.scroll_offset;
cx.with_element_offset2(scroll_offset, |cx| {
for child in &mut self.children {
child.paint(view_state, cx);
}
});
})
})
});
});
if let Some(group) = self.interactivity.group.as_ref() {
GroupBounds::pop(group, cx);
}
}
}
#[derive(Default)]
pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
impl GroupBounds {
pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
cx.default_global::<Self>()
.0
.get(name)
.and_then(|bounds_stack| bounds_stack.last())
.cloned()
}
pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
cx.default_global::<Self>()
.0
.entry(name)
.or_default()
.push(bounds);
}
pub fn pop(name: &SharedString, cx: &mut AppContext) {
cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
}
}
pub struct Focusable<V, E> {
focusability: Focusability<V>,
view_type: PhantomData<V>,
element: E,
}
pub struct Focusability<V> {
focus_handle: Option<FocusHandle>,
focus_listeners: FocusListeners<V>,
focus_style: StyleRefinement,
focus_in_style: StyleRefinement,
in_focus_style: StyleRefinement,
}
impl<V, E> FocusableComponent<V> for Focusable<V, E> {
fn focusability(&mut self) -> &mut Focusability<V> {
&mut self.focusability
}
}
impl<V: 'static, E: InteractiveComponent<V>> InteractiveComponent<V> for Focusable<V, E> {
fn interactivity(&mut self) -> &mut Interactivity<V> {
self.element.interactivity()
}
}
impl<V: 'static, E: StatefulInteractiveComponent<V>> StatefulInteractiveComponent<V>
for Focusable<V, E>
{
fn interactivity(&mut self) -> &mut StatefulInteractivity<V> {
self.element.interactivity()
}
}
pub struct Stateful<V, E> {
id: SharedString,
interactivity: StatefulInteractivity<V>,
view_type: PhantomData<V>,
element: E,
}
pub struct StatefulInteractivity<V> {
click_listeners: SmallVec<[ClickListener<V>; 2]>,
active_style: StyleRefinement,
group_active_style: Option<GroupStyle>,
drag_listener: Option<DragListener<V>>,
hover_listener: Option<HoverListener<V>>,
tooltip_builder: Option<TooltipBuilder<V>>,
}
impl<V: 'static, E> StatefulInteractiveComponent<V> for Stateful<V, E> {
fn interactivity(&mut self) -> &mut StatefulInteractivity<V> {
&mut self.interactivity
}
}
impl<V: 'static, E: InteractiveComponent<V>> InteractiveComponent<V> for Stateful<V, E> {
fn interactivity(&mut self) -> &mut Interactivity<V> {
self.element.interactivity()
}
}
impl<V, E: FocusableComponent<V>> FocusableComponent<V> for Stateful<V, E> {
fn focusability(&mut self) -> &mut Focusability<V> {
self.element.focusability()
}
}
+1 -1
View File
@@ -228,7 +228,7 @@ impl Style {
}
}
pub fn apply_text_style<C, F, R>(&self, cx: &mut C, f: F) -> R
pub fn with_text_style<C, F, R>(&self, cx: &mut C, f: F) -> R
where
C: BorrowAppContext,
F: FnOnce(&mut C) -> R,
+21
View File
@@ -1587,6 +1587,27 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
result
}
/// Update the global element offset based on the given offset. This is used to implement
/// scrolling and position drag handles.
fn with_element_offset2<R>(
&mut self,
offset: Point<Pixels>,
f: impl FnOnce(&mut Self) -> R,
) -> R {
if offset.is_zero() {
return f(self);
};
let offset = self.element_offset() + offset;
self.window_mut()
.current_frame
.element_offset_stack
.push(offset);
let result = f(self);
self.window_mut().current_frame.element_offset_stack.pop();
result
}
/// Obtain the current element offset.
fn element_offset(&self) -> Point<Pixels> {
self.window()