This commit is contained in:
Nathan Sobo 2023-04-11 18:21:56 -06:00
parent 3de8fe0f87
commit e115baa60c
94 changed files with 1477 additions and 1310 deletions

View file

@ -46,13 +46,13 @@ impl<V: View> Element<V> for Align<V> {
fn layout(
&mut self,
view: &mut V,
mut constraint: SizeConstraint,
view: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) {
let mut size = constraint.max;
constraint.min = Vector2F::zero();
let child_size = self.child.layout(view, constraint, cx);
let child_size = self.child.layout(constraint, view, cx);
if size.x().is_infinite() {
size.set_x(child_size.x());
}
@ -64,11 +64,11 @@ impl<V: View> Element<V> for Align<V> {
fn paint(
&mut self,
view: &mut V,
scene: &mut SceneBuilder,
bounds: RectF,
visible_bounds: RectF,
_: &mut Self::LayoutState,
view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState {
let my_center = bounds.size() / 2.;
@ -78,33 +78,33 @@ impl<V: View> Element<V> for Align<V> {
let child_target = child_center + child_center * self.alignment;
self.child.paint(
view,
scene,
bounds.origin() - (child_target - my_target),
visible_bounds,
view,
cx,
);
}
fn rect_for_text_range(
&self,
view: &V,
range_utf16: std::ops::Range<usize>,
_: RectF,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
view: &V,
cx: &ViewContext<V>,
) -> Option<RectF> {
self.child.rect_for_text_range(view, range_utf16, cx)
self.child.rect_for_text_range(range_utf16, view, cx)
}
fn debug(
&self,
view: &V,
bounds: pathfinder_geometry::rect::RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
view: &V,
cx: &ViewContext<V>,
) -> json::Value {
json!({

View file

@ -25,7 +25,7 @@ where
impl<V: View, F> Element<V> for Canvas<V, F>
where
F: FnMut(RectF, RectF, &mut ViewContext<V>),
F: FnMut(&mut SceneBuilder, RectF, RectF, &mut V, &mut ViewContext<V>),
{
type LayoutState = ();
type PaintState = ();
@ -58,7 +58,7 @@ where
view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState {
self.0(bounds, visible_bounds, cx)
self.0(scene, bounds, visible_bounds, view, cx)
}
fn rect_for_text_range(

View file

@ -37,10 +37,10 @@ impl<V: View> Element<V> for Clipped<V> {
view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState {
cx.scene.push_layer(Some(bounds));
self.child
.paint(scene, bounds.origin(), visible_bounds, view, cx);
cx.scene.pop_layer();
scene.paint_layer(Some(bounds), |scene| {
self.child
.paint(scene, bounds.origin(), visible_bounds, view, cx)
})
}
fn rect_for_text_range(

View file

@ -157,7 +157,7 @@ impl<V: View> Element<V> for ConstrainedBox<V> {
view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState {
cx.paint_layer(Some(visible_bounds), |cx| {
scene.paint_layer(Some(visible_bounds), |scene| {
self.child
.paint(scene, bounds.origin(), visible_bounds, view, cx);
})

View file

@ -221,7 +221,7 @@ impl<V: View> Element<V> for Container<V> {
);
if let Some(shadow) = self.style.shadow.as_ref() {
cx.scene.push_shadow(scene::Shadow {
scene.push_shadow(scene::Shadow {
bounds: quad_bounds + shadow.offset,
corner_radius: self.style.corner_radius,
sigma: shadow.blur,
@ -231,7 +231,7 @@ impl<V: View> Element<V> for Container<V> {
if let Some(hit_bounds) = quad_bounds.intersection(visible_bounds) {
if let Some(style) = self.style.cursor {
cx.scene.push_cursor_region(CursorRegion {
scene.push_cursor_region(CursorRegion {
bounds: hit_bounds,
style,
});
@ -242,7 +242,7 @@ impl<V: View> Element<V> for Container<V> {
quad_bounds.origin() + vec2f(self.style.padding.left, self.style.padding.top);
if self.style.border.overlay {
cx.scene.push_quad(Quad {
scene.push_quad(Quad {
bounds: quad_bounds,
background: self.style.background_color,
border: Default::default(),
@ -252,16 +252,16 @@ impl<V: View> Element<V> for Container<V> {
self.child
.paint(scene, child_origin, visible_bounds, view, cx);
cx.scene.push_layer(None);
cx.scene.push_quad(Quad {
scene.push_layer(None);
scene.push_quad(Quad {
bounds: quad_bounds,
background: self.style.overlay_color,
border: self.style.border,
corner_radius: self.style.corner_radius,
});
cx.scene.pop_layer();
scene.pop_layer();
} else {
cx.scene.push_quad(Quad {
scene.push_quad(Quad {
bounds: quad_bounds,
background: self.style.background_color,
border: self.style.border,
@ -277,14 +277,14 @@ impl<V: View> Element<V> for Container<V> {
.paint(scene, child_origin, visible_bounds, view, cx);
if self.style.overlay_color.is_some() {
cx.scene.push_layer(None);
cx.scene.push_quad(Quad {
scene.push_layer(None);
scene.push_quad(Quad {
bounds: quad_bounds,
background: self.style.overlay_color,
border: Default::default(),
corner_radius: 0.,
});
cx.scene.pop_layer();
scene.pop_layer();
}
}
}

View file

@ -33,7 +33,7 @@ impl<V: View> Element<V> for Empty {
fn layout(
&mut self,
constraint: SizeConstraint,
_: &V,
_: &mut V,
_: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) {
let x = if constraint.max.x().is_finite() && !self.collapsed {
@ -56,6 +56,7 @@ impl<V: View> Element<V> for Empty {
_: RectF,
_: RectF,
_: &mut Self::LayoutState,
_: &mut V,
_: &mut ViewContext<V>,
) -> Self::PaintState {
}

View file

@ -42,7 +42,7 @@ impl<V: View> Element<V> for Expanded<V> {
&mut self,
mut constraint: SizeConstraint,
view: &mut V,
cx: &mut ViewContext<Self>,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) {
if self.full_width {
constraint.min.set_x(constraint.max.x());

View file

@ -73,7 +73,7 @@ impl<V: View> Flex<V> {
remaining_space: &mut f32,
remaining_flex: &mut f32,
cross_axis_max: &mut f32,
view: &V,
view: &mut V,
cx: &mut ViewContext<V>,
) {
let cross_axis = self.axis.invert();
@ -124,7 +124,7 @@ impl<V: View> Element<V> for Flex<V> {
fn layout(
&mut self,
constraint: SizeConstraint,
view: &V,
view: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) {
let mut total_flex = None;
@ -253,7 +253,7 @@ impl<V: View> Element<V> for Flex<V> {
bounds: RectF,
visible_bounds: RectF,
remaining_space: &mut Self::LayoutState,
view: &V,
view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState {
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
@ -261,16 +261,16 @@ impl<V: View> Element<V> for Flex<V> {
let mut remaining_space = *remaining_space;
let overflowing = remaining_space < 0.;
if overflowing {
cx.scene.push_layer(Some(visible_bounds));
scene.push_layer(Some(visible_bounds));
}
if let Some(scroll_state) = &self.scroll_state {
cx.scene.push_mouse_region(
scene.push_mouse_region(
crate::MouseRegion::new::<Self>(scroll_state.1, 0, bounds)
.on_scroll({
let scroll_state = scroll_state.0.read(cx).clone();
let axis = self.axis;
move |e, cx| {
move |e, _: &mut V, cx| {
if remaining_space < 0. {
let scroll_delta = e.delta.raw();
@ -298,7 +298,7 @@ impl<V: View> Element<V> for Flex<V> {
}
}
})
.on_move(|_, _| { /* Capture move events */ }),
.on_move(|_, _: &mut V, _| { /* Capture move events */ }),
)
}
@ -356,7 +356,7 @@ impl<V: View> Element<V> for Flex<V> {
}
if overflowing {
cx.scene.pop_layer();
scene.pop_layer();
}
}
@ -372,7 +372,7 @@ impl<V: View> Element<V> for Flex<V> {
) -> Option<RectF> {
self.children
.iter()
.find_map(|child| child.rect_for_text_range(view, range_utf16.clone(), cx))
.find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
}
fn debug(
@ -431,7 +431,7 @@ impl<V: View> Element<V> for FlexItem<V> {
fn layout(
&mut self,
constraint: SizeConstraint,
view: &V,
view: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) {
let size = self.child.layout(constraint, view, cx);
@ -444,7 +444,7 @@ impl<V: View> Element<V> for FlexItem<V> {
bounds: RectF,
visible_bounds: RectF,
_: &mut Self::LayoutState,
view: &V,
view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState {
self.child
@ -474,7 +474,6 @@ impl<V: View> Element<V> for FlexItem<V> {
_: &Self::LayoutState,
_: &Self::PaintState,
view: &V,
cx: &ViewContext<V>,
) -> Value {
json!({

View file

@ -35,7 +35,7 @@ impl<V: View> Element<V> for Hook<V> {
fn layout(
&mut self,
constraint: SizeConstraint,
view: &V,
view: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) {
let size = self.child.layout(constraint, view, cx);

View file

@ -62,7 +62,7 @@ impl<V: View> Element<V> for Image {
fn layout(
&mut self,
constraint: SizeConstraint,
view: &V,
_: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) {
let data = match &self.source {
@ -94,8 +94,8 @@ impl<V: View> Element<V> for Image {
bounds: RectF,
_: RectF,
layout: &mut Self::LayoutState,
_: &V,
cx: &mut ViewContext<V>,
_: &mut V,
_: &mut ViewContext<V>,
) -> Self::PaintState {
if let Some(data) = layout {
scene.push_image(scene::Image {

View file

@ -41,12 +41,11 @@ impl<V: View> Element<V> for KeystrokeLabel {
fn layout(
&mut self,
constraint: SizeConstraint,
view: &V,
view: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, ElementBox<V>) {
let mut element = if let Some(keystrokes) =
cx.app
.keystrokes_for_action(self.window_id, self.view_id, self.action.as_ref())
cx.keystrokes_for_action(self.window_id, self.view_id, self.action.as_ref())
{
Flex::row()
.with_children(keystrokes.iter().map(|keystroke| {
@ -70,7 +69,7 @@ impl<V: View> Element<V> for KeystrokeLabel {
bounds: RectF,
visible_bounds: RectF,
element: &mut ElementBox<V>,
view: &V,
view: &mut V,
cx: &mut ViewContext<V>,
) {
element.paint(scene, bounds.origin(), visible_bounds, view, cx);

View file

@ -134,13 +134,15 @@ impl<V: View> Element<V> for Label {
fn layout(
&mut self,
constraint: SizeConstraint,
view: &V,
_: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) {
let runs = self.compute_runs();
let line =
cx.text_layout_cache
.layout_str(&self.text, self.style.text.font_size, runs.as_slice());
let line = cx.text_layout_cache().layout_str(
&self.text,
self.style.text.font_size,
runs.as_slice(),
);
let size = vec2f(
line.width()
@ -159,8 +161,8 @@ impl<V: View> Element<V> for Label {
bounds: RectF,
visible_bounds: RectF,
line: &mut Self::LayoutState,
_: &V,
cx: &mut ViewContext<Self>,
_: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState {
line.paint(
scene,

View file

@ -13,7 +13,6 @@ pub struct List<V: View> {
state: ListState<V>,
}
#[derive(Clone)]
pub struct ListState<V: View>(Rc<RefCell<StateInner<V>>>);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@ -42,7 +41,7 @@ pub struct ListOffset {
enum ListItem<V: View> {
Unrendered,
Rendered(Rc<ElementBox<V>>),
Rendered(Rc<RefCell<ElementBox<V>>>),
Removed(f32),
}
@ -145,7 +144,7 @@ impl<V: View> Element<V> for List<V> {
view,
cx,
) {
rendered_height += element.size().y();
rendered_height += element.borrow().size().y();
rendered_items.push_back(ListItem::Rendered(element));
}
}
@ -162,7 +161,7 @@ impl<V: View> Element<V> for List<V> {
if let Some(element) =
state.render_item(cursor.start().0, None, item_constraint, view, cx)
{
rendered_height += element.size().y();
rendered_height += element.borrow().size().y();
rendered_items.push_front(ListItem::Rendered(element));
}
} else {
@ -198,7 +197,7 @@ impl<V: View> Element<V> for List<V> {
if let Some(element) =
state.render_item(cursor.start().0, Some(item), item_constraint, view, cx)
{
leading_overdraw += element.size().y();
leading_overdraw += element.borrow().size().y();
rendered_items.push_front(ListItem::Rendered(element));
}
} else {
@ -264,12 +263,13 @@ impl<V: View> Element<V> for List<V> {
let state = self.state.clone();
let height = bounds.height();
let scroll_top = scroll_top.clone();
move |e, cx| {
move |e, view, cx| {
state.0.borrow_mut().scroll(
&scroll_top,
height,
*e.platform_event.delta.raw(),
e.platform_event.delta.precise(),
view,
cx,
)
}
@ -277,8 +277,10 @@ impl<V: View> Element<V> for List<V> {
);
let state = &mut *self.state.0.borrow_mut();
for (mut element, origin) in state.visible_elements(bounds, scroll_top) {
element.paint(scene, origin, visible_bounds, view, cx);
for (element, origin) in state.visible_elements(bounds, scroll_top) {
element
.borrow_mut()
.paint(scene, origin, visible_bounds, view, cx);
}
scene.pop_layer();
@ -304,11 +306,15 @@ impl<V: View> Element<V> for List<V> {
}
if let ListItem::Rendered(element) = item {
if let Some(rect) = element.rect_for_text_range(range_utf16.clone(), view, cx) {
if let Some(rect) =
element
.borrow()
.rect_for_text_range(range_utf16.clone(), view, cx)
{
return Some(rect);
}
item_origin.set_y(item_origin.y() + element.size().y());
item_origin.set_y(item_origin.y() + element.borrow().size().y());
cursor.next(&());
} else {
unreachable!();
@ -329,7 +335,7 @@ impl<V: View> Element<V> for List<V> {
let state = self.state.0.borrow_mut();
let visible_elements = state
.visible_elements(bounds, scroll_top)
.map(|e| e.0.debug(view, cx))
.map(|e| e.0.borrow().debug(view, cx))
.collect::<Vec<_>>();
let visible_range = scroll_top.item_ix..(scroll_top.item_ix + visible_elements.len());
json!({
@ -345,8 +351,7 @@ impl<V: View> ListState<V> {
element_count: usize,
orientation: Orientation,
overdraw: f32,
cx: &mut ViewContext<V>,
mut render_item: F,
render_item: F,
) -> Self
where
V: View,
@ -354,14 +359,9 @@ impl<V: View> ListState<V> {
{
let mut items = SumTree::new();
items.extend((0..element_count).map(|_| ListItem::Unrendered), &());
let handle = cx.weak_handle();
Self(Rc::new(RefCell::new(StateInner {
last_layout_width: None,
render_item: Box::new(move |ix, view, cx| {
render_item(view, ix, cx)
// let handle = handle.upgrade(cx)?;
// Some(cx.render(&handle, |view, cx| render_item(view, ix, cx)))
}),
render_item: Box::new(render_item),
rendered_range: 0..0,
items,
logical_scroll_top: None,
@ -439,6 +439,12 @@ impl<V: View> ListState<V> {
}
}
impl<V: View> Clone for ListState<V> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<V: View> StateInner<V> {
fn render_item(
&mut self,
@ -447,13 +453,13 @@ impl<V: View> StateInner<V> {
constraint: SizeConstraint,
view: &mut V,
cx: &mut ViewContext<V>,
) -> Option<Rc<ElementBox<V>>> {
) -> Option<Rc<RefCell<ElementBox<V>>>> {
if let Some(ListItem::Rendered(element)) = existing_element {
Some(element.clone())
} else {
let mut element = (self.render_item)(view, ix, cx);
element.layout(constraint, view, cx);
Some(element.into())
Some(Rc::new(RefCell::new(element)))
}
}
@ -469,7 +475,7 @@ impl<V: View> StateInner<V> {
&'a self,
bounds: RectF,
scroll_top: &ListOffset,
) -> impl Iterator<Item = (Rc<ElementBox<V>>, Vector2F)> + 'a {
) -> impl Iterator<Item = (Rc<RefCell<ElementBox<V>>>, Vector2F)> + 'a {
let mut item_origin = bounds.origin() - vec2f(0., scroll_top.offset_in_item);
let mut cursor = self.items.cursor::<Count>();
cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
@ -481,7 +487,7 @@ impl<V: View> StateInner<V> {
if let ListItem::Rendered(element) = item {
let result = (element.clone(), item_origin);
item_origin.set_y(item_origin.y() + element.size().y());
item_origin.set_y(item_origin.y() + element.borrow().size().y());
cursor.next(&());
return Some(result);
}
@ -526,7 +532,7 @@ impl<V: View> StateInner<V> {
if self.scroll_handler.is_some() {
let visible_range = self.visible_range(height, scroll_top);
self.scroll_handler.as_mut().unwrap()(visible_range, cx);
self.scroll_handler.as_mut().unwrap()(visible_range, view, cx);
}
cx.notify();
@ -557,7 +563,7 @@ impl<V: View> ListItem<V> {
fn remove(&self) -> Self {
match self {
ListItem::Unrendered => ListItem::Unrendered,
ListItem::Rendered(element) => ListItem::Removed(element.size().y()),
ListItem::Rendered(element) => ListItem::Removed(element.borrow().size().y()),
ListItem::Removed(height) => ListItem::Removed(*height),
}
}
@ -578,7 +584,7 @@ impl<V: View> sum_tree::Item for ListItem<V> {
count: 1,
rendered_count: 1,
unrendered_count: 0,
height: element.size().y(),
height: element.borrow().size().y(),
},
ListItem::Removed(height) => ListItemSummary {
count: 1,
@ -642,7 +648,6 @@ mod tests {
use super::*;
use crate::{elements::Empty, geometry::vector::vec2f, Entity};
use rand::prelude::*;
use std::env;
#[crate::test(self)]
fn test_layout(cx: &mut crate::AppContext) {

View file

@ -7,8 +7,8 @@ use crate::{
platform::CursorStyle,
platform::MouseButton,
scene::{
CursorRegion, HandlerSet, MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseHover,
MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
CursorRegion, EventContext, HandlerSet, MouseClick, MouseDown, MouseDownOut, MouseDrag,
MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
},
Element, ElementBox, MouseRegion, MouseState, SceneBuilder, SizeConstraint, View, ViewContext,
};
@ -31,13 +31,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, view: &mut V, cx: &mut ViewContext<V>, render_child: F) -> Self
pub fn new<F>(region_id: usize, cx: &mut ViewContext<V>, render_child: F) -> Self
where
V: View,
F: FnOnce(&mut MouseState, &mut V, &mut ViewContext<V>) -> ElementBox<V>,
F: FnOnce(&mut MouseState, &mut ViewContext<V>) -> ElementBox<V>,
{
let mut mouse_state = cx.mouse_state::<Tag>(region_id);
let child = render_child(&mut mouse_state, view, cx);
let child = render_child(&mut mouse_state, cx);
let notify_on_hover = mouse_state.accessed_hovered();
let notify_on_click = mouse_state.accessed_clicked();
Self {
@ -57,17 +57,12 @@ 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,
view: &mut V,
cx: &mut ViewContext<V>,
render_child: F,
) -> Self
pub fn above<F>(region_id: usize, cx: &mut ViewContext<V>, render_child: F) -> Self
where
V: View,
F: FnOnce(&mut MouseState, &mut V, &mut ViewContext<V>) -> ElementBox<V>,
F: FnOnce(&mut MouseState, &mut ViewContext<V>) -> ElementBox<V>,
{
let mut handler = Self::new(region_id, view, cx, render_child);
let mut handler = Self::new(region_id, cx, render_child);
handler.above = true;
handler
}
@ -82,14 +77,17 @@ impl<Tag, V: View> MouseEventHandler<Tag, V> {
self
}
pub fn on_move(mut self, handler: impl Fn(MouseMove, &mut ViewContext<V>) + 'static) -> Self {
pub fn on_move(
mut self,
handler: impl Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
) -> Self {
self.handlers = self.handlers.on_move(handler);
self
}
pub fn on_move_out(
mut self,
handler: impl Fn(MouseMoveOut, &mut ViewContext<V>) + 'static,
handler: impl Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
) -> Self {
self.handlers = self.handlers.on_move_out(handler);
self
@ -98,7 +96,7 @@ impl<Tag, V: View> MouseEventHandler<Tag, V> {
pub fn on_down(
mut self,
button: MouseButton,
handler: impl Fn(MouseDown, &mut ViewContext<V>) + 'static,
handler: impl Fn(MouseDown, &mut V, &mut EventContext<V>) + 'static,
) -> Self {
self.handlers = self.handlers.on_down(button, handler);
self
@ -107,7 +105,7 @@ impl<Tag, V: View> MouseEventHandler<Tag, V> {
pub fn on_up(
mut self,
button: MouseButton,
handler: impl Fn(MouseUp, &mut ViewContext<V>) + 'static,
handler: impl Fn(MouseUp, &mut V, &mut EventContext<V>) + 'static,
) -> Self {
self.handlers = self.handlers.on_up(button, handler);
self
@ -116,7 +114,7 @@ impl<Tag, V: View> MouseEventHandler<Tag, V> {
pub fn on_click(
mut self,
button: MouseButton,
handler: impl Fn(MouseClick, &mut ViewContext<V>) + 'static,
handler: impl Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
) -> Self {
self.handlers = self.handlers.on_click(button, handler);
self
@ -125,7 +123,7 @@ impl<Tag, V: View> MouseEventHandler<Tag, V> {
pub fn on_down_out(
mut self,
button: MouseButton,
handler: impl Fn(MouseDownOut, &mut ViewContext<V>) + 'static,
handler: impl Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
) -> Self {
self.handlers = self.handlers.on_down_out(button, handler);
self
@ -134,7 +132,7 @@ impl<Tag, V: View> MouseEventHandler<Tag, V> {
pub fn on_up_out(
mut self,
button: MouseButton,
handler: impl Fn(MouseUpOut, &mut ViewContext<V>) + 'static,
handler: impl Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
) -> Self {
self.handlers = self.handlers.on_up_out(button, handler);
self
@ -143,20 +141,23 @@ impl<Tag, V: View> MouseEventHandler<Tag, V> {
pub fn on_drag(
mut self,
button: MouseButton,
handler: impl Fn(MouseDrag, &mut ViewContext<V>) + 'static,
handler: impl Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
) -> Self {
self.handlers = self.handlers.on_drag(button, handler);
self
}
pub fn on_hover(mut self, handler: impl Fn(MouseHover, &mut ViewContext<V>) + 'static) -> Self {
pub fn on_hover(
mut self,
handler: impl Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
) -> Self {
self.handlers = self.handlers.on_hover(handler);
self
}
pub fn on_scroll(
mut self,
handler: impl Fn(MouseScrollWheel, &mut ViewContext<V>) + 'static,
handler: impl Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
) -> Self {
self.handlers = self.handlers.on_scroll(handler);
self
@ -191,14 +192,14 @@ impl<Tag, V: View> MouseEventHandler<Tag, V> {
let hit_bounds = self.hit_bounds(visible_bounds);
if let Some(style) = self.cursor_style {
cx.scene.push_cursor_region(CursorRegion {
scene.push_cursor_region(CursorRegion {
bounds: hit_bounds,
style,
});
}
cx.scene.push_mouse_region(
scene.push_mouse_region(
MouseRegion::from_handlers::<Tag>(
cx.current_view_id(),
cx.view_id(),
self.region_id,
hit_bounds,
self.handlers.clone(),
@ -236,7 +237,7 @@ impl<Tag, V: View> Element<V> for MouseEventHandler<Tag, V> {
self.child
.paint(scene, bounds.origin(), visible_bounds, view, cx);
cx.paint_layer(None, |cx| {
scene.paint_layer(None, |scene| {
self.paint_regions(scene, bounds, visible_bounds, cx);
});
} else {
@ -254,7 +255,7 @@ impl<Tag, V: View> Element<V> for MouseEventHandler<Tag, V> {
_: &Self::LayoutState,
_: &Self::PaintState,
view: &V,
cx: &ViewContext<Self>,
cx: &ViewContext<V>,
) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, view, cx)
}

View file

@ -127,7 +127,7 @@ impl<V: View> Element<V> for Overlay<V> {
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) {
let constraint = if self.anchor_position.is_some() {
SizeConstraint::new(Vector2F::zero(), cx.window_size)
SizeConstraint::new(Vector2F::zero(), cx.window_size())
} else {
constraint
};
@ -137,7 +137,7 @@ impl<V: View> Element<V> for Overlay<V> {
fn paint(
&mut self,
scene: SceneBuilder,
scene: &mut SceneBuilder,
bounds: RectF,
_: RectF,
size: &mut Self::LayoutState,
@ -163,9 +163,9 @@ impl<V: View> Element<V> for Overlay<V> {
OverlayFitMode::SnapToWindow => {
// Snap the horizontal edges of the overlay to the horizontal edges of the window if
// its horizontal bounds overflow
if bounds.max_x() > cx.window_size.x() {
if bounds.max_x() > cx.window_size().x() {
let mut lower_right = bounds.lower_right();
lower_right.set_x(cx.window_size.x());
lower_right.set_x(cx.window_size().x());
bounds = RectF::from_points(lower_right - *size, lower_right);
} else if bounds.min_x() < 0. {
let mut upper_left = bounds.origin();
@ -175,9 +175,9 @@ impl<V: View> Element<V> for Overlay<V> {
// Snap the vertical edges of the overlay to the vertical edges of the window if
// its vertical bounds overflow.
if bounds.max_y() > cx.window_size.y() {
if bounds.max_y() > cx.window_size().y() {
let mut lower_right = bounds.lower_right();
lower_right.set_y(cx.window_size.y());
lower_right.set_y(cx.window_size().y());
bounds = RectF::from_points(lower_right - *size, lower_right);
} else if bounds.min_y() < 0. {
let mut upper_left = bounds.origin();
@ -188,11 +188,11 @@ impl<V: View> Element<V> for Overlay<V> {
OverlayFitMode::SwitchAnchor => {
let mut anchor_corner = self.anchor_corner;
if bounds.max_x() > cx.window_size.x() {
if bounds.max_x() > cx.window_size().x() {
anchor_corner = anchor_corner.switch_axis(Axis::Horizontal);
}
if bounds.max_y() > cx.window_size.y() {
if bounds.max_y() > cx.window_size().y() {
anchor_corner = anchor_corner.switch_axis(Axis::Vertical);
}
@ -212,22 +212,21 @@ impl<V: View> Element<V> for Overlay<V> {
OverlayFitMode::None => {}
}
cx.paint_stacking_context(None, self.z_index, |cx| {
scene.paint_stacking_context(None, self.z_index, |scene| {
if self.hoverable {
enum OverlayHoverCapture {}
// Block hovers in lower stacking contexts
cx.scene
.push_mouse_region(MouseRegion::new::<OverlayHoverCapture>(
cx.current_view_id(),
cx.current_view_id(),
bounds,
));
scene.push_mouse_region(MouseRegion::new::<OverlayHoverCapture>(
cx.view_id(),
cx.view_id(),
bounds,
));
}
self.child.paint(
scene,
bounds.origin(),
RectF::new(Vector2F::zero(), cx.window_size),
RectF::new(Vector2F::zero(), cx.window_size()),
view,
cx,
);

View file

@ -154,32 +154,28 @@ impl<V: View> Element<V> for Resizable<V> {
view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState {
cx.scene.push_stacking_context(None, None);
scene.push_stacking_context(None, None);
let handle_region = self.side.of_rect(bounds, self.handle_size);
enum ResizeHandle {}
cx.scene.push_mouse_region(
MouseRegion::new::<ResizeHandle>(
cx.current_view_id(),
self.side as usize,
handle_region,
)
.on_down(MouseButton::Left, |_, _| {}) // This prevents the mouse down event from being propagated elsewhere
.on_drag(MouseButton::Left, {
let state = self.state.clone();
let side = self.side;
move |e, cx| {
let prev_width = state.actual_dimension.get();
state
.custom_dimension
.set(0f32.max(prev_width + side.compute_delta(e)).round());
cx.notify();
}
}),
scene.push_mouse_region(
MouseRegion::new::<ResizeHandle>(cx.view_id(), self.side as usize, handle_region)
.on_down(MouseButton::Left, |_, _: &mut V, _| {}) // This prevents the mouse down event from being propagated elsewhere
.on_drag(MouseButton::Left, {
let state = self.state.clone();
let side = self.side;
move |e, _: &mut V, cx| {
let prev_width = state.actual_dimension.get();
state
.custom_dimension
.set(0f32.max(prev_width + side.compute_delta(e)).round());
cx.notify();
}
}),
);
cx.scene.push_cursor_region(crate::CursorRegion {
scene.push_cursor_region(crate::CursorRegion {
bounds: handle_region,
style: match self.side.axis() {
Axis::Horizontal => CursorStyle::ResizeLeftRight,
@ -187,7 +183,7 @@ impl<V: View> Element<V> for Resizable<V> {
},
});
cx.scene.pop_stacking_context();
scene.pop_stacking_context();
self.child
.paint(scene, bounds.origin(), visible_bounds, view, cx);

View file

@ -8,11 +8,18 @@ use crate::{
/// Element which renders it's children in a stack on top of each other.
/// The first child determines the size of the others.
#[derive(Default)]
pub struct Stack<V: View> {
children: Vec<ElementBox<V>>,
}
impl<V: View> Default for Stack<V> {
fn default() -> Self {
Self {
children: Vec::new(),
}
}
}
impl<V: View> Stack<V> {
pub fn new() -> Self {
Self::default()
@ -53,7 +60,7 @@ impl<V: View> Element<V> for Stack<V> {
cx: &mut ViewContext<V>,
) -> Self::PaintState {
for child in &mut self.children {
cx.paint_layer(None, |cx| {
scene.paint_layer(None, |scene| {
child.paint(scene, bounds.origin(), visible_bounds, view, cx);
});
}

View file

@ -37,7 +37,7 @@ impl<V: View> Element<V> for Svg {
fn layout(
&mut self,
constraint: SizeConstraint,
view: &mut V,
_: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) {
match cx.asset_cache.svg(&self.path) {

View file

@ -98,7 +98,7 @@ impl<V: View> Element<V> for Text {
let shaped_lines = layout_highlighted_chunks(
chunks,
&self.style,
cx.text_layout_cache,
cx.text_layout_cache(),
&cx.font_cache,
usize::MAX,
self.text.matches('\n').count() + 1,
@ -213,9 +213,9 @@ impl<V: View> Element<V> for Text {
/// Perform text layout on a series of highlighted chunks of text.
pub fn layout_highlighted_chunks<'a>(
chunks: impl Iterator<Item = (&'a str, Option<HighlightStyle>)>,
text_style: &'a TextStyle,
text_layout_cache: &'a TextLayoutCache,
font_cache: &'a Arc<FontCache>,
text_style: &TextStyle,
text_layout_cache: &TextLayoutCache,
font_cache: &Arc<FontCache>,
max_line_len: usize,
max_line_count: usize,
) -> Vec<Line> {
@ -276,26 +276,22 @@ pub fn layout_highlighted_chunks<'a>(
#[cfg(test)]
mod tests {
use super::*;
use crate::{
elements::Empty, fonts, platform, AppContext, ElementBox, Entity, View, ViewContext,
};
use crate::{elements::Empty, fonts, AppContext, ElementBox, Entity, View, ViewContext};
#[crate::test(self)]
fn test_soft_wrapping_with_carriage_returns(cx: &mut AppContext) {
let (window_id, root_view) = cx.add_window(Default::default(), |_| TestView);
let mut presenter = cx.build_window(
window_id,
root_view.into_any(),
Box::new(platform::test::Window::new(Vector2F::new(800., 600.))),
);
fonts::with_font_cache(cx.font_cache().clone(), || {
let mut text = Text::new("Hello\r\n", Default::default()).with_soft_wrap(true);
let (_, state) = text.layout(
SizeConstraint::new(Default::default(), vec2f(f32::INFINITY, f32::INFINITY)),
&mut presenter.build_layout_context(Default::default(), false, cx),
);
assert_eq!(state.shaped_lines.len(), 2);
assert_eq!(state.wrap_boundaries.len(), 2);
root_view.update(cx, |view, cx| {
let mut text = Text::new("Hello\r\n", Default::default()).with_soft_wrap(true);
let (_, state) = text.layout(
SizeConstraint::new(Default::default(), vec2f(f32::INFINITY, f32::INFINITY)),
view,
cx,
);
assert_eq!(state.shaped_lines.len(), 2);
assert_eq!(state.wrap_boundaries.len(), 2);
});
});
}

View file

@ -55,7 +55,6 @@ impl<V: View> Tooltip<V> {
action: Option<Box<dyn Action>>,
style: TooltipStyle,
child: ElementBox<V>,
view: &mut V,
cx: &mut ViewContext<V>,
) -> Self {
struct ElementState<Tag>(Tag);
@ -78,10 +77,10 @@ impl<V: View> Tooltip<V> {
Overlay::new(
Self::render_tooltip(cx.window_id, focused_view_id, text, style, action, false)
.constrained()
.dynamically(move |constraint, cx| {
.dynamically(move |constraint, view, cx| {
SizeConstraint::strict_along(
Axis::Vertical,
collapsed_tooltip.layout(constraint, cx).y(),
collapsed_tooltip.layout(constraint, view, cx).y(),
)
})
.boxed(),
@ -93,36 +92,34 @@ impl<V: View> Tooltip<V> {
} else {
None
};
let child =
MouseEventHandler::<MouseEventHandlerState<Tag>>::new(id, view, cx, |_, _| child)
.on_hover(move |e, cx| {
let position = e.position;
let window_id = cx.window_id();
if let Some(view_id) = cx.view_id() {
if e.started {
if !state.visible.get() {
state.position.set(position);
let child = MouseEventHandler::<MouseEventHandlerState<Tag>, _>::new(id, cx, |_, _| child)
.on_hover(move |e, _, cx| {
let position = e.position;
let window_id = cx.window_id();
let view_id = cx.view_id();
if e.started {
if !state.visible.get() {
state.position.set(position);
let mut debounce = state.debounce.borrow_mut();
if debounce.is_none() {
*debounce = Some(cx.spawn({
let state = state.clone();
|mut cx| async move {
cx.background().timer(DEBOUNCE_TIMEOUT).await;
state.visible.set(true);
cx.update(|cx| cx.notify_view(window_id, view_id));
}
}));
let mut debounce = state.debounce.borrow_mut();
if debounce.is_none() {
*debounce = Some(cx.spawn({
let state = state.clone();
|_, mut cx| async move {
cx.background().timer(DEBOUNCE_TIMEOUT).await;
state.visible.set(true);
cx.update(|cx| cx.notify_view(window_id, view_id));
}
}
} else {
state.visible.set(false);
state.debounce.take();
cx.notify();
}));
}
}
})
.boxed();
} else {
state.visible.set(false);
state.debounce.take();
cx.notify();
}
})
.boxed();
Self {
child,
tooltip,
@ -137,7 +134,7 @@ impl<V: View> Tooltip<V> {
style: TooltipStyle,
action: Option<Box<dyn Action>>,
measure: bool,
) -> impl Element {
) -> impl Element<V> {
Flex::row()
.with_child({
let text = Text::new(text, style.text)
@ -181,7 +178,7 @@ impl<V: View> Element<V> for Tooltip<V> {
let size = self.child.layout(constraint, view, cx);
if let Some(tooltip) = self.tooltip.as_mut() {
tooltip.layout(
SizeConstraint::new(Vector2F::zero(), cx.window_size),
SizeConstraint::new(Vector2F::zero(), cx.window_size()),
view,
cx,
);

View file

@ -6,7 +6,6 @@ use crate::{
},
json::{self, json},
platform::ScrollWheelEvent,
scene::MouseScrollWheel,
ElementBox, MouseRegion, SceneBuilder, View, ViewContext,
};
use json::ToJson;
@ -47,7 +46,7 @@ pub struct UniformList<V: View> {
state: UniformListState,
item_count: usize,
#[allow(clippy::type_complexity)]
append_items: Box<dyn Fn(Range<usize>, &mut Vec<ElementBox<V>>, &mut V, &mut ViewContext<V>)>,
append_items: Box<dyn Fn(&mut V, Range<usize>, &mut Vec<ElementBox<V>>, &mut ViewContext<V>)>,
padding_top: f32,
padding_bottom: f32,
get_width_from_item: Option<usize>,
@ -63,19 +62,12 @@ impl<V: View> UniformList<V> {
) -> Self
where
V: View,
F: 'static + Fn(&mut V, Range<usize>, &mut Vec<ElementBox<V>>, &mut V, &mut ViewContext<V>),
F: 'static + Fn(&mut V, Range<usize>, &mut Vec<ElementBox<V>>, &mut ViewContext<V>),
{
let handle = cx.handle();
Self {
state,
item_count,
append_items: Box::new(move |range, items, cx| {
if let Some(handle) = handle.upgrade(cx) {
cx.render(&handle, |view, cx| {
append_items(view, range, items, cx);
});
}
}),
append_items: Box::new(append_items),
padding_top: 0.,
padding_bottom: 0.,
get_width_from_item: None,
@ -194,18 +186,18 @@ impl<V: View> Element<V> for UniformList<V> {
let sample_item_ix;
let sample_item;
if let Some(sample_ix) = self.get_width_from_item {
(self.append_items)(sample_ix..sample_ix + 1, &mut items, cx);
(self.append_items)(view, sample_ix..sample_ix + 1, &mut items, cx);
sample_item_ix = sample_ix;
if let Some(mut item) = items.pop() {
item_size = item.layout(constraint, cx);
item_size = item.layout(constraint, view, cx);
size.set_x(item_size.x());
sample_item = item;
} else {
return no_items;
}
} else {
(self.append_items)(0..1, &mut items, cx);
(self.append_items)(view, 0..1, &mut items, cx);
sample_item_ix = 0;
if let Some(mut item) = items.pop() {
item_size = item.layout(
@ -213,6 +205,7 @@ impl<V: View> Element<V> for UniformList<V> {
vec2f(constraint.max.x(), 0.0),
vec2f(constraint.max.x(), f32::INFINITY),
),
view,
cx,
);
item_size.set_x(size.x());
@ -249,16 +242,16 @@ impl<V: View> Element<V> for UniformList<V> {
if (start..end).contains(&sample_item_ix) {
if sample_item_ix > start {
(self.append_items)(start..sample_item_ix, &mut items, cx);
(self.append_items)(view, start..sample_item_ix, &mut items, cx);
}
items.push(sample_item);
if sample_item_ix < end {
(self.append_items)(sample_item_ix + 1..end, &mut items, cx);
(self.append_items)(view, sample_item_ix + 1..end, &mut items, cx);
}
} else {
(self.append_items)(start..end, &mut items, cx);
(self.append_items)(view, start..end, &mut items, cx);
}
for item in &mut items {
@ -289,20 +282,16 @@ impl<V: View> Element<V> for UniformList<V> {
) -> Self::PaintState {
let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
cx.scene.push_layer(Some(visible_bounds));
scene.push_layer(Some(visible_bounds));
cx.scene.push_mouse_region(
scene.push_mouse_region(
MouseRegion::new::<Self>(self.view_id, 0, visible_bounds).on_scroll({
let scroll_max = layout.scroll_max;
let state = self.state.clone();
move |MouseScrollWheel {
platform_event:
ScrollWheelEvent {
position, delta, ..
},
..
},
cx| {
move |event, _, cx| {
let ScrollWheelEvent {
position, delta, ..
} = event.platform_event;
if !Self::scroll(
state.clone(),
position,
@ -328,7 +317,7 @@ impl<V: View> Element<V> for UniformList<V> {
item_origin += vec2f(0.0, layout.item_height);
}
cx.scene.pop_layer();
scene.pop_layer();
}
fn rect_for_text_range(