Rename Drawable::boxed to into_element and make containers generic

Multi-element are now generic over any drawable child, which can be converted
into an element.

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
Co-Authored-By: Max Brunsfeld <max@zed.dev>
This commit is contained in:
Antonio Scandurra
2023-04-21 12:43:19 -06:00
committed by Nathan Sobo
co-authored by Nathan Sobo Max Brunsfeld
parent 4d433663bd
commit 03619dfa55
80 changed files with 1132 additions and 1434 deletions
+19 -19
View File
@@ -4685,7 +4685,7 @@ mod tests {
impl super::View for View {
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
post_inc(&mut self.render_count);
Empty::new().boxed()
Empty::new().into_element()
}
fn ui_name() -> &'static str {
@@ -4737,7 +4737,7 @@ mod tests {
impl super::View for View {
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
Empty::new().boxed()
Empty::new().into_element()
}
fn ui_name() -> &'static str {
@@ -4809,11 +4809,11 @@ mod tests {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Element<Self> {
enum Handler {}
let mouse_down_count = self.mouse_down_count.clone();
MouseEventHandler::<Handler, _>::new(0, cx, |_, _| Empty::new().boxed())
MouseEventHandler::<Handler, _>::new(0, cx, |_, _| Empty::new())
.on_down(MouseButton::Left, move |_, _, _| {
mouse_down_count.fetch_add(1, SeqCst);
})
.boxed()
.into_element()
}
fn ui_name() -> &'static str {
@@ -4873,7 +4873,7 @@ mod tests {
}
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
Empty::new().boxed()
Empty::new().into_element()
}
}
@@ -5391,7 +5391,7 @@ mod tests {
impl super::View for View {
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
Empty::new().boxed()
Empty::new().into_element()
}
fn ui_name() -> &'static str {
@@ -5458,7 +5458,7 @@ mod tests {
impl super::View for View {
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
Empty::new().boxed()
Empty::new().into_element()
}
fn ui_name() -> &'static str {
@@ -5639,7 +5639,7 @@ mod tests {
impl View for ViewA {
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
Empty::new().boxed()
Empty::new().into_element()
}
fn ui_name() -> &'static str {
@@ -5657,7 +5657,7 @@ mod tests {
impl View for ViewB {
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
Empty::new().boxed()
Empty::new().into_element()
}
fn ui_name() -> &'static str {
@@ -5805,7 +5805,7 @@ mod tests {
impl super::View for View {
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
Empty::new().boxed()
Empty::new().into_element()
}
fn ui_name() -> &'static str {
@@ -5932,7 +5932,7 @@ mod tests {
impl super::View for View1 {
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
Empty::new().boxed()
Empty::new().into_element()
}
fn ui_name() -> &'static str {
"View1"
@@ -5940,7 +5940,7 @@ mod tests {
}
impl super::View for View2 {
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
Empty::new().boxed()
Empty::new().into_element()
}
fn ui_name() -> &'static str {
"View2"
@@ -6110,7 +6110,7 @@ mod tests {
}
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
Empty::new().boxed()
Empty::new().into_element()
}
}
@@ -6172,7 +6172,7 @@ mod tests {
}
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
Empty::new().named(format!("render count: {}", post_inc(&mut self.0)))
Empty::new().into_named_element(format!("render count: {}", post_inc(&mut self.0)))
}
}
@@ -6261,7 +6261,7 @@ mod tests {
}
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
Empty::new().boxed()
Empty::new().into_element()
}
}
@@ -6342,7 +6342,7 @@ mod tests {
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
self.rendered.set(true);
Empty::new().boxed()
Empty::new().into_element()
}
}
@@ -6367,9 +6367,9 @@ mod tests {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Element<Self> {
if let Some(child) = self.child.as_ref() {
ChildView::new(child, cx).boxed()
ChildView::new(child, cx).into_element()
} else {
Empty::new().boxed()
Empty::new().into_element()
}
}
}
@@ -6408,7 +6408,7 @@ mod tests {
}
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
Empty::new().boxed()
Empty::new().into_element()
}
}
}
+90 -31
View File
@@ -47,7 +47,7 @@ use std::{
};
use util::ResultExt;
pub trait Drawable<V: View> {
pub trait Drawable<V: View>: 'static {
type LayoutState;
type PaintState;
@@ -92,7 +92,7 @@ pub trait Drawable<V: View> {
cx: &ViewContext<V>,
) -> serde_json::Value;
fn boxed(self) -> Element<V>
fn into_element(self) -> Element<V>
where
Self: 'static + Sized,
{
@@ -103,17 +103,7 @@ pub trait Drawable<V: View> {
}
}
fn into_root(self, cx: &ViewContext<V>) -> RootElement<V>
where
Self: 'static + Sized,
{
RootElement {
element: self.boxed(),
view: cx.handle().downgrade(),
}
}
fn named(self, name: impl Into<Cow<'static, str>>) -> Element<V>
fn into_named_element(self, name: impl Into<Cow<'static, str>>) -> Element<V>
where
Self: 'static + Sized,
{
@@ -124,53 +114,63 @@ pub trait Drawable<V: View> {
}
}
fn into_root_element(self, cx: &ViewContext<V>) -> RootElement<V>
where
Self: 'static + Sized,
{
RootElement {
element: self.into_element(),
view: cx.handle().downgrade(),
}
}
fn constrained(self) -> ConstrainedBox<V>
where
Self: 'static + Sized,
{
ConstrainedBox::new(self.boxed())
ConstrainedBox::new(self.into_element())
}
fn aligned(self) -> Align<V>
where
Self: 'static + Sized,
{
Align::new(self.boxed())
Align::new(self.into_element())
}
fn clipped(self) -> Clipped<V>
where
Self: 'static + Sized,
{
Clipped::new(self.boxed())
Clipped::new(self.into_element())
}
fn contained(self) -> Container<V>
where
Self: 'static + Sized,
{
Container::new(self.boxed())
Container::new(self.into_element())
}
fn expanded(self) -> Expanded<V>
where
Self: 'static + Sized,
{
Expanded::new(self.boxed())
Expanded::new(self.into_element())
}
fn flex(self, flex: f32, expanded: bool) -> FlexItem<V>
where
Self: 'static + Sized,
{
FlexItem::new(self.boxed()).flex(flex, expanded)
FlexItem::new(self.into_element()).flex(flex, expanded)
}
fn flex_float(self) -> FlexItem<V>
where
Self: 'static + Sized,
{
FlexItem::new(self.boxed()).float()
FlexItem::new(self.into_element()).float()
}
fn with_tooltip<Tag: 'static>(
@@ -184,7 +184,7 @@ pub trait Drawable<V: View> {
where
Self: 'static + Sized,
{
Tooltip::new::<Tag, V>(id, text, action, style, self.boxed(), cx)
Tooltip::new::<Tag, V>(id, text, action, style, self.into_element(), cx)
}
fn with_resize_handle<Tag: 'static>(
@@ -199,7 +199,7 @@ pub trait Drawable<V: View> {
Self: 'static + Sized,
{
Resizable::new::<Tag, V>(
self.boxed(),
self.into_element(),
element_id,
side,
handle_size,
@@ -499,6 +499,64 @@ impl<V: View> Element<V> {
}
}
impl<V: View> Drawable<V> for Element<V> {
type LayoutState = ();
type PaintState = ();
fn layout(
&mut self,
constraint: SizeConstraint,
view: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) {
let size = self.layout(constraint, view, cx);
(size, ())
}
fn paint(
&mut self,
scene: &mut SceneBuilder,
bounds: RectF,
visible_bounds: RectF,
_: &mut Self::LayoutState,
view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState {
self.paint(scene, bounds.origin(), visible_bounds, view, cx);
}
fn rect_for_text_range(
&self,
range_utf16: Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
view: &V,
cx: &ViewContext<V>,
) -> Option<RectF> {
self.rect_for_text_range(range_utf16, view, cx)
}
fn debug(
&self,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
view: &V,
cx: &ViewContext<V>,
) -> serde_json::Value {
self.debug(view, cx)
}
fn into_element(self) -> Element<V>
where
Self: Sized,
{
self
}
}
pub struct RootElement<V: View> {
element: Element<V>,
view: WeakViewHandle<V>,
@@ -510,7 +568,7 @@ impl<V: View> RootElement<V> {
}
}
pub trait Component<V: View> {
pub trait Component<V: View>: 'static {
fn render(&self, view: &mut V, cx: &mut ViewContext<V>) -> Element<V>;
}
@@ -718,21 +776,22 @@ impl<V: View, R: View> Drawable<V> for RootElement<R> {
}
pub trait ParentElement<'a, V: View>: Extend<Element<V>> + Sized {
fn add_children(&mut self, children: impl IntoIterator<Item = Element<V>>) {
self.extend(children);
fn add_children<D: Drawable<V>>(&mut self, children: impl IntoIterator<Item = D>) {
self.extend(children.into_iter().map(|child| child.into_element()));
}
fn add_child(&mut self, child: Element<V>) {
self.add_children(Some(child));
fn add_child<D: Drawable<V>>(&mut self, child: D) {
self.extend(Some(child.into_element()));
}
fn with_children(mut self, children: impl IntoIterator<Item = Element<V>>) -> Self {
self.add_children(children);
fn with_children<D: Drawable<V>>(mut self, children: impl IntoIterator<Item = D>) -> Self {
self.extend(children.into_iter().map(|child| child.into_element()));
self
}
fn with_child(self, child: Element<V>) -> Self {
self.with_children(Some(child))
fn with_child<D: Drawable<V>>(mut self, child: D) -> Self {
self.extend(Some(child.into_element()));
self
}
}
+1 -1
View File
@@ -25,7 +25,7 @@ where
impl<V: View, F> Drawable<V> for Canvas<V, F>
where
F: FnMut(&mut SceneBuilder, RectF, RectF, &mut V, &mut ViewContext<V>),
F: 'static + FnMut(&mut SceneBuilder, RectF, RectF, &mut V, &mut ViewContext<V>),
{
type LayoutState = ();
type PaintState = ();
+2 -2
View File
@@ -28,9 +28,9 @@ impl<V: View> ToJson for Constraint<V> {
}
impl<V: View> ConstrainedBox<V> {
pub fn new(child: Element<V>) -> Self {
pub fn new(child: impl Drawable<V>) -> Self {
Self {
child,
child: child.into_element(),
constraint: Constraint::Static(Default::default()),
}
}
+2 -2
View File
@@ -13,9 +13,9 @@ pub struct Expanded<V: View> {
}
impl<V: View> Expanded<V> {
pub fn new(child: Element<V>) -> Self {
pub fn new(child: impl Drawable<V>) -> Self {
Self {
child,
child: child.into_element(),
full_width: true,
full_height: true,
}
+2 -2
View File
@@ -403,13 +403,13 @@ pub struct FlexItem<V: View> {
}
impl<V: View> FlexItem<V> {
pub fn new(child: Element<V>) -> Self {
pub fn new(child: impl Drawable<V>) -> Self {
FlexItem {
metadata: FlexParentData {
flex: None,
float: false,
},
child,
child: child.into_element(),
}
}
+2 -2
View File
@@ -12,9 +12,9 @@ pub struct Hook<V: View> {
}
impl<V: View> Hook<V> {
pub fn new(child: Element<V>) -> Self {
pub fn new(child: impl Drawable<V>) -> Self {
Self {
child,
child: child.into_element(),
after_layout: None,
}
}
+2 -3
View File
@@ -49,11 +49,10 @@ impl<V: View> Drawable<V> for KeystrokeLabel {
Label::new(keystroke.to_string(), self.text_style.clone())
.contained()
.with_style(self.container_style)
.boxed()
}))
.boxed()
.into_element()
} else {
Empty::new().collapsed().boxed()
Empty::new().collapsed().into_element()
};
let size = element.layout(constraint, view, cx);
+8 -8
View File
@@ -347,21 +347,21 @@ impl<V: View> Drawable<V> for List<V> {
}
impl<V: View> ListState<V> {
pub fn new<F>(
pub fn new<D, F>(
element_count: usize,
orientation: Orientation,
overdraw: f32,
render_item: F,
mut render_item: F,
) -> Self
where
V: View,
F: 'static + FnMut(&mut V, usize, &mut ViewContext<V>) -> Element<V>,
D: Drawable<V>,
F: 'static + FnMut(&mut V, usize, &mut ViewContext<V>) -> D,
{
let mut items = SumTree::new();
items.extend((0..element_count).map(|_| ListItem::Unrendered), &());
Self(Rc::new(RefCell::new(StateInner {
last_layout_width: None,
render_item: Box::new(render_item),
render_item: Box::new(move |view, ix, cx| render_item(view, ix, cx).into_element()),
rendered_range: 0..0,
items,
logical_scroll_top: None,
@@ -660,7 +660,7 @@ mod tests {
let elements = elements.clone();
move |_, ix, _| {
let (id, height) = elements.borrow()[ix];
TestElement::new(id, height).boxed()
TestElement::new(id, height).into_element()
}
});
@@ -765,7 +765,7 @@ mod tests {
let elements = elements.clone();
move |_, ix, _| {
let (id, height) = elements.borrow()[ix];
TestElement::new(id, height).boxed()
TestElement::new(id, height).into_element()
}
});
@@ -921,7 +921,7 @@ mod tests {
}
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
Empty::new().boxed()
Empty::new().into_element()
}
}
@@ -32,13 +32,13 @@ pub struct MouseEventHandler<Tag: 'static, V: View> {
/// Element which provides a render_child callback with a MouseState and paints a mouse
/// region under (or above) it for easy mouse event handling.
impl<Tag, V: View> MouseEventHandler<Tag, V> {
pub fn new<F>(region_id: usize, cx: &mut ViewContext<V>, render_child: F) -> Self
pub fn new<D, F>(region_id: usize, cx: &mut ViewContext<V>, render_child: F) -> Self
where
V: View,
F: FnOnce(&mut MouseState, &mut ViewContext<V>) -> Element<V>,
D: Drawable<V>,
F: FnOnce(&mut MouseState, &mut ViewContext<V>) -> D,
{
let mut mouse_state = cx.mouse_state::<Tag>(region_id);
let child = render_child(&mut mouse_state, cx);
let child = render_child(&mut mouse_state, cx).into_element();
let notify_on_hover = mouse_state.accessed_hovered();
let notify_on_click = mouse_state.accessed_clicked();
Self {
@@ -58,10 +58,10 @@ impl<Tag, V: View> MouseEventHandler<Tag, V> {
/// Modifies the MouseEventHandler to render the MouseRegion above the child element. Useful
/// for drag and drop handling and similar events which should be captured before the child
/// gets the opportunity
pub fn above<F>(region_id: usize, cx: &mut ViewContext<V>, render_child: F) -> Self
pub fn above<D, F>(region_id: usize, cx: &mut ViewContext<V>, render_child: F) -> Self
where
V: View,
F: FnOnce(&mut MouseState, &mut ViewContext<V>) -> Element<V>,
D: Drawable<V>,
F: FnOnce(&mut MouseState, &mut ViewContext<V>) -> D,
{
let mut handler = Self::new(region_id, cx, render_child);
handler.above = true;
+2 -2
View File
@@ -73,9 +73,9 @@ impl AnchorCorner {
}
impl<V: View> Overlay<V> {
pub fn new(child: Element<V>) -> Self {
pub fn new(child: impl Drawable<V>) -> Self {
Self {
child,
child: child.into_element(),
anchor_position: None,
anchor_corner: AnchorCorner::TopLeft,
fit_mode: OverlayFitMode::None,
+1 -2
View File
@@ -108,7 +108,6 @@ impl<V: View> Resizable<V> {
Axis::Horizontal => constrained.with_max_width(state.custom_dimension.get()),
Axis::Vertical => constrained.with_max_height(state.custom_dimension.get()),
}
.boxed()
})
.on_after_layout({
let state = state.clone();
@@ -116,7 +115,7 @@ impl<V: View> Resizable<V> {
state.actual_dimension.set(side.relevant_component(size));
}
})
.boxed();
.into_element();
Self {
side,
+1 -1
View File
@@ -308,7 +308,7 @@ mod tests {
}
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
Empty::new().boxed()
Empty::new().into_element()
}
}
}
+9 -11
View File
@@ -71,8 +71,7 @@ impl<V: View> Tooltip<V> {
style.clone(),
action.as_ref().map(|a| a.boxed_clone()),
true,
)
.boxed();
);
Some(
Overlay::new(
Self::render_tooltip(focused_view_id, text, style, action, false)
@@ -80,14 +79,13 @@ impl<V: View> Tooltip<V> {
.dynamically(move |constraint, view, cx| {
SizeConstraint::strict_along(
Axis::Vertical,
collapsed_tooltip.layout(constraint, view, cx).y(),
collapsed_tooltip.layout(constraint, view, cx).0.y(),
)
})
.boxed(),
}),
)
.with_fit_mode(OverlayFitMode::SwitchAnchor)
.with_anchor_position(state.position.get())
.boxed(),
.into_element(),
)
} else {
None
@@ -119,7 +117,7 @@ impl<V: View> Tooltip<V> {
cx.notify();
}
})
.boxed();
.into_element();
Self {
child,
tooltip,
@@ -145,9 +143,9 @@ impl<V: View> Tooltip<V> {
};
if measure {
text.flex(1., false).boxed()
text.flex(1., false).into_element()
} else {
text.flex(1., false).aligned().boxed()
text.flex(1., false).aligned().into_element()
}
})
.with_children(action.and_then(|action| {
@@ -158,9 +156,9 @@ impl<V: View> Tooltip<V> {
style.keystroke.text,
);
if measure {
Some(keystroke_label.boxed())
Some(keystroke_label.into_element())
} else {
Some(keystroke_label.aligned().boxed())
Some(keystroke_label.aligned().into_element())
}
}))
.contained()
+1 -1
View File
@@ -243,6 +243,6 @@ impl View for EmptyView {
}
fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
Drawable::boxed(Empty::new())
Empty::new().into_element()
}
}
+39 -52
View File
@@ -94,7 +94,7 @@ impl View for Select {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Element<Self> {
if self.item_count == 0 {
return Empty::new().boxed();
return Empty::new().into_element();
}
enum Header {}
@@ -107,71 +107,58 @@ impl View for Select {
};
let mut result = Flex::column().with_child(
MouseEventHandler::<Header, _>::new(self.handle.id(), cx, |mouse_state, cx| {
Container::new((self.render_item)(
(self.render_item)(
self.selected_item_ix,
ItemType::Header,
mouse_state.hovered(),
cx,
))
)
.contained()
.with_style(style.header)
.boxed()
})
.on_click(
MouseButton::Left,
move |_, _, cx: &mut EventContext<Self>| cx.dispatch_action(ToggleSelect),
)
.boxed(),
),
);
if self.is_open {
result.add_child(
Overlay::new(
Container::new(
ConstrainedBox::new(
UniformList::new(
self.list_state.clone(),
self.item_count,
cx,
move |this, mut range, items, cx| {
let selected_item_ix = this.selected_item_ix;
range.end = range.end.min(this.item_count);
items.extend(range.map(|ix| {
MouseEventHandler::<Item, _>::new(
ix,
cx,
|mouse_state, cx| {
(this.render_item)(
ix,
if ix == selected_item_ix {
ItemType::Selected
} else {
ItemType::Unselected
},
mouse_state.hovered(),
cx,
)
},
)
.on_click(
MouseButton::Left,
move |_, _, cx: &mut EventContext<Self>| {
cx.dispatch_action(SelectItem(ix))
},
)
.boxed()
}))
result.add_child(Overlay::new(
UniformList::new(
self.list_state.clone(),
self.item_count,
cx,
move |this, mut range, items, cx| {
let selected_item_ix = this.selected_item_ix;
range.end = range.end.min(this.item_count);
items.extend(range.map(|ix| {
MouseEventHandler::<Item, _>::new(ix, cx, |mouse_state, cx| {
(this.render_item)(
ix,
if ix == selected_item_ix {
ItemType::Selected
} else {
ItemType::Unselected
},
mouse_state.hovered(),
cx,
)
})
.on_click(
MouseButton::Left,
move |_, _, cx: &mut EventContext<Self>| {
cx.dispatch_action(SelectItem(ix))
},
)
.boxed(),
)
.with_max_height(200.)
.boxed(),
)
.with_style(style.menu)
.boxed(),
.into_element()
}))
},
)
.boxed(),
)
.constrained()
.with_max_height(200.)
.contained()
.with_style(style.menu),
));
}
result.boxed()
result.into_element()
}
}