Checkpoint

This commit is contained in:
Nathan Sobo 2023-11-14 01:15:48 -07:00
parent ce30a689a0
commit 27fb381cca
82 changed files with 661 additions and 1907 deletions

View file

@ -1,9 +1,9 @@
use collections::{CommandPaletteFilter, HashMap};
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
actions, div, Action, AppContext, Component, Div, EventEmitter, FocusHandle, Keystroke,
ParentElement, Render, StatelessInteractive, Styled, View, ViewContext, VisualContext,
WeakView, WindowContext,
actions, div, prelude::*, Action, AppContext, Component, EventEmitter, FocusHandle, Keystroke,
Node, ParentComponent, Render, Styled, View, ViewContext, VisualContext, WeakView,
WindowContext,
};
use picker::{Picker, PickerDelegate};
use std::{
@ -77,7 +77,7 @@ impl Modal for CommandPalette {
}
impl Render for CommandPalette {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
v_stack().w_96().child(self.picker.clone())
@ -148,7 +148,7 @@ impl CommandPaletteDelegate {
}
impl PickerDelegate for CommandPaletteDelegate {
type ListItem = Div<Picker<Self>>;
type ListItem = Node<Picker<Self>>;
fn placeholder_text(&self) -> Arc<str> {
"Execute a command...".into()

View file

@ -39,12 +39,12 @@ use futures::FutureExt;
use fuzzy::{StringMatch, StringMatchCandidate};
use git::diff_hunk_to_display;
use gpui::{
action, actions, div, point, px, relative, rems, size, uniform_list, AnyElement, AppContext,
AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context,
action, actions, div, point, prelude::*, px, relative, rems, size, uniform_list, AnyElement,
AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context,
EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight, HighlightStyle, Hsla,
InputHandler, KeyContext, Model, MouseButton, ParentElement, Pixels, Render,
StatelessInteractive, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View,
ViewContext, VisualContext, WeakView, WindowContext,
InputHandler, KeyContext, Model, MouseButton, ParentComponent, Pixels, Render, Styled,
Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext, VisualContext,
WeakView, WindowContext,
};
use highlight_matching_bracket::refresh_matching_bracket_highlights;
use hover_popover::{hide_hover, HoverState};

View file

@ -2442,7 +2442,7 @@ enum Invisible {
impl Element<Editor> for EditorElement {
type ElementState = ();
fn id(&self) -> Option<gpui::ElementId> {
fn element_id(&self) -> Option<gpui::ElementId> {
None
}

View file

@ -9,7 +9,7 @@ use collections::HashSet;
use futures::future::try_join_all;
use gpui::{
div, point, AnyElement, AppContext, AsyncAppContext, Entity, EntityId, EventEmitter,
FocusHandle, Model, ParentElement, Pixels, SharedString, Styled, Subscription, Task, View,
FocusHandle, Model, ParentComponent, Pixels, SharedString, Styled, Subscription, Task, View,
ViewContext, VisualContext, WeakView,
};
use language::{

View file

@ -1,7 +1,7 @@
use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor};
use gpui::{
actions, div, AppContext, Div, EventEmitter, ParentElement, Render, SharedString,
StatelessInteractive, Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
actions, div, prelude::*, AppContext, EventEmitter, Node, ParentComponent, Render,
SharedString, Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
};
use text::{Bias, Point};
use theme::ActiveTheme;
@ -145,11 +145,11 @@ impl GoToLine {
}
impl Render for GoToLine {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
modal(cx)
.context("GoToLine")
.key_context("GoToLine")
.on_action(Self::cancel)
.on_action(Self::confirm)
.w_96()

View file

@ -8,7 +8,7 @@ use std::{any::Any, mem};
pub trait Element<V: 'static> {
type ElementState: 'static;
fn id(&self) -> Option<ElementId>;
fn element_id(&self) -> Option<ElementId>;
/// Called to initialize this element for the current frame. If this
/// element had state in a previous frame, it will be passed in for the 3rd argument.
@ -38,7 +38,7 @@ pub trait Element<V: 'static> {
#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
pub trait ParentElement<V: 'static> {
pub trait ParentComponent<V: 'static> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
fn child(mut self, child: impl Component<V>) -> Self
@ -120,7 +120,7 @@ where
E::ElementState: 'static,
{
fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
let frame_state = if let Some(id) = self.element.id() {
let frame_state = if let Some(id) = self.element.element_id() {
cx.with_element_state(id, |element_state, cx| {
let element_state = self.element.initialize(view_state, element_state, cx);
((), element_state)
@ -142,7 +142,7 @@ where
frame_state: initial_frame_state,
} => {
frame_state = initial_frame_state;
if let Some(id) = self.element.id() {
if let Some(id) = self.element.element_id() {
layout_id = cx.with_element_state(id, |element_state, cx| {
let mut element_state = element_state.unwrap();
let layout_id = self.element.layout(state, &mut element_state, cx);
@ -181,7 +181,7 @@ where
..
} => {
let bounds = cx.layout_bounds(layout_id);
if let Some(id) = self.element.id() {
if let Some(id) = self.element.element_id() {
cx.with_element_state(id, |element_state, cx| {
let mut element_state = element_state.unwrap();
self.element
@ -351,7 +351,7 @@ where
{
type ElementState = AnyElement<V>;
fn id(&self) -> Option<ElementId> {
fn element_id(&self) -> Option<ElementId> {
None
}

View file

@ -1,12 +1,13 @@
mod div;
// mod div;
mod img;
mod node;
mod svg;
mod text;
mod uniform_list;
pub use div::*;
// pub use div::*;
pub use img::*;
pub use node::*;
pub use svg::*;
pub use text::*;
pub use uniform_list::*;

View file

@ -55,16 +55,6 @@ where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
pub fn group(mut self, group: impl Into<SharedString>) -> Self {
self.group = Some(group.into());
self
}
pub fn z_index(mut self, z_index: u32) -> Self {
self.base_style.z_index = Some(z_index);
self
}
pub fn context<C>(mut self, context: C) -> Self
where
Self: Sized,
@ -77,22 +67,6 @@ where
self
}
pub fn overflow_hidden(mut self) -> Self {
self.base_style.overflow.x = Some(Overflow::Hidden);
self.base_style.overflow.y = Some(Overflow::Hidden);
self
}
pub fn overflow_hidden_x(mut self) -> Self {
self.base_style.overflow.x = Some(Overflow::Hidden);
self
}
pub fn overflow_hidden_y(mut self) -> Self {
self.base_style.overflow.y = Some(Overflow::Hidden);
self
}
pub fn compute_style(
&self,
bounds: Bounds<Pixels>,
@ -135,22 +109,6 @@ impl<V: 'static> Div<V, StatefulInteractivity<V>, NonFocusableKeyDispatch> {
base_style: self.base_style,
}
}
pub fn overflow_scroll(mut self) -> Self {
self.base_style.overflow.x = Some(Overflow::Scroll);
self.base_style.overflow.y = Some(Overflow::Scroll);
self
}
pub fn overflow_x_scroll(mut self) -> Self {
self.base_style.overflow.x = Some(Overflow::Scroll);
self
}
pub fn overflow_y_scroll(mut self) -> Self {
self.base_style.overflow.y = Some(Overflow::Scroll);
self
}
}
impl<V: 'static> Div<V, StatelessInteractivity<V>, NonFocusableKeyDispatch> {

View file

@ -1,35 +1,28 @@
use crate::{
div, AnyElement, BorrowWindow, Bounds, Component, Div, DivState, Element, ElementId,
ElementInteractivity, FocusListeners, Focusable, FocusableKeyDispatch, KeyDispatch, LayoutId,
NonFocusableKeyDispatch, Pixels, SharedString, StatefulInteractive, StatefulInteractivity,
StatelessInteractive, StatelessInteractivity, StyleRefinement, Styled, ViewContext,
AnyElement, BorrowWindow, Bounds, Component, Element, InteractiveComponent,
InteractiveElementState, Interactivity, LayoutId, Pixels, SharedString, StyleRefinement,
Styled, ViewContext,
};
use futures::FutureExt;
use util::ResultExt;
pub struct Img<
V: 'static,
I: ElementInteractivity<V> = StatelessInteractivity<V>,
F: KeyDispatch<V> = NonFocusableKeyDispatch,
> {
base: Div<V, I, F>,
pub struct Img<V: 'static> {
interactivity: Interactivity<V>,
uri: Option<SharedString>,
grayscale: bool,
}
pub fn img<V: 'static>() -> Img<V, StatelessInteractivity<V>, NonFocusableKeyDispatch> {
pub fn img<V: 'static>() -> Img<V> {
Img {
base: div(),
interactivity: Interactivity::default(),
uri: None,
grayscale: false,
}
}
impl<V, I, F> Img<V, I, F>
impl<V> Img<V>
where
V: 'static,
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
pub fn uri(mut self, uri: impl Into<SharedString>) -> Self {
self.uri = Some(uri.into());
@ -42,38 +35,17 @@ where
}
}
impl<V, F> Img<V, StatelessInteractivity<V>, F>
where
F: KeyDispatch<V>,
{
pub fn id(self, id: impl Into<ElementId>) -> Img<V, StatefulInteractivity<V>, F> {
Img {
base: self.base.id(id),
uri: self.uri,
grayscale: self.grayscale,
}
}
}
impl<V, I, F> Component<V> for Img<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
impl<V> Component<V> for Img<V> {
fn render(self) -> AnyElement<V> {
AnyElement::new(self)
}
}
impl<V, I, F> Element<V> for Img<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
type ElementState = DivState;
impl<V> Element<V> for Img<V> {
type ElementState = InteractiveElementState;
fn id(&self) -> Option<crate::ElementId> {
self.base.id()
fn element_id(&self) -> Option<crate::ElementId> {
self.interactivity.element_id.clone()
}
fn initialize(
@ -82,7 +54,7 @@ where
element_state: Option<Self::ElementState>,
cx: &mut ViewContext<V>,
) -> Self::ElementState {
self.base.initialize(view_state, element_state, cx)
self.interactivity.initialize(element_state, cx)
}
fn layout(
@ -91,7 +63,9 @@ where
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) -> LayoutId {
self.base.layout(view_state, element_state, cx)
self.interactivity.layout(element_state, cx, |style, cx| {
cx.request_layout(&style, None)
})
}
fn paint(
@ -101,86 +75,50 @@ where
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) {
cx.with_z_index(0, |cx| {
self.base.paint(bounds, view, element_state, cx);
});
self.interactivity.paint(
bounds,
bounds.size,
element_state,
cx,
|style, scroll_offset, cx| {
let corner_radii = style.corner_radii;
let style = self.base.compute_style(bounds, element_state, cx);
let corner_radii = style.corner_radii;
if let Some(uri) = self.uri.clone() {
// eprintln!(">>> image_cache.get({uri}");
let image_future = cx.image_cache.get(uri.clone());
// eprintln!("<<< image_cache.get({uri}");
if let Some(data) = image_future
.clone()
.now_or_never()
.and_then(ResultExt::log_err)
{
let corner_radii = corner_radii.to_pixels(bounds.size, cx.rem_size());
cx.with_z_index(1, |cx| {
cx.paint_image(bounds, corner_radii, data, self.grayscale)
.log_err()
});
} else {
cx.spawn(|_, mut cx| async move {
if image_future.await.log_err().is_some() {
cx.on_next_frame(|cx| cx.notify());
if let Some(uri) = self.uri.clone() {
// eprintln!(">>> image_cache.get({uri}");
let image_future = cx.image_cache.get(uri.clone());
// eprintln!("<<< image_cache.get({uri}");
if let Some(data) = image_future
.clone()
.now_or_never()
.and_then(ResultExt::log_err)
{
let corner_radii = corner_radii.to_pixels(bounds.size, cx.rem_size());
cx.with_z_index(1, |cx| {
cx.paint_image(bounds, corner_radii, data, self.grayscale)
.log_err()
});
} else {
cx.spawn(|_, mut cx| async move {
if image_future.await.log_err().is_some() {
cx.on_next_frame(|cx| cx.notify());
}
})
.detach()
}
})
.detach()
}
}
}
},
)
}
}
impl<V, I, F> Styled for Img<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
impl<V> Styled for Img<V> {
fn style(&mut self) -> &mut StyleRefinement {
self.base.style()
&mut self.interactivity.base_style
}
}
impl<V, I, F> StatelessInteractive<V> for Img<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
self.base.stateless_interactivity()
}
}
impl<V, F> StatefulInteractive<V> for Img<V, StatefulInteractivity<V>, F>
where
F: KeyDispatch<V>,
{
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
self.base.stateful_interactivity()
}
}
impl<V, I> Focusable<V> for Img<V, I, FocusableKeyDispatch<V>>
where
V: 'static,
I: ElementInteractivity<V>,
{
fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
self.base.focus_listeners()
}
fn set_focus_style(&mut self, style: StyleRefinement) {
self.base.set_focus_style(style)
}
fn set_focus_in_style(&mut self, style: StyleRefinement) {
self.base.set_focus_in_style(style)
}
fn set_in_focus_style(&mut self, style: StyleRefinement) {
self.base.set_in_focus_style(style)
impl<V> InteractiveComponent<V> for Img<V> {
fn interactivity(&mut self) -> &mut Interactivity<V> {
&mut self.interactivity
}
}

View file

@ -1,9 +1,9 @@
use crate::{
point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusEvent, FocusHandle,
KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent,
MouseUpEvent, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style,
StyleRefinement, Styled, Task, View, ViewContext, Visibility,
BorrowWindow, Bounds, ClickEvent, Component, DispatchPhase, Element, ElementId, FocusEvent,
FocusHandle, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent,
MouseMoveEvent, MouseUpEvent, ParentComponent, Pixels, Point, Render, ScrollWheelEvent,
SharedString, Size, Style, StyleRefinement, Styled, Task, View, ViewContext, Visibility,
};
use collections::HashMap;
use parking_lot::Mutex;
@ -32,6 +32,11 @@ pub struct GroupStyle {
pub trait InteractiveComponent<V: 'static>: Sized + Element<V> {
fn interactivity(&mut self) -> &mut Interactivity<V>;
fn group(mut self, group: impl Into<SharedString>) -> Self {
self.interactivity().group = Some(group.into());
self
}
fn id(mut self, id: impl Into<ElementId>) -> Stateful<V, Self> {
self.interactivity().element_id = Some(id.into());
@ -41,9 +46,9 @@ pub trait InteractiveComponent<V: 'static>: Sized + Element<V> {
}
}
fn track_focus(mut self, focus_handle: FocusHandle) -> Focusable<V, Self> {
fn track_focus(mut self, focus_handle: &FocusHandle) -> Focusable<V, Self> {
self.interactivity().focusable = true;
self.interactivity().tracked_focus_handle = Some(focus_handle);
self.interactivity().tracked_focus_handle = Some(focus_handle.clone());
Focusable {
element: self,
view_type: PhantomData,
@ -269,8 +274,27 @@ pub trait InteractiveComponent<V: 'static>: Sized + Element<V> {
}
pub trait StatefulInteractiveComponent<V: 'static, E: Element<V>>: InteractiveComponent<V> {
fn focusable(mut self) -> Self {
fn focusable(mut self) -> Focusable<V, Self> {
self.interactivity().focusable = true;
Focusable {
element: self,
view_type: PhantomData,
}
}
fn overflow_scroll(mut self) -> Self {
self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
self
}
fn overflow_x_scroll(mut self) -> Self {
self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
self
}
fn overflow_y_scroll(mut self) -> Self {
self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
self
}
@ -514,16 +538,16 @@ pub type KeyUpListener<V> =
pub type ActionListener<V> =
Box<dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext<V>) + 'static>;
pub fn node<V: 'static>() -> Node<V> {
pub fn div<V: 'static>() -> Node<V> {
Node {
interactivity: Interactivity::default(),
children: Vec::default(),
children: SmallVec::default(),
}
}
pub struct Node<V> {
interactivity: Interactivity<V>,
children: Vec<AnyElement<V>>,
children: SmallVec<[AnyElement<V>; 2]>,
}
impl<V> Styled for Node<V> {
@ -538,10 +562,16 @@ impl<V: 'static> InteractiveComponent<V> for Node<V> {
}
}
impl<V: 'static> ParentComponent<V> for Node<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
}
impl<V: 'static> Element<V> for Node<V> {
type ElementState = NodeState;
fn id(&self) -> Option<ElementId> {
fn element_id(&self) -> Option<ElementId> {
self.interactivity.element_id.clone()
}
@ -641,48 +671,54 @@ impl<V: 'static> Element<V> for Node<V> {
}
}
impl<V: 'static> Component<V> for Node<V> {
fn render(self) -> AnyElement<V> {
AnyElement::new(self)
}
}
pub struct NodeState {
child_layout_ids: SmallVec<[LayoutId; 4]>,
interactive_state: InteractiveElementState,
}
pub struct Interactivity<V> {
element_id: Option<ElementId>,
key_context: KeyContext,
focusable: bool,
tracked_focus_handle: Option<FocusHandle>,
focus_listeners: FocusListeners<V>,
scroll_offset: Point<Pixels>,
group: Option<SharedString>,
base_style: StyleRefinement,
focus_style: StyleRefinement,
focus_in_style: StyleRefinement,
in_focus_style: StyleRefinement,
hover_style: StyleRefinement,
group_hover_style: Option<GroupStyle>,
active_style: StyleRefinement,
group_active_style: Option<GroupStyle>,
drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
key_down_listeners: SmallVec<[KeyDownListener<V>; 2]>,
key_up_listeners: SmallVec<[KeyUpListener<V>; 2]>,
action_listeners: SmallVec<[(TypeId, ActionListener<V>); 8]>,
drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
click_listeners: SmallVec<[ClickListener<V>; 2]>,
drag_listener: Option<DragListener<V>>,
hover_listener: Option<HoverListener<V>>,
tooltip_builder: Option<TooltipBuilder<V>>,
pub element_id: Option<ElementId>,
pub key_context: KeyContext,
pub focusable: bool,
pub tracked_focus_handle: Option<FocusHandle>,
pub focus_listeners: FocusListeners<V>,
// pub scroll_offset: Point<Pixels>,
pub group: Option<SharedString>,
pub base_style: StyleRefinement,
pub focus_style: StyleRefinement,
pub focus_in_style: StyleRefinement,
pub in_focus_style: StyleRefinement,
pub hover_style: StyleRefinement,
pub group_hover_style: Option<GroupStyle>,
pub active_style: StyleRefinement,
pub group_active_style: Option<GroupStyle>,
pub drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
pub group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
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 drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
pub click_listeners: SmallVec<[ClickListener<V>; 2]>,
pub drag_listener: Option<DragListener<V>>,
pub hover_listener: Option<HoverListener<V>>,
pub tooltip_builder: Option<TooltipBuilder<V>>,
}
impl<V> Interactivity<V>
where
V: 'static,
{
fn initialize(
pub fn initialize(
&mut self,
element_state: Option<InteractiveElementState>,
cx: &mut ViewContext<V>,
@ -703,7 +739,7 @@ where
element_state
}
fn layout(
pub fn layout(
&mut self,
element_state: &mut InteractiveElementState,
cx: &mut ViewContext<V>,
@ -719,7 +755,7 @@ where
})
}
fn paint(
pub fn paint(
&mut self,
bounds: Bounds<Pixels>,
content_size: Size<Pixels>,
@ -996,6 +1032,11 @@ where
GroupBounds::push(group, bounds, cx);
}
let scroll_offset = element_state
.scroll_offset
.as_ref()
.map(|scroll_offset| *scroll_offset.lock());
cx.with_element_id(self.element_id.clone(), |cx| {
cx.with_key_dispatch(
self.key_context.clone(),
@ -1026,7 +1067,7 @@ where
}
}
f(style, self.scroll_offset, cx)
f(style, scroll_offset.unwrap_or_default(), cx)
},
);
});
@ -1036,7 +1077,7 @@ where
}
}
fn compute_style(
pub fn compute_style(
&self,
bounds: Option<Bounds<Pixels>>,
element_state: &mut InteractiveElementState,
@ -1118,7 +1159,7 @@ impl<V: 'static> Default for Interactivity<V> {
focusable: false,
tracked_focus_handle: None,
focus_listeners: SmallVec::default(),
scroll_offset: Point::default(),
// scroll_offset: Point::default(),
group: None,
base_style: StyleRefinement::default(),
focus_style: StyleRefinement::default(),
@ -1148,15 +1189,15 @@ impl<V: 'static> Default for Interactivity<V> {
#[derive(Default)]
pub struct InteractiveElementState {
focus_handle: Option<FocusHandle>,
clicked_state: Arc<Mutex<ElementClickedState>>,
hover_state: Arc<Mutex<bool>>,
pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
active_tooltip: Arc<Mutex<Option<ActiveTooltip>>>,
pub focus_handle: Option<FocusHandle>,
pub clicked_state: Arc<Mutex<ElementClickedState>>,
pub hover_state: Arc<Mutex<bool>>,
pub pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
pub scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
pub active_tooltip: Arc<Mutex<Option<ActiveTooltip>>>,
}
struct ActiveTooltip {
pub struct ActiveTooltip {
#[allow(unused)] // used to drop the task
waiting: Option<Task<()>>,
tooltip: Option<AnyTooltip>,
@ -1164,7 +1205,7 @@ struct ActiveTooltip {
/// Whether or not the element or a group that contains it is clicked by the mouse.
#[derive(Copy, Clone, Default, Eq, PartialEq)]
struct ElementClickedState {
pub struct ElementClickedState {
pub group: bool,
pub element: bool,
}
@ -1222,6 +1263,16 @@ impl<V: 'static, E: StatefulInteractiveComponent<V, E>> StatefulInteractiveCompo
{
}
impl<V, E> Styled for Focusable<V, E>
where
V: 'static,
E: Styled,
{
fn style(&mut self) -> &mut StyleRefinement {
self.element.style()
}
}
impl<V, E> Element<V> for Focusable<V, E>
where
V: 'static,
@ -1229,8 +1280,8 @@ where
{
type ElementState = E::ElementState;
fn id(&self) -> Option<ElementId> {
self.element.id()
fn element_id(&self) -> Option<ElementId> {
self.element.element_id()
}
fn initialize(
@ -1262,11 +1313,41 @@ where
}
}
impl<V, E> Component<V> for Focusable<V, E>
where
V: 'static,
E: 'static + Element<V>,
{
fn render(self) -> AnyElement<V> {
AnyElement::new(self)
}
}
impl<V, E> ParentComponent<V> for Focusable<V, E>
where
V: 'static,
E: ParentComponent<V>,
{
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
self.element.children_mut()
}
}
pub struct Stateful<V, E> {
element: E,
view_type: PhantomData<V>,
}
impl<V, E> Styled for Stateful<V, E>
where
V: 'static,
E: Styled,
{
fn style(&mut self) -> &mut StyleRefinement {
self.element.style()
}
}
impl<V, E> StatefulInteractiveComponent<V, E> for Stateful<V, E>
where
V: 'static,
@ -1294,8 +1375,8 @@ where
{
type ElementState = E::ElementState;
fn id(&self) -> Option<ElementId> {
self.element.id()
fn element_id(&self) -> Option<ElementId> {
self.element.element_id()
}
fn initialize(
@ -1326,3 +1407,23 @@ where
self.element.paint(bounds, view_state, element_state, cx)
}
}
impl<V, E> Component<V> for Stateful<V, E>
where
V: 'static,
E: 'static + Element<V>,
{
fn render(self) -> AnyElement<V> {
AnyElement::new(self)
}
}
impl<V, E> ParentComponent<V> for Stateful<V, E>
where
V: 'static,
E: ParentComponent<V>,
{
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
self.element.children_mut()
}
}

View file

@ -1,69 +1,40 @@
use crate::{
div, AnyElement, Bounds, Component, Div, DivState, Element, ElementId, ElementInteractivity,
FocusListeners, Focusable, FocusableKeyDispatch, KeyDispatch, LayoutId,
NonFocusableKeyDispatch, Pixels, SharedString, StatefulInteractive, StatefulInteractivity,
StatelessInteractive, StatelessInteractivity, StyleRefinement, Styled, ViewContext,
AnyElement, Bounds, Component, Element, ElementId, InteractiveComponent,
InteractiveElementState, Interactivity, LayoutId, Pixels, SharedString, StyleRefinement,
Styled, ViewContext,
};
use util::ResultExt;
pub struct Svg<
V: 'static,
I: ElementInteractivity<V> = StatelessInteractivity<V>,
F: KeyDispatch<V> = NonFocusableKeyDispatch,
> {
base: Div<V, I, F>,
pub struct Svg<V: 'static> {
interactivity: Interactivity<V>,
path: Option<SharedString>,
}
pub fn svg<V: 'static>() -> Svg<V, StatelessInteractivity<V>, NonFocusableKeyDispatch> {
pub fn svg<V: 'static>() -> Svg<V> {
Svg {
base: div(),
interactivity: Interactivity::default(),
path: None,
}
}
impl<V, I, F> Svg<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
impl<V> Svg<V> {
pub fn path(mut self, path: impl Into<SharedString>) -> Self {
self.path = Some(path.into());
self
}
}
impl<V, F> Svg<V, StatelessInteractivity<V>, F>
where
F: KeyDispatch<V>,
{
pub fn id(self, id: impl Into<ElementId>) -> Svg<V, StatefulInteractivity<V>, F> {
Svg {
base: self.base.id(id),
path: self.path,
}
}
}
impl<V, I, F> Component<V> for Svg<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
impl<V> Component<V> for Svg<V> {
fn render(self) -> AnyElement<V> {
AnyElement::new(self)
}
}
impl<V, I, F> Element<V> for Svg<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
type ElementState = DivState;
impl<V> Element<V> for Svg<V> {
type ElementState = InteractiveElementState;
fn id(&self) -> Option<crate::ElementId> {
self.base.id()
fn element_id(&self) -> Option<ElementId> {
self.interactivity.element_id.clone()
}
fn initialize(
@ -72,7 +43,7 @@ where
element_state: Option<Self::ElementState>,
cx: &mut ViewContext<V>,
) -> Self::ElementState {
self.base.initialize(view_state, element_state, cx)
self.interactivity.initialize(element_state, cx)
}
fn layout(
@ -81,7 +52,9 @@ where
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) -> LayoutId {
self.base.layout(view_state, element_state, cx)
self.interactivity.layout(element_state, cx, |style, cx| {
cx.request_layout(&style, None)
})
}
fn paint(
@ -93,65 +66,23 @@ where
) where
Self: Sized,
{
self.base.paint(bounds, view, element_state, cx);
let color = self
.base
.compute_style(bounds, element_state, cx)
.text
.color;
if let Some((path, color)) = self.path.as_ref().zip(color) {
cx.paint_svg(bounds, path.clone(), color).log_err();
}
self.interactivity
.paint(bounds, bounds.size, element_state, cx, |style, _, cx| {
if let Some((path, color)) = self.path.as_ref().zip(style.text.color) {
cx.paint_svg(bounds, path.clone(), color).log_err();
}
})
}
}
impl<V, I, F> Styled for Svg<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
impl<V> Styled for Svg<V> {
fn style(&mut self) -> &mut StyleRefinement {
self.base.style()
&mut self.interactivity.base_style
}
}
impl<V, I, F> StatelessInteractive<V> for Svg<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
self.base.stateless_interactivity()
}
}
impl<V, F> StatefulInteractive<V> for Svg<V, StatefulInteractivity<V>, F>
where
V: 'static,
F: KeyDispatch<V>,
{
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
self.base.stateful_interactivity()
}
}
impl<V: 'static, I> Focusable<V> for Svg<V, I, FocusableKeyDispatch<V>>
where
I: ElementInteractivity<V>,
{
fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
self.base.focus_listeners()
}
fn set_focus_style(&mut self, style: StyleRefinement) {
self.base.set_focus_style(style)
}
fn set_focus_in_style(&mut self, style: StyleRefinement) {
self.base.set_focus_in_style(style)
}
fn set_in_focus_style(&mut self, style: StyleRefinement) {
self.base.set_in_focus_style(style)
impl<V> InteractiveComponent<V> for Svg<V> {
fn interactivity(&mut self) -> &mut Interactivity<V> {
&mut self.interactivity
}
}

View file

@ -53,7 +53,7 @@ impl<V: 'static> Component<V> for Text<V> {
impl<V: 'static> Element<V> for Text<V> {
type ElementState = Arc<Mutex<Option<TextElementState>>>;
fn id(&self) -> Option<crate::ElementId> {
fn element_id(&self) -> Option<crate::ElementId> {
None
}

View file

@ -1,24 +1,23 @@
use crate::{
point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element,
ElementId, ElementInteractivity, InteractiveElementState, LayoutId, Pixels, Point, Size,
StatefulInteractive, StatefulInteractivity, StatelessInteractive, StatelessInteractivity,
StyleRefinement, Styled, ViewContext,
ElementId, InteractiveComponent, InteractiveElementState, Interactivity, LayoutId, Pixels,
Point, Size, StyleRefinement, Styled, ViewContext,
};
use parking_lot::Mutex;
use smallvec::SmallVec;
use std::{cmp, ops::Range, sync::Arc};
use std::{cmp, mem, ops::Range, sync::Arc};
use taffy::style::Overflow;
/// uniform_list provides lazy rendering for a set of items that are of uniform height.
/// When rendered into a container with overflow-y: hidden and a fixed (or max) height,
/// uniform_list will only render the visibile subset of items.
pub fn uniform_list<Id, V, C>(
id: Id,
pub fn uniform_list<I, V, C>(
id: I,
item_count: usize,
f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> SmallVec<[C; 64]>,
) -> UniformList<V>
where
Id: Into<ElementId>,
I: Into<ElementId>,
V: 'static,
C: Component<V>,
{
@ -37,7 +36,10 @@ where
.map(|component| component.render())
.collect()
}),
interactivity: StatefulInteractivity::new(id, StatelessInteractivity::default()),
interactivity: Interactivity {
element_id: Some(id.into()),
..Default::default()
},
scroll_handle: None,
}
}
@ -54,7 +56,7 @@ pub struct UniformList<V: 'static> {
&'a mut ViewContext<V>,
) -> SmallVec<[AnyElement<V>; 64]>,
>,
interactivity: StatefulInteractivity<V>,
interactivity: Interactivity<V>,
scroll_handle: Option<UniformListScrollHandle>,
}
@ -103,7 +105,7 @@ pub struct UniformListState {
impl<V: 'static> Element<V> for UniformList<V> {
type ElementState = UniformListState;
fn id(&self) -> Option<crate::ElementId> {
fn element_id(&self) -> Option<crate::ElementId> {
Some(self.id.clone())
}
@ -113,13 +115,18 @@ impl<V: 'static> Element<V> for UniformList<V> {
element_state: Option<Self::ElementState>,
cx: &mut ViewContext<V>,
) -> Self::ElementState {
element_state.unwrap_or_else(|| {
if let Some(mut element_state) = element_state {
element_state.interactive = self
.interactivity
.initialize(Some(element_state.interactive), cx);
element_state
} else {
let item_size = self.measure_item(view_state, None, cx);
UniformListState {
interactive: InteractiveElementState::default(),
interactive: self.interactivity.initialize(None, cx),
item_size,
}
})
}
}
fn layout(
@ -132,35 +139,44 @@ impl<V: 'static> Element<V> for UniformList<V> {
let item_size = element_state.item_size;
let rem_size = cx.rem_size();
cx.request_measured_layout(
self.computed_style(),
rem_size,
move |known_dimensions: Size<Option<Pixels>>, available_space: Size<AvailableSpace>| {
let desired_height = item_size.height * max_items;
let width = known_dimensions
.width
.unwrap_or(match available_space.width {
AvailableSpace::Definite(x) => x,
AvailableSpace::MinContent | AvailableSpace::MaxContent => item_size.width,
});
let height = match available_space.height {
AvailableSpace::Definite(x) => desired_height.min(x),
AvailableSpace::MinContent | AvailableSpace::MaxContent => desired_height,
};
size(width, height)
},
)
self.interactivity
.layout(&mut element_state.interactive, cx, |style, cx| {
cx.request_measured_layout(
style,
rem_size,
move |known_dimensions: Size<Option<Pixels>>,
available_space: Size<AvailableSpace>| {
let desired_height = item_size.height * max_items;
let width = known_dimensions
.width
.unwrap_or(match available_space.width {
AvailableSpace::Definite(x) => x,
AvailableSpace::MinContent | AvailableSpace::MaxContent => {
item_size.width
}
});
let height = match available_space.height {
AvailableSpace::Definite(x) => desired_height.min(x),
AvailableSpace::MinContent | AvailableSpace::MaxContent => {
desired_height
}
};
size(width, height)
},
)
})
}
fn paint(
&mut self,
bounds: crate::Bounds<crate::Pixels>,
bounds: Bounds<crate::Pixels>,
view_state: &mut V,
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) {
let style = self.computed_style();
let style =
self.interactivity
.compute_style(Some(bounds), &mut element_state.interactive, cx);
let border = style.border_widths.to_pixels(cx.rem_size());
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
@ -170,74 +186,75 @@ impl<V: 'static> Element<V> for UniformList<V> {
- point(border.right + padding.right, border.bottom + padding.bottom),
);
cx.with_z_index(style.z_index.unwrap_or(0), |cx| {
style.paint(bounds, cx);
let item_size = element_state.item_size;
let content_size = Size {
width: padded_bounds.size.width,
height: item_size.height * self.item_count,
};
let content_size;
if self.item_count > 0 {
let item_height = self
.measure_item(view_state, Some(padded_bounds.size.width), cx)
.height;
if let Some(scroll_handle) = self.scroll_handle.clone() {
scroll_handle.0.lock().replace(ScrollHandleState {
item_height,
list_height: padded_bounds.size.height,
scroll_offset: element_state.interactive.track_scroll_offset(),
});
}
let visible_item_count = if item_height > px(0.) {
(padded_bounds.size.height / item_height).ceil() as usize + 1
} else {
0
};
let scroll_offset = element_state
.interactive
.scroll_offset()
.map_or((0.0).into(), |offset| offset.y);
let first_visible_element_ix = (-scroll_offset / item_height).floor() as usize;
let visible_range = first_visible_element_ix
..cmp::min(
first_visible_element_ix + visible_item_count,
self.item_count,
);
let mut interactivity = mem::take(&mut self.interactivity);
let shared_scroll_offset = element_state.interactive.scroll_offset.clone().unwrap();
let mut items = (self.render_items)(view_state, visible_range.clone(), cx);
interactivity.paint(
bounds,
content_size,
&mut element_state.interactive,
cx,
|style, scroll_offset, cx| {
let border = style.border_widths.to_pixels(cx.rem_size());
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
content_size = Size {
width: padded_bounds.size.width,
height: item_height * self.item_count,
};
cx.with_z_index(1, |cx| {
for (item, ix) in items.iter_mut().zip(visible_range) {
let item_origin =
padded_bounds.origin + point(px(0.), item_height * ix + scroll_offset);
let available_space = size(
AvailableSpace::Definite(padded_bounds.size.width),
AvailableSpace::Definite(item_height),
);
item.draw(item_origin, available_space, view_state, cx);
}
});
} else {
content_size = Size {
width: bounds.size.width,
height: px(0.),
};
}
let overflow = point(style.overflow.x, Overflow::Scroll);
cx.with_z_index(0, |cx| {
self.interactivity.handle_events(
bounds,
content_size,
overflow,
&mut element_state.interactive,
cx,
let padded_bounds = Bounds::from_corners(
bounds.origin + point(border.left + padding.left, border.top + padding.top),
bounds.lower_right()
- point(border.right + padding.right, border.bottom + padding.bottom),
);
});
})
cx.with_z_index(style.z_index.unwrap_or(0), |cx| {
style.paint(bounds, cx);
if self.item_count > 0 {
let item_height = self
.measure_item(view_state, Some(padded_bounds.size.width), cx)
.height;
if let Some(scroll_handle) = self.scroll_handle.clone() {
scroll_handle.0.lock().replace(ScrollHandleState {
item_height,
list_height: padded_bounds.size.height,
scroll_offset: shared_scroll_offset,
});
}
let visible_item_count = if item_height > px(0.) {
(padded_bounds.size.height / item_height).ceil() as usize + 1
} else {
0
};
let first_visible_element_ix =
(-scroll_offset.y / item_height).floor() as usize;
let visible_range = first_visible_element_ix
..cmp::min(
first_visible_element_ix + visible_item_count,
self.item_count,
);
let mut items = (self.render_items)(view_state, visible_range.clone(), cx);
cx.with_z_index(1, |cx| {
for (item, ix) in items.iter_mut().zip(visible_range) {
let item_origin = padded_bounds.origin
+ point(px(0.), item_height * ix + scroll_offset.y);
let available_space = size(
AvailableSpace::Definite(padded_bounds.size.width),
AvailableSpace::Definite(item_height),
);
item.draw(item_origin, available_space, view_state, cx);
}
});
}
})
},
);
self.interactivity = interactivity;
}
}
@ -275,14 +292,8 @@ impl<V> UniformList<V> {
}
}
impl<V: 'static> StatelessInteractive<V> for UniformList<V> {
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
self.interactivity.as_stateless_mut()
}
}
impl<V: 'static> StatefulInteractive<V> for UniformList<V> {
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
impl<V> InteractiveComponent<V> for UniformList<V> {
fn interactivity(&mut self) -> &mut crate::Interactivity<V> {
&mut self.interactivity
}
}

View file

@ -1,915 +1,17 @@
use crate::{
div, point, px, Action, AnyDrag, AnyTooltip, AnyView, AppContext, Bounds, Component,
DispatchPhase, Div, Element, ElementId, FocusHandle, KeyContext, Keystroke, Modifiers,
Overflow, Pixels, Point, Render, SharedString, Size, Style, StyleRefinement, Task, View,
ViewContext,
div, point, px, AnyDrag, AnyTooltip, AnyView, AppContext, Bounds, Component, DispatchPhase,
FocusHandle, Keystroke, Modifiers, Node, Pixels, Point, Render, SharedString, StyleRefinement,
Task, ViewContext,
};
use collections::HashMap;
use derive_more::{Deref, DerefMut};
use parking_lot::Mutex;
use refineable::Refineable;
use smallvec::SmallVec;
use std::{
any::{Any, TypeId},
fmt::Debug,
marker::PhantomData,
mem,
ops::Deref,
path::PathBuf,
sync::Arc,
time::Duration,
any::Any, fmt::Debug, marker::PhantomData, ops::Deref, path::PathBuf, sync::Arc, time::Duration,
};
const DRAG_THRESHOLD: f64 = 2.;
const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
const TOOLTIP_OFFSET: Point<Pixels> = Point::new(px(10.0), px(8.0));
pub trait StatelessInteractive<V: 'static>: Element<V> {
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V>;
fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.stateless_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.stateless_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.stateless_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.stateless_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.stateless_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.stateless_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.stateless_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.stateless_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.stateless_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.stateless_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.stateless_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.stateless_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.stateless_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.stateless_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.stateless_interactivity().drop_listeners.push((
TypeId::of::<W>(),
Box::new(move |view, dragged_view, cx| {
listener(view, dragged_view.downcast().unwrap(), cx);
}),
));
self
}
}
pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V>;
fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.stateful_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.stateful_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.stateful_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.stateful_interactivity().drag_listener.is_none(),
"calling on_drag more than once on the same element is not supported"
);
self.stateful_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.stateful_interactivity().hover_listener.is_none(),
"calling on_hover more than once on the same element is not supported"
);
self.stateful_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.stateful_interactivity().tooltip_builder.is_none(),
"calling tooltip more than once on the same element is not supported"
);
self.stateful_interactivity().tooltip_builder = Some(Arc::new(move |view_state, cx| {
build_tooltip(view_state, cx).into()
}));
self
}
}
pub trait ElementInteractivity<V: 'static>: 'static {
fn as_stateless(&self) -> &StatelessInteractivity<V>;
fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V>;
fn as_stateful(&self) -> Option<&StatefulInteractivity<V>>;
fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>>;
fn refine_style(
&self,
style: &mut Style,
bounds: Bounds<Pixels>,
element_state: &InteractiveElementState,
cx: &mut ViewContext<V>,
) {
let mouse_position = cx.mouse_position();
let stateless = self.as_stateless();
if let Some(group_hover) = stateless.group_hover_style.as_ref() {
if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
if group_bounds.contains_point(&mouse_position) {
style.refine(&group_hover.style);
}
}
}
if bounds.contains_point(&mouse_position) {
style.refine(&stateless.hover_style);
}
if let Some(drag) = cx.active_drag.take() {
for (state_type, group_drag_style) in &self.as_stateless().group_drag_over_styles {
if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
if *state_type == drag.view.entity_type()
&& group_bounds.contains_point(&mouse_position)
{
style.refine(&group_drag_style.style);
}
}
}
for (state_type, drag_over_style) in &self.as_stateless().drag_over_styles {
if *state_type == drag.view.entity_type() && bounds.contains_point(&mouse_position)
{
style.refine(drag_over_style);
}
}
cx.active_drag = Some(drag);
}
if let Some(stateful) = self.as_stateful() {
let active_state = element_state.active_state.lock();
if active_state.group {
if let Some(group_style) = stateful.group_active_style.as_ref() {
style.refine(&group_style.style);
}
}
if active_state.element {
style.refine(&stateful.active_style);
}
}
}
fn initialize(&mut self, cx: &mut ViewContext<V>) {
let stateless = self.as_stateless_mut();
for listener in stateless.key_down_listeners.drain(..) {
cx.on_key_event(move |state, event: &KeyDownEvent, phase, cx| {
listener(state, event, phase, cx);
})
}
for listener in stateless.key_up_listeners.drain(..) {
cx.on_key_event(move |state, event: &KeyUpEvent, phase, cx| {
listener(state, event, phase, cx);
})
}
for (action_type, listener) in stateless.action_listeners.drain(..) {
cx.on_action(action_type, listener)
}
}
fn handle_events(
&mut self,
bounds: Bounds<Pixels>,
content_size: Size<Pixels>,
overflow: Point<Overflow>,
element_state: &mut InteractiveElementState,
cx: &mut ViewContext<V>,
) {
let stateless = self.as_stateless_mut();
for listener in stateless.mouse_down_listeners.drain(..) {
cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
listener(state, event, &bounds, phase, cx);
})
}
for listener in stateless.mouse_up_listeners.drain(..) {
cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
listener(state, event, &bounds, phase, cx);
})
}
for listener in stateless.mouse_move_listeners.drain(..) {
cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
listener(state, event, &bounds, phase, cx);
})
}
for listener in stateless.scroll_wheel_listeners.drain(..) {
cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
listener(state, event, &bounds, phase, cx);
})
}
let hover_group_bounds = stateless
.group_hover_style
.as_ref()
.and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
if let Some(group_bounds) = hover_group_bounds {
let hovered = group_bounds.contains_point(&cx.mouse_position());
cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
if phase == DispatchPhase::Capture {
if group_bounds.contains_point(&event.position) != hovered {
cx.notify();
}
}
});
}
if stateless.hover_style.is_some()
|| (cx.active_drag.is_some() && !stateless.drag_over_styles.is_empty())
{
let hovered = bounds.contains_point(&cx.mouse_position());
cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
if phase == DispatchPhase::Capture {
if bounds.contains_point(&event.position) != hovered {
cx.notify();
}
}
});
}
if cx.active_drag.is_some() {
let drop_listeners = mem::take(&mut stateless.drop_listeners);
cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
if let Some(drag_state_type) =
cx.active_drag.as_ref().map(|drag| drag.view.entity_type())
{
for (drop_state_type, listener) in &drop_listeners {
if *drop_state_type == drag_state_type {
let drag = cx
.active_drag
.take()
.expect("checked for type drag state type above");
listener(view, drag.view.clone(), cx);
cx.notify();
cx.stop_propagation();
}
}
}
}
});
}
if let Some(stateful) = self.as_stateful_mut() {
let click_listeners = mem::take(&mut stateful.click_listeners);
let drag_listener = mem::take(&mut stateful.drag_listener);
if !click_listeners.is_empty() || drag_listener.is_some() {
let pending_mouse_down = element_state.pending_mouse_down.clone();
let mouse_down = pending_mouse_down.lock().clone();
if let Some(mouse_down) = mouse_down {
if let Some(drag_listener) = drag_listener {
let active_state = element_state.active_state.clone();
cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
if cx.active_drag.is_some() {
if phase == DispatchPhase::Capture {
cx.notify();
}
} else if phase == DispatchPhase::Bubble
&& bounds.contains_point(&event.position)
&& (event.position - mouse_down.position).magnitude()
> DRAG_THRESHOLD
{
*active_state.lock() = ActiveState::default();
let cursor_offset = event.position - bounds.origin;
let drag = drag_listener(view_state, cursor_offset, cx);
cx.active_drag = Some(drag);
cx.notify();
cx.stop_propagation();
}
});
}
cx.on_mouse_event(move |view_state, event: &MouseUpEvent, phase, cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position)
{
let mouse_click = ClickEvent {
down: mouse_down.clone(),
up: event.clone(),
};
for listener in &click_listeners {
listener(view_state, &mouse_click, cx);
}
}
*pending_mouse_down.lock() = None;
});
} else {
cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position)
{
*pending_mouse_down.lock() = Some(event.clone());
}
});
}
}
if let Some(hover_listener) = stateful.hover_listener.take() {
let was_hovered = element_state.hover_state.clone();
let has_mouse_down = element_state.pending_mouse_down.clone();
cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
if phase != DispatchPhase::Bubble {
return;
}
let is_hovered =
bounds.contains_point(&event.position) && has_mouse_down.lock().is_none();
let mut was_hovered = was_hovered.lock();
if is_hovered != was_hovered.clone() {
*was_hovered = is_hovered;
drop(was_hovered);
hover_listener(view_state, is_hovered, cx);
}
});
}
if let Some(tooltip_builder) = stateful.tooltip_builder.take() {
let active_tooltip = element_state.active_tooltip.clone();
let pending_mouse_down = element_state.pending_mouse_down.clone();
cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
if phase != DispatchPhase::Bubble {
return;
}
let is_hovered = bounds.contains_point(&event.position)
&& pending_mouse_down.lock().is_none();
if !is_hovered {
active_tooltip.lock().take();
return;
}
if active_tooltip.lock().is_none() {
let task = cx.spawn({
let active_tooltip = active_tooltip.clone();
let tooltip_builder = tooltip_builder.clone();
move |view, mut cx| async move {
cx.background_executor().timer(TOOLTIP_DELAY).await;
view.update(&mut cx, move |view_state, cx| {
active_tooltip.lock().replace(ActiveTooltip {
waiting: None,
tooltip: Some(AnyTooltip {
view: tooltip_builder(view_state, cx),
cursor_offset: cx.mouse_position() + TOOLTIP_OFFSET,
}),
});
cx.notify();
})
.ok();
}
});
active_tooltip.lock().replace(ActiveTooltip {
waiting: Some(task),
tooltip: None,
});
}
});
if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() {
if active_tooltip.tooltip.is_some() {
cx.active_tooltip = active_tooltip.tooltip.clone()
}
}
}
let active_state = element_state.active_state.clone();
if active_state.lock().is_none() {
let active_group_bounds = stateful
.group_active_style
.as_ref()
.and_then(|group_active| GroupBounds::get(&group_active.group, cx));
cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble {
let group = active_group_bounds
.map_or(false, |bounds| bounds.contains_point(&down.position));
let element = bounds.contains_point(&down.position);
if group || element {
*active_state.lock() = ActiveState { group, element };
cx.notify();
}
}
});
} else {
cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
if phase == DispatchPhase::Capture {
*active_state.lock() = ActiveState::default();
cx.notify();
}
});
}
if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
let scroll_offset = element_state
.scroll_offset
.get_or_insert_with(Arc::default)
.clone();
let line_height = cx.line_height();
let scroll_max = (content_size - bounds.size).max(&Size::default());
cx.on_mouse_event(move |_, event: &ScrollWheelEvent, phase, cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
let mut scroll_offset = scroll_offset.lock();
let old_scroll_offset = *scroll_offset;
let delta = event.delta.pixel_delta(line_height);
if overflow.x == Overflow::Scroll {
scroll_offset.x =
(scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
}
if overflow.y == Overflow::Scroll {
scroll_offset.y =
(scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
}
if *scroll_offset != old_scroll_offset {
cx.notify();
cx.stop_propagation();
}
}
});
}
}
}
}
#[derive(Deref, DerefMut)]
pub struct StatefulInteractivity<V> {
pub id: ElementId,
#[deref]
#[deref_mut]
stateless: StatelessInteractivity<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> StatefulInteractivity<V> {
pub fn new(id: ElementId, stateless: StatelessInteractivity<V>) -> Self {
Self {
id,
stateless,
click_listeners: SmallVec::new(),
active_style: StyleRefinement::default(),
group_active_style: None,
drag_listener: None,
hover_listener: None,
tooltip_builder: None,
}
}
}
impl<V: 'static> ElementInteractivity<V> for StatefulInteractivity<V> {
fn as_stateful(&self) -> Option<&StatefulInteractivity<V>> {
Some(self)
}
fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>> {
Some(self)
}
fn as_stateless(&self) -> &StatelessInteractivity<V> {
&self.stateless
}
fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V> {
&mut self.stateless
}
}
type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static;
pub struct StatelessInteractivity<V> {
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]>,
}
impl<V> StatelessInteractivity<V> {
pub fn into_stateful(self, id: impl Into<ElementId>) -> StatefulInteractivity<V> {
StatefulInteractivity {
id: id.into(),
stateless: self,
click_listeners: SmallVec::new(),
drag_listener: None,
hover_listener: None,
tooltip_builder: None,
active_style: StyleRefinement::default(),
group_active_style: None,
}
}
}
pub struct GroupStyle {
pub group: SharedString,
pub style: StyleRefinement,
}
#[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();
}
}
#[derive(Copy, Clone, Default, Eq, PartialEq)]
struct ActiveState {
pub group: bool,
pub element: bool,
}
impl ActiveState {
pub fn is_none(&self) -> bool {
!self.group && !self.element
}
}
#[derive(Default)]
pub struct InteractiveElementState {
active_state: Arc<Mutex<ActiveState>>,
hover_state: Arc<Mutex<bool>>,
pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
active_tooltip: Arc<Mutex<Option<ActiveTooltip>>>,
}
struct ActiveTooltip {
#[allow(unused)] // used to drop the task
waiting: Option<Task<()>>,
tooltip: Option<AnyTooltip>,
}
impl InteractiveElementState {
pub fn scroll_offset(&self) -> Option<Point<Pixels>> {
self.scroll_offset
.as_ref()
.map(|offset| offset.lock().clone())
}
pub fn track_scroll_offset(&mut self) -> Arc<Mutex<Point<Pixels>>> {
self.scroll_offset
.get_or_insert_with(|| Arc::new(Mutex::new(Default::default())))
.clone()
}
}
impl<V> Default for StatelessInteractivity<V> {
fn default() -> Self {
Self {
dispatch_context: KeyContext::default(),
mouse_down_listeners: SmallVec::new(),
mouse_up_listeners: SmallVec::new(),
mouse_move_listeners: SmallVec::new(),
scroll_wheel_listeners: SmallVec::new(),
key_down_listeners: SmallVec::new(),
key_up_listeners: SmallVec::new(),
action_listeners: SmallVec::new(),
hover_style: StyleRefinement::default(),
group_hover_style: None,
drag_over_styles: SmallVec::new(),
group_drag_over_styles: SmallVec::new(),
drop_listeners: SmallVec::new(),
}
}
}
impl<V: 'static> ElementInteractivity<V> for StatelessInteractivity<V> {
fn as_stateful(&self) -> Option<&StatefulInteractivity<V>> {
None
}
fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>> {
None
}
fn as_stateless(&self) -> &StatelessInteractivity<V> {
self
}
fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V> {
self
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct KeyDownEvent {
pub keystroke: Keystroke,
@ -991,10 +93,6 @@ where
}
}
// impl<S, R, V, E> Render for Drag<S, R, V, E> {
// // fn render(&mut self, cx: ViewContext<Self>) ->
// }
#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
pub enum MouseButton {
Left,
@ -1103,7 +201,7 @@ impl Deref for MouseExitEvent {
pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>);
impl Render for ExternalPaths {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, _: &mut ViewContext<Self>) -> Self::Element {
div() // Intentionally left empty because the platform will render icons for the dragged files
@ -1229,8 +327,8 @@ pub type ActionListener<V> =
#[cfg(test)]
mod test {
use crate::{
self as gpui, div, Div, FocusHandle, KeyBinding, Keystroke, ParentElement, Render,
StatefulInteractivity, StatelessInteractive, TestAppContext, VisualContext,
self as gpui, div, FocusHandle, InteractiveComponent, KeyBinding, Keystroke, Node,
ParentComponent, Render, Stateful, TestAppContext, VisualContext,
};
struct TestView {
@ -1242,12 +340,12 @@ mod test {
actions!(TestAction);
impl Render for TestView {
type Element = Div<Self, StatefulInteractivity<Self>>;
type Element = Stateful<Self, Node<Self>>;
fn render(&mut self, _: &mut gpui::ViewContext<Self>) -> Self::Element {
div().id("testview").child(
div()
.context("test")
.key_context("test")
.track_focus(&self.focus_handle)
.on_key_down(|this: &mut TestView, _, _, _| this.saw_key_down = true)
.on_action(|this: &mut TestView, _: &TestAction, _| this.saw_action = true),

View file

@ -1,7 +1,7 @@
use crate::{
build_action_from_type, Action, Bounds, DispatchPhase, Element, FocusEvent, FocusHandle,
FocusId, KeyContext, KeyMatch, Keymap, Keystroke, KeystrokeMatcher, MouseDownEvent, Pixels,
Style, StyleRefinement, ViewContext, WindowContext,
build_action_from_type, Action, Bounds, DispatchPhase, FocusEvent, FocusHandle, FocusId,
KeyContext, KeyMatch, Keymap, Keystroke, KeystrokeMatcher, MouseDownEvent, Pixels, Style,
StyleRefinement, ViewContext, WindowContext,
};
use collections::HashMap;
use parking_lot::Mutex;
@ -342,115 +342,3 @@ impl<V: 'static> KeyDispatch<V> for NonFocusableKeyDispatch {
&mut self.key_context
}
}
pub trait Focusable<V: 'static>: Element<V> {
fn focus_listeners(&mut self) -> &mut FocusListeners<V>;
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 focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.set_focus_style(f(StyleRefinement::default()));
self
}
fn focus_in(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.set_focus_in_style(f(StyleRefinement::default()));
self
}
fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.set_in_focus_style(f(StyleRefinement::default()));
self
}
fn on_focus(
mut self,
listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.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.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.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.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
}
}

View file

@ -1 +1,4 @@
pub use crate::{Context, ParentElement, Refineable};
pub use crate::{
BorrowAppContext, BorrowWindow, Component, Context, FocusableComponent, InteractiveComponent,
ParentComponent, Refineable, Render, StatefulInteractiveComponent, Styled, VisualContext,
};

View file

@ -6,21 +6,20 @@ use crate::{
use crate::{BoxShadow, TextStyleRefinement};
use refineable::Refineable;
use smallvec::{smallvec, SmallVec};
use taffy::style::Overflow;
pub trait Styled {
pub trait Styled: Sized {
fn style(&mut self) -> &mut StyleRefinement;
fn computed_style(&mut self) -> Style {
Style::default().refined(self.style().clone())
}
gpui2_macros::style_helpers!();
fn z_index(mut self, z_index: u32) -> Self {
self.style().z_index = Some(z_index);
self
}
/// Sets the size of the element to the full width and height.
fn full(mut self) -> Self
where
Self: Sized,
{
fn full(mut self) -> Self {
self.style().size.width = Some(relative(1.).into());
self.style().size.height = Some(relative(1.).into());
self
@ -28,118 +27,98 @@ pub trait Styled {
/// Sets the position of the element to `relative`.
/// [Docs](https://tailwindcss.com/docs/position)
fn relative(mut self) -> Self
where
Self: Sized,
{
fn relative(mut self) -> Self {
self.style().position = Some(Position::Relative);
self
}
/// Sets the position of the element to `absolute`.
/// [Docs](https://tailwindcss.com/docs/position)
fn absolute(mut self) -> Self
where
Self: Sized,
{
fn absolute(mut self) -> Self {
self.style().position = Some(Position::Absolute);
self
}
/// Sets the display type of the element to `block`.
/// [Docs](https://tailwindcss.com/docs/display)
fn block(mut self) -> Self
where
Self: Sized,
{
fn block(mut self) -> Self {
self.style().display = Some(Display::Block);
self
}
/// Sets the display type of the element to `flex`.
/// [Docs](https://tailwindcss.com/docs/display)
fn flex(mut self) -> Self
where
Self: Sized,
{
fn flex(mut self) -> Self {
self.style().display = Some(Display::Flex);
self
}
/// Sets the visibility of the element to `visible`.
/// [Docs](https://tailwindcss.com/docs/visibility)
fn visible(mut self) -> Self
where
Self: Sized,
{
fn visible(mut self) -> Self {
self.style().visibility = Some(Visibility::Visible);
self
}
/// Sets the visibility of the element to `hidden`.
/// [Docs](https://tailwindcss.com/docs/visibility)
fn invisible(mut self) -> Self
where
Self: Sized,
{
fn invisible(mut self) -> Self {
self.style().visibility = Some(Visibility::Hidden);
self
}
fn cursor(mut self, cursor: CursorStyle) -> Self
where
Self: Sized,
{
fn overflow_hidden(mut self) -> Self {
self.style().overflow.x = Some(Overflow::Hidden);
self.style().overflow.y = Some(Overflow::Hidden);
self
}
fn overflow_hidden_x(mut self) -> Self {
self.style().overflow.x = Some(Overflow::Hidden);
self
}
fn overflow_hidden_y(mut self) -> Self {
self.style().overflow.y = Some(Overflow::Hidden);
self
}
fn cursor(mut self, cursor: CursorStyle) -> Self {
self.style().mouse_cursor = Some(cursor);
self
}
/// Sets the cursor style when hovering an element to `default`.
/// [Docs](https://tailwindcss.com/docs/cursor)
fn cursor_default(mut self) -> Self
where
Self: Sized,
{
fn cursor_default(mut self) -> Self {
self.style().mouse_cursor = Some(CursorStyle::Arrow);
self
}
/// Sets the cursor style when hovering an element to `pointer`.
/// [Docs](https://tailwindcss.com/docs/cursor)
fn cursor_pointer(mut self) -> Self
where
Self: Sized,
{
fn cursor_pointer(mut self) -> Self {
self.style().mouse_cursor = Some(CursorStyle::PointingHand);
self
}
/// Sets the flex direction of the element to `column`.
/// [Docs](https://tailwindcss.com/docs/flex-direction#column)
fn flex_col(mut self) -> Self
where
Self: Sized,
{
fn flex_col(mut self) -> Self {
self.style().flex_direction = Some(FlexDirection::Column);
self
}
/// Sets the flex direction of the element to `row`.
/// [Docs](https://tailwindcss.com/docs/flex-direction#row)
fn flex_row(mut self) -> Self
where
Self: Sized,
{
fn flex_row(mut self) -> Self {
self.style().flex_direction = Some(FlexDirection::Row);
self
}
/// Sets the element to allow a flex item to grow and shrink as needed, ignoring its initial size.
/// [Docs](https://tailwindcss.com/docs/flex#flex-1)
fn flex_1(mut self) -> Self
where
Self: Sized,
{
fn flex_1(mut self) -> Self {
self.style().flex_grow = Some(1.);
self.style().flex_shrink = Some(1.);
self.style().flex_basis = Some(relative(0.).into());
@ -148,10 +127,7 @@ pub trait Styled {
/// Sets the element to allow a flex item to grow and shrink, taking into account its initial size.
/// [Docs](https://tailwindcss.com/docs/flex#auto)
fn flex_auto(mut self) -> Self
where
Self: Sized,
{
fn flex_auto(mut self) -> Self {
self.style().flex_grow = Some(1.);
self.style().flex_shrink = Some(1.);
self.style().flex_basis = Some(Length::Auto);
@ -160,10 +136,7 @@ pub trait Styled {
/// Sets the element to allow a flex item to shrink but not grow, taking into account its initial size.
/// [Docs](https://tailwindcss.com/docs/flex#initial)
fn flex_initial(mut self) -> Self
where
Self: Sized,
{
fn flex_initial(mut self) -> Self {
self.style().flex_grow = Some(0.);
self.style().flex_shrink = Some(1.);
self.style().flex_basis = Some(Length::Auto);
@ -172,10 +145,7 @@ pub trait Styled {
/// Sets the element to prevent a flex item from growing or shrinking.
/// [Docs](https://tailwindcss.com/docs/flex#none)
fn flex_none(mut self) -> Self
where
Self: Sized,
{
fn flex_none(mut self) -> Self {
self.style().flex_grow = Some(0.);
self.style().flex_shrink = Some(0.);
self
@ -183,40 +153,28 @@ pub trait Styled {
/// Sets the element to allow a flex item to grow to fill any available space.
/// [Docs](https://tailwindcss.com/docs/flex-grow)
fn grow(mut self) -> Self
where
Self: Sized,
{
fn grow(mut self) -> Self {
self.style().flex_grow = Some(1.);
self
}
/// Sets the element to align flex items to the start of the container's cross axis.
/// [Docs](https://tailwindcss.com/docs/align-items#start)
fn items_start(mut self) -> Self
where
Self: Sized,
{
fn items_start(mut self) -> Self {
self.style().align_items = Some(AlignItems::FlexStart);
self
}
/// Sets the element to align flex items to the end of the container's cross axis.
/// [Docs](https://tailwindcss.com/docs/align-items#end)
fn items_end(mut self) -> Self
where
Self: Sized,
{
fn items_end(mut self) -> Self {
self.style().align_items = Some(AlignItems::FlexEnd);
self
}
/// Sets the element to align flex items along the center of the container's cross axis.
/// [Docs](https://tailwindcss.com/docs/align-items#center)
fn items_center(mut self) -> Self
where
Self: Sized,
{
fn items_center(mut self) -> Self {
self.style().align_items = Some(AlignItems::Center);
self
}
@ -224,40 +182,28 @@ pub trait Styled {
/// Sets the element to justify flex items along the container's main axis
/// such that there is an equal amount of space between each item.
/// [Docs](https://tailwindcss.com/docs/justify-content#space-between)
fn justify_between(mut self) -> Self
where
Self: Sized,
{
fn justify_between(mut self) -> Self {
self.style().justify_content = Some(JustifyContent::SpaceBetween);
self
}
/// Sets the element to justify flex items along the center of the container's main axis.
/// [Docs](https://tailwindcss.com/docs/justify-content#center)
fn justify_center(mut self) -> Self
where
Self: Sized,
{
fn justify_center(mut self) -> Self {
self.style().justify_content = Some(JustifyContent::Center);
self
}
/// Sets the element to justify flex items against the start of the container's main axis.
/// [Docs](https://tailwindcss.com/docs/justify-content#start)
fn justify_start(mut self) -> Self
where
Self: Sized,
{
fn justify_start(mut self) -> Self {
self.style().justify_content = Some(JustifyContent::Start);
self
}
/// Sets the element to justify flex items against the end of the container's main axis.
/// [Docs](https://tailwindcss.com/docs/justify-content#end)
fn justify_end(mut self) -> Self
where
Self: Sized,
{
fn justify_end(mut self) -> Self {
self.style().justify_content = Some(JustifyContent::End);
self
}
@ -265,10 +211,7 @@ pub trait Styled {
/// Sets the element to justify items along the container's main axis such
/// that there is an equal amount of space on each side of each item.
/// [Docs](https://tailwindcss.com/docs/justify-content#space-around)
fn justify_around(mut self) -> Self
where
Self: Sized,
{
fn justify_around(mut self) -> Self {
self.style().justify_content = Some(JustifyContent::SpaceAround);
self
}
@ -295,30 +238,21 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow(mut self, shadows: SmallVec<[BoxShadow; 2]>) -> Self
where
Self: Sized,
{
fn shadow(mut self, shadows: SmallVec<[BoxShadow; 2]>) -> Self {
self.style().box_shadow = Some(shadows);
self
}
/// Clears the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_none(mut self) -> Self
where
Self: Sized,
{
fn shadow_none(mut self) -> Self {
self.style().box_shadow = Some(Default::default());
self
}
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_sm(mut self) -> Self
where
Self: Sized,
{
fn shadow_sm(mut self) -> Self {
self.style().box_shadow = Some(smallvec::smallvec![BoxShadow {
color: hsla(0., 0., 0., 0.05),
offset: point(px(0.), px(1.)),
@ -330,10 +264,7 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_md(mut self) -> Self
where
Self: Sized,
{
fn shadow_md(mut self) -> Self {
self.style().box_shadow = Some(smallvec![
BoxShadow {
color: hsla(0.5, 0., 0., 0.1),
@ -353,10 +284,7 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_lg(mut self) -> Self
where
Self: Sized,
{
fn shadow_lg(mut self) -> Self {
self.style().box_shadow = Some(smallvec![
BoxShadow {
color: hsla(0., 0., 0., 0.1),
@ -376,10 +304,7 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_xl(mut self) -> Self
where
Self: Sized,
{
fn shadow_xl(mut self) -> Self {
self.style().box_shadow = Some(smallvec![
BoxShadow {
color: hsla(0., 0., 0., 0.1),
@ -399,10 +324,7 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow_2xl(mut self) -> Self
where
Self: Sized,
{
fn shadow_2xl(mut self) -> Self {
self.style().box_shadow = Some(smallvec![BoxShadow {
color: hsla(0., 0., 0., 0.25),
offset: point(px(0.), px(25.)),
@ -417,198 +339,138 @@ pub trait Styled {
&mut style.text
}
fn text_color(mut self, color: impl Into<Hsla>) -> Self
where
Self: Sized,
{
fn text_color(mut self, color: impl Into<Hsla>) -> Self {
self.text_style().get_or_insert_with(Default::default).color = Some(color.into());
self
}
fn text_size(mut self, size: impl Into<AbsoluteLength>) -> Self
where
Self: Sized,
{
fn text_size(mut self, size: impl Into<AbsoluteLength>) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(size.into());
self
}
fn text_xs(mut self) -> Self
where
Self: Sized,
{
fn text_xs(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(0.75).into());
self
}
fn text_sm(mut self) -> Self
where
Self: Sized,
{
fn text_sm(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(0.875).into());
self
}
fn text_base(mut self) -> Self
where
Self: Sized,
{
fn text_base(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.0).into());
self
}
fn text_lg(mut self) -> Self
where
Self: Sized,
{
fn text_lg(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.125).into());
self
}
fn text_xl(mut self) -> Self
where
Self: Sized,
{
fn text_xl(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.25).into());
self
}
fn text_2xl(mut self) -> Self
where
Self: Sized,
{
fn text_2xl(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.5).into());
self
}
fn text_3xl(mut self) -> Self
where
Self: Sized,
{
fn text_3xl(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.875).into());
self
}
fn text_decoration_none(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_none(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.underline = None;
self
}
fn text_decoration_color(mut self, color: impl Into<Hsla>) -> Self
where
Self: Sized,
{
fn text_decoration_color(mut self, color: impl Into<Hsla>) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.color = Some(color.into());
self
}
fn text_decoration_solid(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_solid(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.wavy = false;
self
}
fn text_decoration_wavy(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_wavy(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.wavy = true;
self
}
fn text_decoration_0(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_0(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.thickness = px(0.);
self
}
fn text_decoration_1(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_1(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.thickness = px(1.);
self
}
fn text_decoration_2(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_2(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.thickness = px(2.);
self
}
fn text_decoration_4(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_4(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.thickness = px(4.);
self
}
fn text_decoration_8(mut self) -> Self
where
Self: Sized,
{
fn text_decoration_8(mut self) -> Self {
let style = self.text_style().get_or_insert_with(Default::default);
let underline = style.underline.get_or_insert_with(Default::default);
underline.thickness = px(8.);
self
}
fn font(mut self, family_name: impl Into<SharedString>) -> Self
where
Self: Sized,
{
fn font(mut self, family_name: impl Into<SharedString>) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_family = Some(family_name.into());
self
}
fn line_height(mut self, line_height: impl Into<DefiniteLength>) -> Self
where
Self: Sized,
{
fn line_height(mut self, line_height: impl Into<DefiniteLength>) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.line_height = Some(line_height.into());

View file

@ -206,7 +206,7 @@ impl<V: Render> From<View<V>> for AnyView {
impl<ParentViewState: 'static> Element<ParentViewState> for AnyView {
type ElementState = Box<dyn Any>;
fn id(&self) -> Option<ElementId> {
fn element_id(&self) -> Option<ElementId> {
Some(self.model.entity_id.into())
}

View file

@ -130,7 +130,7 @@ fn generate_predefined_setter(
let method = quote! {
#[doc = #doc_string]
fn #method_name(mut self) -> Self where Self: std::marker::Sized {
fn #method_name(mut self) -> Self {
let style = self.style();
#(#field_assignments)*
self
@ -163,7 +163,7 @@ fn generate_custom_value_setter(
let method = quote! {
#[doc = #doc_string]
fn #method_name(mut self, length: impl std::clone::Clone + Into<gpui::#length_type>) -> Self where Self: std::marker::Sized {
fn #method_name(mut self, length: impl std::clone::Clone + Into<gpui::#length_type>) -> Self {
let style = self.style();
#(#field_assignments)*
self

View file

@ -1,6 +1,6 @@
use editor::Editor;
use gpui::{
div, uniform_list, Component, Div, ParentElement, Render, StatelessInteractive, Styled, Task,
div, uniform_list, Component, Node, ParentComponent, Render, Styled, Task,
UniformListScrollHandle, View, ViewContext, VisualContext, WindowContext,
};
use std::{cmp, sync::Arc};
@ -139,11 +139,11 @@ impl<D: PickerDelegate> Picker<D> {
}
impl<D: PickerDelegate> Render for Picker<D> {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
div()
.context("picker")
.key_context("picker")
.size_full()
.elevation_2(cx)
.on_action(Self::select_next)

View file

@ -1,12 +1,12 @@
use crate::story::Story;
use gpui::{px, Div, Render};
use gpui::{prelude::*, px, Node, Render};
use theme2::{default_color_scales, ColorScaleStep};
use ui::prelude::*;
pub struct ColorsStory;
impl Render for ColorsStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let color_scales = default_color_scales();

View file

@ -1,6 +1,5 @@
use gpui::{
actions, div, Div, FocusHandle, Focusable, FocusableKeyDispatch, KeyBinding, ParentElement,
Render, StatefulInteractivity, StatelessInteractive, Styled, View, VisualContext,
actions, div, prelude::*, FocusHandle, Focusable, KeyBinding, Node, Render, Stateful, View,
WindowContext,
};
use theme2::ActiveTheme;
@ -28,7 +27,7 @@ impl FocusStory {
}
impl Render for FocusStory {
type Element = Div<Self, StatefulInteractivity<Self>, FocusableKeyDispatch<Self>>;
type Element = Focusable<Self, Stateful<Self, Node<Self>>>;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
let theme = cx.theme();
@ -42,7 +41,7 @@ impl Render for FocusStory {
div()
.id("parent")
.focusable()
.context("parent")
.key_context("parent")
.on_action(|_, action: &ActionA, cx| {
println!("Action A dispatched on parent");
})
@ -62,7 +61,7 @@ impl Render for FocusStory {
.child(
div()
.track_focus(&self.child_1_focus)
.context("child-1")
.key_context("child-1")
.on_action(|_, action: &ActionB, cx| {
println!("Action B dispatched on child 1 during");
})
@ -82,7 +81,7 @@ impl Render for FocusStory {
.child(
div()
.track_focus(&self.child_2_focus)
.context("child-2")
.key_context("child-2")
.on_action(|_, action: &ActionC, cx| {
println!("Action C dispatched on child 2");
})

View file

@ -1,5 +1,5 @@
use crate::{story::Story, story_selector::ComponentStory};
use gpui::{Div, Render, StatefulInteractivity, View, VisualContext};
use gpui::{prelude::*, Node, Render, Stateful, View};
use strum::IntoEnumIterator;
use ui::prelude::*;
@ -12,7 +12,7 @@ impl KitchenSinkStory {
}
impl Render for KitchenSinkStory {
type Element = Div<Self, StatefulInteractivity<Self>>;
type Element = Stateful<Self, Node<Self>>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let component_stories = ComponentStory::iter()

View file

@ -1,11 +1,7 @@
use std::sync::Arc;
use fuzzy::StringMatchCandidate;
use gpui::{
div, Component, Div, KeyBinding, ParentElement, Render, StatelessInteractive, Styled, Task,
View, VisualContext, WindowContext,
};
use gpui::{div, prelude::*, KeyBinding, Node, Render, Styled, Task, View, WindowContext};
use picker::{Picker, PickerDelegate};
use std::sync::Arc;
use theme2::ActiveTheme;
pub struct PickerStory {
@ -38,7 +34,7 @@ impl Delegate {
}
impl PickerDelegate for Delegate {
type ListItem = Div<Picker<Self>>;
type ListItem = Node<Picker<Self>>;
fn match_count(&self) -> usize {
self.candidates.len()
@ -207,7 +203,7 @@ impl PickerStory {
}
impl Render for PickerStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
div()

View file

@ -1,6 +1,5 @@
use gpui::{
div, px, Component, Div, ParentElement, Render, SharedString, StatefulInteractivity, Styled,
View, VisualContext, WindowContext,
div, prelude::*, px, Node, Render, SharedString, Stateful, Styled, View, WindowContext,
};
use theme2::ActiveTheme;
@ -13,7 +12,7 @@ impl ScrollStory {
}
impl Render for ScrollStory {
type Element = Div<Self, StatefulInteractivity<Self>>;
type Element = Stateful<Self, Node<Self>>;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
let theme = cx.theme();

View file

@ -1,4 +1,4 @@
use gpui::{div, white, Div, ParentElement, Render, Styled, View, VisualContext, WindowContext};
use gpui::{div, white, Node, ParentComponent, Render, Styled, View, VisualContext, WindowContext};
pub struct TextStory;
@ -9,7 +9,7 @@ impl TextStory {
}
impl Render for TextStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
div().size_full().bg(white()).child(concat!(

View file

@ -1,4 +1,4 @@
use gpui::{px, rgb, Div, Hsla, Render};
use gpui::{px, rgb, Hsla, Node, Render};
use ui::prelude::*;
use crate::story::Story;
@ -8,7 +8,7 @@ use crate::story::Story;
pub struct ZIndexStory;
impl Render for ZIndexStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)
@ -77,7 +77,7 @@ trait Styles: Styled + Sized {
}
}
impl<V: 'static> Styles for Div<V> {}
impl<V: 'static> Styles for Node<V> {}
#[derive(Component)]
struct ZIndexExample {

View file

@ -9,7 +9,7 @@ use std::sync::Arc;
use clap::Parser;
use gpui::{
div, px, size, AnyView, AppContext, Bounds, Div, Render, ViewContext, VisualContext,
div, px, size, AnyView, AppContext, Bounds, Node, Render, ViewContext, VisualContext,
WindowBounds, WindowOptions,
};
use log::LevelFilter;
@ -107,7 +107,7 @@ impl StoryWrapper {
}
impl Render for StoryWrapper {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
div()

View file

@ -40,12 +40,12 @@ pub use stories::*;
mod stories {
use super::*;
use crate::{ActiveTheme, Story};
use gpui::{div, img, px, Div, ParentElement, Render, Styled, ViewContext};
use gpui::{div, img, px, Node, ParentComponent, Render, Styled, ViewContext};
pub struct PlayerStory;
impl Render for PlayerStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx).child(

View file

@ -1,11 +1,11 @@
use gpui::{div, Component, Div, ParentElement, Styled, ViewContext};
use gpui::{div, Component, Node, ParentComponent, Styled, ViewContext};
use crate::ActiveTheme;
pub struct Story {}
impl Story {
pub fn container<V: 'static>(cx: &mut ViewContext<V>) -> Div<V> {
pub fn container<V: 'static>(cx: &mut ViewContext<V>) -> Node<V> {
div()
.size_full()
.flex()

View file

@ -44,12 +44,12 @@ pub use stories::*;
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
use gpui::{Node, Render};
pub struct AvatarStory;
impl Render for AvatarStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -1,6 +1,6 @@
use std::sync::Arc;
use gpui::{div, DefiniteLength, Hsla, MouseButton, WindowContext};
use gpui::{div, DefiniteLength, Hsla, MouseButton, StatefulInteractiveComponent, WindowContext};
use crate::{
h_stack, prelude::*, Icon, IconButton, IconColor, IconElement, Label, LabelColor,
@ -236,13 +236,13 @@ pub use stories::*;
mod stories {
use super::*;
use crate::{h_stack, v_stack, LabelColor, Story};
use gpui::{rems, Div, Render};
use gpui::{rems, Node, Render};
use strum::IntoEnumIterator;
pub struct ButtonStory;
impl Render for ButtonStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let states = InteractionState::iter();

View file

@ -1,9 +1,5 @@
use gpui::{div, prelude::*, Component, ElementId, Styled, ViewContext};
use std::sync::Arc;
use gpui::{
div, Component, ElementId, ParentElement, StatefulInteractive, StatelessInteractive, Styled,
ViewContext,
};
use theme2::ActiveTheme;
use crate::{Icon, IconColor, IconElement, Selection};
@ -175,12 +171,12 @@ pub use stories::*;
mod stories {
use super::*;
use crate::{h_stack, Story};
use gpui::{Div, Render};
use gpui::{Node, Render};
pub struct CheckboxStory;
impl Render for CheckboxStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -65,12 +65,12 @@ pub use stories::*;
mod stories {
use super::*;
use crate::story::Story;
use gpui::{Div, Render};
use gpui::{Node, Render};
pub struct ContextMenuStory;
impl Render for ContextMenuStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -47,12 +47,12 @@ pub use stories::*;
mod stories {
use super::*;
use crate::{Button, Story};
use gpui::{Div, Render};
use gpui::{Node, Render};
pub struct DetailsStory;
impl Render for DetailsStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -1,11 +1,11 @@
use gpui::Div;
use gpui::Node;
use crate::{prelude::*, v_stack};
/// Create an elevated surface.
///
/// Must be used inside of a relative parent element
pub fn elevated_surface<V: 'static>(level: ElevationIndex, cx: &mut ViewContext<V>) -> Div<V> {
pub fn elevated_surface<V: 'static>(level: ElevationIndex, cx: &mut ViewContext<V>) -> Node<V> {
let colors = cx.theme().colors();
// let shadow = BoxShadow {
@ -23,6 +23,6 @@ pub fn elevated_surface<V: 'static>(level: ElevationIndex, cx: &mut ViewContext<
.shadow(level.shadow())
}
pub fn modal<V>(cx: &mut ViewContext<V>) -> Div<V> {
pub fn modal<V: 'static>(cx: &mut ViewContext<V>) -> Node<V> {
elevated_surface(ElevationIndex::ModalSurface, cx)
}

View file

@ -33,12 +33,12 @@ pub use stories::*;
mod stories {
use super::*;
use crate::{static_players, Story};
use gpui::{Div, Render};
use gpui::{Node, Render};
pub struct FacepileStory;
impl Render for FacepileStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let players = static_players();

View file

@ -204,7 +204,7 @@ pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use gpui::{Div, Render};
use gpui::{Node, Render};
use strum::IntoEnumIterator;
use crate::Story;
@ -214,7 +214,7 @@ mod stories {
pub struct IconStory;
impl Render for IconStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let icons = Icon::iter();

View file

@ -1,9 +1,7 @@
use std::sync::Arc;
use gpui::{rems, MouseButton};
use crate::{h_stack, prelude::*};
use crate::{ClickHandler, Icon, IconColor, IconElement};
use gpui::{prelude::*, rems, MouseButton};
use std::sync::Arc;
struct IconButtonHandlers<V: 'static> {
click: Option<ClickHandler<V>>,

View file

@ -1,6 +1,5 @@
use crate::prelude::*;
use crate::Label;
use crate::LabelColor;
use crate::{prelude::*, Label, LabelColor};
use gpui::prelude::*;
#[derive(Default, PartialEq)]
pub enum InputVariant {
@ -111,12 +110,12 @@ pub use stories::*;
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
use gpui::{Node, Render};
pub struct InputStory;
impl Render for InputStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -158,13 +158,13 @@ pub use stories::*;
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
use gpui::{Node, Render};
use itertools::Itertools;
pub struct KeybindingStory;
impl Render for KeybindingStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let all_modifier_permutations = ModifierKey::iter().permutations(2);

View file

@ -196,12 +196,12 @@ pub use stories::*;
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
use gpui::{Node, Render};
pub struct LabelStory;
impl Render for LabelStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -74,7 +74,7 @@ impl<V: 'static> Modal<V> {
}
}
impl<V: 'static> ParentElement<V> for Modal<V> {
impl<V: 'static> ParentComponent<V> for Modal<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}

View file

@ -1,5 +1,5 @@
use crate::prelude::*;
use crate::{h_stack, v_stack, Keybinding, Label, LabelColor};
use crate::{h_stack, prelude::*, v_stack, Keybinding, Label, LabelColor};
use gpui::prelude::*;
#[derive(Component)]
pub struct Palette {
@ -159,7 +159,7 @@ pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use gpui::{Div, Render};
use gpui::{Node, Render};
use crate::{ModifierKeys, Story};
@ -168,7 +168,7 @@ mod stories {
pub struct PaletteStory;
impl Render for PaletteStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
{

View file

@ -1,4 +1,4 @@
use gpui::{AbsoluteLength, AnyElement};
use gpui::{prelude::*, AbsoluteLength, AnyElement};
use smallvec::SmallVec;
use crate::prelude::*;
@ -113,7 +113,7 @@ impl<V: 'static> Panel<V> {
}
}
impl<V: 'static> ParentElement<V> for Panel<V> {
impl<V: 'static> ParentComponent<V> for Panel<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
@ -126,12 +126,12 @@ pub use stories::*;
mod stories {
use super::*;
use crate::{Label, Story};
use gpui::{Div, Render};
use gpui::{InteractiveComponent, Node, Render};
pub struct PanelStory;
impl Render for PanelStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -1,17 +1,17 @@
use gpui::{div, Div};
use gpui::{div, Node};
use crate::StyledExt;
/// Horizontally stacks elements.
///
/// Sets `flex()`, `flex_row()`, `items_center()`
pub fn h_stack<V: 'static>() -> Div<V> {
pub fn h_stack<V: 'static>() -> Node<V> {
div().h_flex()
}
/// Vertically stacks elements.
///
/// Sets `flex()`, `flex_col()`
pub fn v_stack<V: 'static>() -> Div<V> {
pub fn v_stack<V: 'static>() -> Node<V> {
div().v_flex()
}

View file

@ -1,6 +1,6 @@
use crate::prelude::*;
use crate::{Icon, IconColor, IconElement, Label, LabelColor};
use gpui::{red, Div, ElementId, Render, View, VisualContext};
use gpui::{prelude::*, red, ElementId, Node, Render, View};
#[derive(Component, Clone)]
pub struct Tab {
@ -21,7 +21,7 @@ struct TabDragState {
}
impl Render for TabDragState {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
div().w_8().h_4().bg(red())
@ -178,7 +178,7 @@ mod stories {
pub struct TabStory;
impl Render for TabStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let git_statuses = GitStatus::iter();

View file

@ -1,7 +1,6 @@
use gpui::AnyElement;
use smallvec::SmallVec;
use crate::prelude::*;
use gpui::{prelude::*, AnyElement};
use smallvec::SmallVec;
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
pub enum ToastOrigin {
@ -59,7 +58,7 @@ impl<V: 'static> Toast<V> {
}
}
impl<V: 'static> ParentElement<V> for Toast<V> {
impl<V: 'static> ParentComponent<V> for Toast<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
@ -70,7 +69,7 @@ pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use gpui::{Div, Render};
use gpui::{Node, Render};
use crate::{Label, Story};
@ -79,7 +78,7 @@ mod stories {
pub struct ToastStory;
impl Render for ToastStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -1,4 +1,4 @@
use gpui::{div, Component, ParentElement};
use gpui::{div, Component, ParentComponent};
use crate::{Icon, IconColor, IconElement, IconSize};

View file

@ -1,4 +1,4 @@
use gpui::{div, Div, ParentElement, Render, SharedString, Styled, ViewContext};
use gpui::{div, Node, ParentComponent, Render, SharedString, Styled, ViewContext};
use theme2::ActiveTheme;
#[derive(Clone, Debug)]
@ -13,7 +13,7 @@ impl TextTooltip {
}
impl Render for TextTooltip {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let theme = cx.theme();

View file

@ -1,8 +1,8 @@
use gpui::rems;
use gpui::Rems;
pub use gpui::{
div, Component, Element, ElementId, ParentElement, SharedString, StatefulInteractive,
StatelessInteractive, Styled, ViewContext, WindowContext,
div, Component, Element, ElementId, InteractiveComponent, ParentComponent, SharedString, Styled,
ViewContext, WindowContext,
};
pub use crate::elevation::*;

View file

@ -1,11 +1,11 @@
use gpui::Div;
use gpui::Node;
use crate::prelude::*;
pub struct Story {}
impl Story {
pub fn container<V: 'static>(cx: &mut ViewContext<V>) -> Div<V> {
pub fn container<V: 'static>(cx: &mut ViewContext<V>) -> Node<V> {
div()
.size_full()
.flex()

View file

@ -1,4 +1,4 @@
use gpui::{Div, ElementInteractivity, KeyDispatch, Styled, UniformList, ViewContext};
use gpui::{Styled, ViewContext};
use theme2::ActiveTheme;
use crate::{ElevationIndex, UITextSize};
@ -93,11 +93,4 @@ pub trait StyledExt: Styled + Sized {
}
}
impl<V, I, F> StyledExt for Div<V, I, F>
where
I: ElementInteractivity<V>,
F: KeyDispatch<V>,
{
}
impl<V> StyledExt for UniformList<V> {}
impl<E: Styled> StyledExt for E {}

View file

@ -1,6 +1,6 @@
use crate::prelude::*;
use crate::{Icon, IconButton, Label, Panel, PanelSide};
use gpui::{rems, AbsoluteLength};
use gpui::{prelude::*, rems, AbsoluteLength};
#[derive(Component)]
pub struct AssistantPanel {
@ -77,11 +77,11 @@ pub use stories::*;
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
use gpui::{Node, Render};
pub struct AssistantPanelStory;
impl Render for AssistantPanelStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -1,9 +1,7 @@
use crate::{h_stack, prelude::*, HighlightedText};
use gpui::{prelude::*, Node};
use std::path::PathBuf;
use crate::prelude::*;
use crate::{h_stack, HighlightedText};
use gpui::Div;
#[derive(Clone)]
pub struct Symbol(pub Vec<HighlightedText>);
@ -18,7 +16,7 @@ impl Breadcrumb {
Self { path, symbols }
}
fn render_separator<V: 'static>(&self, cx: &WindowContext) -> Div<V> {
fn render_separator<V: 'static>(&self, cx: &WindowContext) -> Node<V> {
div()
.child(" ")
.text_color(cx.theme().colors().text_muted)
@ -79,7 +77,7 @@ mod stories {
pub struct BreadcrumbStory;
impl Render for BreadcrumbStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -235,12 +235,12 @@ mod stories {
empty_buffer_example, hello_world_rust_buffer_example,
hello_world_rust_buffer_with_status_example, Story,
};
use gpui::{rems, Div, Render};
use gpui::{rems, Node, Render};
pub struct BufferStory;
impl Render for BufferStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -1,4 +1,4 @@
use gpui::{Div, Render, View, VisualContext};
use gpui::{Node, Render, View, VisualContext};
use crate::prelude::*;
use crate::{h_stack, Icon, IconButton, IconColor, Input};
@ -27,9 +27,9 @@ impl BufferSearch {
}
impl Render for BufferSearch {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Node<Self> {
h_stack()
.bg(cx.theme().colors().toolbar_background)
.p_2()

View file

@ -1,7 +1,6 @@
use crate::{prelude::*, Icon, IconButton, Input, Label, LabelColor};
use chrono::NaiveDateTime;
use crate::prelude::*;
use crate::{Icon, IconButton, Input, Label, LabelColor};
use gpui::prelude::*;
#[derive(Component)]
pub struct ChatPanel {
@ -108,7 +107,7 @@ pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use chrono::DateTime;
use gpui::{Div, Render};
use gpui::{Node, Render};
use crate::{Panel, Story};
@ -117,7 +116,7 @@ mod stories {
pub struct ChatPanelStory;
impl Render for ChatPanelStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -1,7 +1,8 @@
use crate::{prelude::*, Toggle};
use crate::{
static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon, List, ListHeader,
prelude::*, static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon,
List, ListHeader, Toggle,
};
use gpui::prelude::*;
#[derive(Component)]
pub struct CollabPanel {
@ -92,12 +93,12 @@ pub use stories::*;
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
use gpui::{Node, Render};
pub struct CollabPanelStory;
impl Render for CollabPanelStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -27,7 +27,7 @@ pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use gpui::{Div, Render};
use gpui::{Node, Render};
use crate::Story;
@ -36,7 +36,7 @@ mod stories {
pub struct CommandPaletteStory;
impl Render for CommandPaletteStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -25,7 +25,7 @@ pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use gpui::{Div, Render};
use gpui::{Node, Render};
use crate::Story;
@ -34,7 +34,7 @@ mod stories {
pub struct CopilotModalStory;
impl Render for CopilotModalStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -1,6 +1,6 @@
use std::path::PathBuf;
use gpui::{Div, Render, View, VisualContext};
use gpui::{Node, Render, View, VisualContext};
use crate::prelude::*;
use crate::{
@ -48,9 +48,9 @@ impl EditorPane {
}
impl Render for EditorPane {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Node<Self> {
v_stack()
.w_full()
.h_full()

View file

@ -40,12 +40,12 @@ pub use stories::*;
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
use gpui::{Node, Render};
pub struct LanguageSelectorStory;
impl Render for LanguageSelectorStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -40,12 +40,12 @@ pub use stories::*;
mod stories {
use super::*;
use crate::{hello_world_rust_buffer_example, Story};
use gpui::{Div, Render};
use gpui::{Node, Render};
pub struct MultiBufferStory;
impl Render for MultiBufferStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -1,10 +1,9 @@
use crate::utils::naive_format_distance_from_now;
use crate::{
h_stack, prelude::*, static_new_notification_items_2, v_stack, Avatar, ButtonOrIconButton,
Icon, IconElement, Label, LabelColor, LineHeightStyle, ListHeaderMeta, ListSeparator,
PublicPlayer, UnreadIndicator,
h_stack, prelude::*, static_new_notification_items_2, utils::naive_format_distance_from_now,
v_stack, Avatar, ButtonOrIconButton, ClickHandler, Icon, IconElement, Label, LabelColor,
LineHeightStyle, ListHeader, ListHeaderMeta, ListSeparator, PublicPlayer, UnreadIndicator,
};
use crate::{ClickHandler, ListHeader};
use gpui::prelude::*;
#[derive(Component)]
pub struct NotificationsPanel {
@ -353,12 +352,12 @@ pub use stories::*;
mod stories {
use super::*;
use crate::{Panel, Story};
use gpui::{Div, Render};
use gpui::{Node, Render};
pub struct NotificationsPanelStory;
impl Render for NotificationsPanelStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -59,7 +59,7 @@ impl<V: 'static> Pane<V> {
}
}
impl<V: 'static> ParentElement<V> for Pane<V> {
impl<V: 'static> ParentComponent<V> for Pane<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}

View file

@ -1,7 +1,8 @@
use crate::prelude::*;
use crate::{
static_project_panel_project_items, static_project_panel_single_items, Input, List, ListHeader,
prelude::*, static_project_panel_project_items, static_project_panel_single_items, Input, List,
ListHeader,
};
use gpui::prelude::*;
#[derive(Component)]
pub struct ProjectPanel {
@ -54,12 +55,12 @@ pub use stories::*;
mod stories {
use super::*;
use crate::{Panel, Story};
use gpui::{Div, Render};
use gpui::{Node, Render};
pub struct ProjectPanelStory;
impl Render for ProjectPanelStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -36,12 +36,12 @@ pub use stories::*;
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
use gpui::{Node, Render};
pub struct RecentProjectsStory;
impl Render for RecentProjectsStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -1,5 +1,5 @@
use crate::prelude::*;
use crate::{Icon, IconButton, Tab};
use crate::{prelude::*, Icon, IconButton, Tab};
use gpui::prelude::*;
#[derive(Component)]
pub struct TabBar {
@ -100,12 +100,12 @@ pub use stories::*;
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
use gpui::{Node, Render};
pub struct TabBarStory;
impl Render for TabBarStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -83,11 +83,11 @@ pub use stories::*;
mod stories {
use super::*;
use crate::Story;
use gpui::{Div, Render};
use gpui::{Node, Render};
pub struct TerminalStory;
impl Render for TerminalStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -39,7 +39,7 @@ pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use gpui::{Div, Render};
use gpui::{Node, Render};
use crate::Story;
@ -48,7 +48,7 @@ mod stories {
pub struct ThemeSelectorStory;
impl Render for ThemeSelectorStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -1,7 +1,7 @@
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use gpui::{Div, Render, View, VisualContext};
use gpui::{Node, Render, View, VisualContext};
use crate::prelude::*;
use crate::settings::user_settings;
@ -86,9 +86,9 @@ impl TitleBar {
}
impl Render for TitleBar {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Node<Self> {
let settings = user_settings(cx);
// let has_focus = cx.window_is_active();
@ -202,9 +202,9 @@ mod stories {
}
impl Render for TitleBarStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Node<Self> {
Story::container(cx)
.child(Story::title_for::<_, TitleBar>(cx))
.child(Story::label(cx, "Default"))

View file

@ -73,7 +73,7 @@ mod stories {
use std::path::PathBuf;
use std::str::FromStr;
use gpui::{Div, Render};
use gpui::{Node, Render};
use crate::{Breadcrumb, HighlightedText, Icon, IconButton, Story, Symbol};
@ -82,7 +82,7 @@ mod stories {
pub struct ToolbarStory;
impl Render for ToolbarStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -77,7 +77,7 @@ pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use gpui::{Div, Render};
use gpui::{Node, Render};
use crate::Story;
@ -86,7 +86,7 @@ mod stories {
pub struct TrafficLightsStory;
impl Render for TrafficLightsStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx)

View file

@ -1,7 +1,7 @@
use std::sync::Arc;
use chrono::DateTime;
use gpui::{px, relative, Div, Render, Size, View, VisualContext};
use gpui::{px, relative, Node, Render, Size, View, VisualContext};
use settings2::Settings;
use theme2::ThemeSettings;
@ -192,9 +192,9 @@ impl Workspace {
}
impl Render for Workspace {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Node<Self> {
let root_group = PaneGroup::new_panes(
vec![Pane::new(
"pane-0",
@ -388,7 +388,7 @@ mod stories {
}
impl Render for WorkspaceStory {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
div().child(self.workspace.clone())

View file

@ -1,7 +1,7 @@
use crate::{status_bar::StatusItemView, Axis, Workspace};
use gpui::{
div, Action, AnyView, AppContext, Div, Entity, EntityId, EventEmitter, ParentElement, Render,
Subscription, View, ViewContext, WeakView, WindowContext,
div, Action, AnyView, AppContext, Entity, EntityId, EventEmitter, Node, ParentComponent,
Render, Subscription, View, ViewContext, WeakView, WindowContext,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -419,7 +419,7 @@ impl Dock {
}
impl Render for Dock {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
todo!()
@ -621,7 +621,7 @@ impl PanelButtons {
// }
impl Render for PanelButtons {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
// todo!()
@ -647,7 +647,7 @@ impl StatusItemView for PanelButtons {
#[cfg(any(test, feature = "test-support"))]
pub mod test {
use super::*;
use gpui::{div, Div, ViewContext, WindowContext};
use gpui::{div, Node, ViewContext, WindowContext};
pub struct TestPanel {
pub position: DockPosition,
@ -672,7 +672,7 @@ pub mod test {
}
impl Render for TestPanel {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
div()

View file

@ -1,6 +1,6 @@
use gpui::{
div, px, AnyView, Div, EventEmitter, FocusHandle, ParentElement, Render, StatelessInteractive,
Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
div, prelude::*, px, AnyView, EventEmitter, FocusHandle, InteractiveComponent, Node,
ParentComponent, Render, Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
};
use ui::v_stack;
@ -76,7 +76,7 @@ impl ModalLayer {
}
impl Render for ModalLayer {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let Some(active_modal) = &self.active_modal else {

View file

@ -165,7 +165,7 @@ impl Workspace {
pub mod simple_message_notification {
use super::{Notification, NotificationEvent};
use gpui::{AnyElement, AppContext, Div, EventEmitter, Render, TextStyle, ViewContext};
use gpui::{AnyElement, AppContext, EventEmitter, Node, Render, TextStyle, ViewContext};
use serde::Deserialize;
use std::{borrow::Cow, sync::Arc};
@ -252,7 +252,7 @@ pub mod simple_message_notification {
}
impl Render for MessageNotification {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
todo!()

View file

@ -1,5 +1,3 @@
// mod dragged_item_receiver;
use crate::{
item::{Item, ItemHandle, ItemSettings, WeakItemHandle},
toolbar::Toolbar,
@ -9,9 +7,9 @@ use crate::{
use anyhow::Result;
use collections::{HashMap, HashSet, VecDeque};
use gpui::{
actions, register_action, AppContext, AsyncWindowContext, Component, Div, EntityId,
EventEmitter, FocusHandle, Model, PromptLevel, Render, Task, View, ViewContext, VisualContext,
WeakView, WindowContext,
actions, prelude::*, register_action, AppContext, AsyncWindowContext, Component, EntityId,
EventEmitter, FocusHandle, Model, Node, PromptLevel, Render, Task, View, ViewContext,
VisualContext, WeakView, WindowContext,
};
use parking_lot::Mutex;
use project2::{Project, ProjectEntryId, ProjectPath};
@ -1903,7 +1901,7 @@ impl Pane {
// }
impl Render for Pane {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
v_stack()
@ -2928,7 +2926,7 @@ struct DraggedTab {
}
impl Render for DraggedTab {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
div().w_8().h_4().bg(gpui::red())

View file

@ -2,8 +2,8 @@ use std::any::TypeId;
use crate::{ItemHandle, Pane};
use gpui::{
div, AnyView, Component, Div, ParentElement, Render, Styled, Subscription, View, ViewContext,
WindowContext,
div, AnyView, Component, Node, ParentComponent, Render, Styled, Subscription, View,
ViewContext, WindowContext,
};
use theme2::ActiveTheme;
use util::ResultExt;
@ -34,7 +34,7 @@ pub struct StatusBar {
}
impl Render for StatusBar {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
div()

View file

@ -1,6 +1,6 @@
use crate::ItemHandle;
use gpui::{
AnyView, Div, Entity, EntityId, EventEmitter, Render, View, ViewContext, WindowContext,
AnyView, Entity, EntityId, EventEmitter, Node, Render, View, ViewContext, WindowContext,
};
pub enum ToolbarItemEvent {
@ -52,7 +52,7 @@ pub struct Toolbar {
}
impl Render for Toolbar {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
todo!()

View file

@ -36,12 +36,11 @@ use futures::{
Future, FutureExt, StreamExt,
};
use gpui::{
actions, div, point, rems, size, Action, AnyModel, AnyView, AnyWeakView, AppContext,
AsyncAppContext, AsyncWindowContext, Bounds, Component, Div, Entity, EntityId, EventEmitter,
FocusHandle, GlobalPixels, KeyContext, Model, ModelContext, ParentElement, Point, Render, Size,
StatefulInteractive, StatelessInteractive, StatelessInteractivity, Styled, Subscription, Task,
View, ViewContext, VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle,
WindowOptions,
actions, div, point, prelude::*, rems, size, Action, AnyModel, AnyView, AnyWeakView,
AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Component, Entity, EntityId,
EventEmitter, FocusHandle, GlobalPixels, KeyContext, Model, ModelContext, Node,
ParentComponent, Point, Render, Size, Styled, Subscription, Task, View, ViewContext, WeakView,
WindowBounds, WindowContext, WindowHandle, WindowOptions,
};
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
use itertools::Itertools;
@ -443,7 +442,6 @@ struct Follower {
impl AppState {
#[cfg(any(test, feature = "test-support"))]
pub fn test(cx: &mut AppContext) -> Arc<Self> {
use gpui::Context;
use node_runtime::FakeNodeRuntime;
use settings2::SettingsStore;
@ -531,13 +529,7 @@ pub enum Event {
pub struct Workspace {
weak_self: WeakView<Self>,
focus_handle: FocusHandle,
workspace_actions: Vec<
Box<
dyn Fn(
Div<Workspace, StatelessInteractivity<Workspace>>,
) -> Div<Workspace, StatelessInteractivity<Workspace>>,
>,
>,
workspace_actions: Vec<Box<dyn Fn(Node<Workspace>) -> Node<Workspace>>>,
zoomed: Option<AnyWeakView>,
zoomed_position: Option<DockPosition>,
center: PaneGroup,
@ -3450,7 +3442,6 @@ impl Workspace {
#[cfg(any(test, feature = "test-support"))]
pub fn test_new(project: Model<Project>, cx: &mut ViewContext<Self>) -> Self {
use gpui::Context;
use node_runtime::FakeNodeRuntime;
let client = project.read(cx).client();
@ -3512,10 +3503,7 @@ impl Workspace {
}));
}
fn add_workspace_actions_listeners(
&self,
mut div: Div<Workspace, StatelessInteractivity<Workspace>>,
) -> Div<Workspace, StatelessInteractivity<Workspace>> {
fn add_workspace_actions_listeners(&self, mut div: Node<Workspace>) -> Node<Workspace> {
for action in self.workspace_actions.iter() {
div = (action)(div)
}
@ -3740,14 +3728,14 @@ fn notify_if_database_failed(workspace: WindowHandle<Workspace>, cx: &mut AsyncA
impl EventEmitter<Event> for Workspace {}
impl Render for Workspace {
type Element = Div<Self>;
type Element = Node<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let mut context = KeyContext::default();
context.add("Workspace");
self.add_workspace_actions_listeners(div())
.context(context)
.key_context(context)
.relative()
.size_full()
.flex()