WIP: Everything shredded

This commit is contained in:
Nathan Sobo 2023-04-10 16:10:32 -06:00
parent 7536645eea
commit 6638407ff9
26 changed files with 1136 additions and 1354 deletions

View file

@ -28,7 +28,8 @@ pub fn init(cx: &mut AppContext) {
cx.add_action(ContextMenu::cancel); cx.add_action(ContextMenu::cancel);
} }
type ContextMenuItemBuilder = Box<dyn Fn(&mut MouseState, &theme::ContextMenuItem) -> ElementBox>; type ContextMenuItemBuilder =
Box<dyn Fn(&mut MouseState, &theme::ContextMenuItem) -> ElementBox>;
pub enum ContextMenuItemLabel { pub enum ContextMenuItemLabel {
String(Cow<'static, str>), String(Cow<'static, str>),

View file

@ -4,7 +4,7 @@ use super::{
}; };
use crate::{Anchor, ExcerptId, ExcerptRange, ToPoint as _}; use crate::{Anchor, ExcerptId, ExcerptRange, ToPoint as _};
use collections::{Bound, HashMap, HashSet}; use collections::{Bound, HashMap, HashSet};
use gpui::{fonts::HighlightStyle, ElementBox, RenderContext}; use gpui::{fonts::HighlightStyle, RenderContext, ElementBox};
use language::{BufferSnapshot, Chunk, Patch, Point}; use language::{BufferSnapshot, Chunk, Patch, Point};
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{ use std::{

File diff suppressed because it is too large Load diff

View file

@ -23,8 +23,8 @@ use crate::{
platform, platform,
platform::{Appearance, Event, InputHandler, KeyDownEvent, Platform}, platform::{Appearance, Event, InputHandler, KeyDownEvent, Platform},
Action, AnyViewHandle, AppContext, Entity, FontCache, Handle, ModelContext, ModelHandle, Action, AnyViewHandle, AppContext, Entity, FontCache, Handle, ModelContext, ModelHandle,
ReadModelWith, ReadViewWith, RenderContext, Task, UpdateModel, UpdateView, View, ViewContext, ReadModelWith, ReadViewWith, Task, UpdateModel, UpdateView, View, ViewContext, ViewHandle,
ViewHandle, WeakHandle, WeakHandle,
}; };
use collections::BTreeMap; use collections::BTreeMap;

View file

@ -1,23 +1,20 @@
use crate::{ use crate::{
app::WindowInvalidation, app::WindowInvalidation,
elements::Element, elements::Element,
font_cache::FontCache,
geometry::rect::RectF, geometry::rect::RectF,
json::{self, ToJson}, json::{self, ToJson},
keymap_matcher::{Keystroke, MatchResult}, keymap_matcher::{Keystroke, MatchResult},
platform::{ platform::{
self, Appearance, CursorStyle, Event, FontSystem, MouseButton, MouseMovedEvent, self, Appearance, CursorStyle, Event, KeyDownEvent, KeyUpEvent, ModifiersChangedEvent,
PromptLevel, WindowBounds, MouseButton, MouseMovedEvent, PromptLevel, WindowBounds,
}, },
scene::{ scene::{
CursorRegion, MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseEvent, MouseHover, CursorRegion, MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseEvent, MouseHover,
MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut, Scene, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut, Scene,
}, },
text_layout::TextLayoutCache, text_layout::TextLayoutCache,
Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AnyWeakViewHandle, AppContext, Action, AnyView, AnyViewHandle, AnyWeakViewHandle, AppContext, ElementBox, MouseRegion,
AssetCache, ElementBox, Entity, ModelHandle, MouseRegion, MouseRegionId, MouseState, ParentId, MouseRegionId, RenderParams, SceneBuilder, View,
ReadModel, ReadView, RenderContext, RenderParams, SceneBuilder, UpgradeModelHandle,
UpgradeViewHandle, View, ViewHandle, WeakModelHandle, WeakViewHandle,
}; };
use anyhow::bail; use anyhow::bail;
use collections::{HashMap, HashSet}; use collections::{HashMap, HashSet};
@ -29,11 +26,7 @@ use sqlez::{
bindable::{Bind, Column, StaticColumnCount}, bindable::{Bind, Column, StaticColumnCount},
statement::Statement, statement::Statement,
}; };
use std::{ use std::ops::{Deref, DerefMut, Range};
marker::PhantomData,
ops::{Deref, DerefMut, Range},
sync::Arc,
};
use uuid::Uuid; use uuid::Uuid;
pub struct Window { pub struct Window {
@ -44,19 +37,17 @@ pub struct Window {
pub(crate) is_fullscreen: bool, pub(crate) is_fullscreen: bool,
pub(crate) invalidation: Option<WindowInvalidation>, pub(crate) invalidation: Option<WindowInvalidation>,
pub(crate) platform_window: Box<dyn platform::Window>, pub(crate) platform_window: Box<dyn platform::Window>,
pub(crate) rendered_views: HashMap<usize, ElementBox>, pub(crate) rendered_views: HashMap<usize, Box<dyn RenderedView>>,
titlebar_height: f32,
appearance: Appearance,
cursor_regions: Vec<CursorRegion>, cursor_regions: Vec<CursorRegion>,
mouse_regions: Vec<(MouseRegion, usize)>, mouse_regions: Vec<(MouseRegion, usize)>,
font_cache: Arc<FontCache>,
text_layout_cache: TextLayoutCache,
asset_cache: Arc<AssetCache>,
last_mouse_moved_event: Option<Event>, last_mouse_moved_event: Option<Event>,
hovered_region_ids: HashSet<MouseRegionId>, hovered_region_ids: HashSet<MouseRegionId>,
clicked_region_ids: HashSet<MouseRegionId>, clicked_region_ids: HashSet<MouseRegionId>,
clicked_button: Option<MouseButton>, clicked_button: Option<MouseButton>,
mouse_position: Vector2F, mouse_position: Vector2F,
titlebar_height: f32, text_layout_cache: TextLayoutCache,
appearance: Appearance,
} }
impl Window { impl Window {
@ -64,9 +55,6 @@ impl Window {
window_id: usize, window_id: usize,
root_view: AnyViewHandle, root_view: AnyViewHandle,
platform_window: Box<dyn platform::Window>, platform_window: Box<dyn platform::Window>,
font_cache: Arc<FontCache>,
text_layout_cache: TextLayoutCache,
asset_cache: Arc<AssetCache>,
cx: &mut AppContext, cx: &mut AppContext,
) -> Self { ) -> Self {
let focused_view_id = Some(root_view.id()); let focused_view_id = Some(root_view.id());
@ -83,9 +71,7 @@ impl Window {
rendered_views: cx.render_views(window_id, titlebar_height, appearance), rendered_views: cx.render_views(window_id, titlebar_height, appearance),
cursor_regions: Default::default(), cursor_regions: Default::default(),
mouse_regions: Default::default(), mouse_regions: Default::default(),
font_cache, text_layout_cache: TextLayoutCache::new(cx.font_system.clone()),
text_layout_cache,
asset_cache,
last_mouse_moved_event: None, last_mouse_moved_event: None,
hovered_region_ids: Default::default(), hovered_region_ids: Default::default(),
clicked_region_ids: Default::default(), clicked_region_ids: Default::default(),
@ -100,7 +86,7 @@ impl Window {
pub struct WindowContext<'a: 'b, 'b> { pub struct WindowContext<'a: 'b, 'b> {
app_context: &'a mut AppContext, app_context: &'a mut AppContext,
pub(crate) window: &'b mut Window, // TODO: make this private? pub(crate) window: &'b mut Window, // TODO: make this private?
window_id: usize, pub(crate) window_id: usize,
} }
impl Deref for WindowContext<'_, '_> { impl Deref for WindowContext<'_, '_> {
@ -126,9 +112,19 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
} }
} }
pub fn update_any_view<F, T>(&mut self, view_id: usize, f: F) -> Option<T>
where
F: FnOnce(&mut dyn AnyView, &mut Self) -> T,
{
let view = self.views.remove(&(self.window_id, view_id))?;
let result = f(view.as_any_mut(), self);
self.views.insert((self.window_id, view_id), view);
Some(result)
}
pub fn dispatch_keystroke(&mut self, keystroke: &Keystroke) -> bool { pub fn dispatch_keystroke(&mut self, keystroke: &Keystroke) -> bool {
let window_id = self.window_id; let window_id = self.window_id;
if let Some(focused_view_id) = self.focused_view_id(window_id) { if let Some(focused_view_id) = self.focused_view_id() {
let dispatch_path = self let dispatch_path = self
.ancestors(window_id, focused_view_id) .ancestors(window_id, focused_view_id)
.filter_map(|view_id| { .filter_map(|view_id| {
@ -511,20 +507,71 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
any_event_handled any_event_handled
} }
pub fn build_event_context<'c>( pub fn dispatch_key_down(&mut self, window_id: usize, event: &KeyDownEvent) -> bool {
&'c mut self, if let Some(focused_view_id) = self.window.focused_view_id {
notified_views: &'c mut HashSet<usize>, for view_id in self
) -> EventContext<'c> { .ancestors(window_id, focused_view_id)
EventContext { .collect::<Vec<_>>()
font_cache: &self.window.font_cache, {
text_layout_cache: &self.window.text_layout_cache, if let Some(mut view) = self.views.remove(&(window_id, view_id)) {
view_stack: Default::default(), let handled = view.key_down(event, self, view_id);
notified_views, self.views.insert((window_id, view_id), view);
notify_count: 0, if handled {
handled: false, return true;
window_id: self.window_id,
app: self,
} }
} else {
log::error!("view {} does not exist", view_id)
}
}
}
false
}
pub fn dispatch_key_up(&mut self, window_id: usize, event: &KeyUpEvent) -> bool {
if let Some(focused_view_id) = self.window.fo {
for view_id in self
.ancestors(window_id, focused_view_id)
.collect::<Vec<_>>()
{
if let Some(mut view) = self.views.remove(&(window_id, view_id)) {
let handled = view.key_up(event, self, view_id);
self.views.insert((window_id, view_id), view);
if handled {
return true;
}
} else {
log::error!("view {} does not exist", view_id)
}
}
}
false
}
pub fn dispatch_modifiers_changed(
&mut self,
window_id: usize,
event: &ModifiersChangedEvent,
) -> bool {
if let Some(focused_view_id) = self.window.focused_view_id {
for view_id in self
.ancestors(window_id, focused_view_id)
.collect::<Vec<_>>()
{
if let Some(mut view) = self.views.remove(&(window_id, view_id)) {
let handled = view.modifiers_changed(event, self, view_id);
self.views.insert((window_id, view_id), view);
if handled {
return true;
}
} else {
log::error!("view {} does not exist", view_id)
}
}
}
false
} }
pub fn invalidate(&mut self, invalidation: &mut WindowInvalidation, appearance: Appearance) { pub fn invalidate(&mut self, invalidation: &mut WindowInvalidation, appearance: Appearance) {
@ -593,113 +640,56 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
} }
} }
pub fn build_scene(&mut self, refreshing: bool) -> Scene { pub fn build_scene(&mut self) -> Scene {
let window_size = self.window.platform_window.content_size(); let window_size = self.window.platform_window.content_size();
let scale_factor = self.window.platform_window.scale_factor(); let scale_factor = self.window.platform_window.scale_factor();
let mut scene_builder = SceneBuilder::new(scale_factor); let root_view_id = self.window.root_view.id();
let rendered_root = self.window.rendered_views.remove(&root_view_id).unwrap();
rendered_root.layout(root_view_id, SizeConstraint::strict(window_size), self);
if let Some(root_view_id) = self.root_view_id(self.window_id) { let mut scene_builder = SceneBuilder::new(scale_factor);
self.layout(window_size, refreshing, self); let paint_bounds = RectF::from_points(Vector2F::zero(), window_size);
let mut paint_cx = self.build_paint_context(&mut scene_builder, window_size); rendered_root.paint(
paint_cx.paint(
root_view_id, root_view_id,
Vector2F::zero(), &mut scene_builder,
RectF::new(Vector2F::zero(), window_size), paint_bounds,
paint_bounds,
self,
); );
self.window.text_layout_cache.finish_frame(); self.window.text_layout_cache.finish_frame();
let scene = scene_builder.build(); let scene = scene_builder.build();
self.window.cursor_regions = scene.cursor_regions(); self.window.cursor_regions = scene.cursor_regions();
self.window.mouse_regions = scene.mouse_regions(); self.window.mouse_regions = scene.mouse_regions();
// window.is_topmost for the mouse moved event's postion? if self.window_is_active() {
if self.window_is_active(self.window_id) {
if let Some(event) = self.window.last_mouse_moved_event.clone() { if let Some(event) = self.window.last_mouse_moved_event.clone() {
self.dispatch_event(event, true); self.dispatch_event(event, true);
} }
} }
scene scene
} else {
log::error!("could not find root_view_id for window {}", self.window_id);
scene_builder.build()
}
}
fn layout(&mut self, window_size: Vector2F, refreshing: bool, cx: &mut AppContext) {
if let Some(root_view_id) = cx.root_view_id(self.window_id) {
self.build_layout_context(window_size, refreshing, cx)
.layout(root_view_id, SizeConstraint::strict(window_size));
}
}
pub fn build_layout_context<'c>(
&'c mut self,
window_size: Vector2F,
refreshing: bool,
cx: &'c mut AppContext,
) -> LayoutContext<'c> {
LayoutContext {
window_id: self.window_id,
rendered_views: &mut self.window.rendered_views,
font_cache: &self.font_cache,
font_system: cx.platform().fonts(),
text_layout_cache: &self.window.text_layout_cache,
asset_cache: &self.window.asset_cache,
view_stack: Vec::new(),
refreshing,
hovered_region_ids: self.window.hovered_region_ids.clone(),
clicked_region_ids: self
.window
.clicked_button
.map(|button| (self.window.clicked_region_ids.clone(), button)),
titlebar_height: self.window.titlebar_height,
appearance: self.window.appearance,
window_size,
app: cx,
}
}
pub fn build_paint_context<'c>(
&'c mut self,
scene: &'c mut SceneBuilder,
window_size: Vector2F,
) -> PaintContext {
PaintContext {
scene,
window_size,
font_cache: &self.font_cache,
text_layout_cache: &self.window.text_layout_cache,
rendered_views: &mut self.window.rendered_views,
view_stack: Vec::new(),
app: self,
}
} }
pub fn rect_for_text_range(&self, range_utf16: Range<usize>) -> Option<RectF> { pub fn rect_for_text_range(&self, range_utf16: Range<usize>) -> Option<RectF> {
self.focused_view_id(self.window_id).and_then(|view_id| { todo!()
let cx = MeasurementContext {
app: self,
rendered_views: &self.window.rendered_views,
window_id: self.window_id,
};
cx.rect_for_text_range(view_id, range_utf16)
})
} }
pub fn debug_elements(&self) -> Option<json::Value> { pub fn debug_elements(&self) -> Option<json::Value> {
let view = self.root_view(self.window_id)?; todo!()
Some(json!({ // let view = self.root_view()?;
"root_view": view.debug_json(self), // Some(json!({
"root_element": self.window.rendered_views.get(&view.id()) // "root_view": view.debug_json(self),
.map(|root_element| { // "root_element": self.window.rendered_views.get(&view.id())
root_element.debug(&DebugContext { // .map(|root_element| {
rendered_views: &self.window.rendered_views, // root_element.debug(&DebugContext {
font_cache: &self.window.font_cache, // rendered_views: &self.window.rendered_views,
app: self, // font_cache: &self.window.font_cache,
}) // app: self,
}) // })
})) // })
// }))
} }
pub fn set_window_title(&mut self, title: &str) { pub fn set_window_title(&mut self, title: &str) {
@ -720,6 +710,26 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
self.window.platform_window.activate(); self.window.platform_window.activate();
} }
pub fn window_is_active(&self) -> bool {
self.window.is_active
}
pub fn window_is_fullscreen(&self) -> bool {
self.window.is_fullscreen
}
pub fn root_view(&self, window_id: usize) -> &AnyViewHandle {
&self.window.root_view
}
pub fn root_view_id(&self) -> usize {
self.window.root_view.id()
}
pub fn focused_view_id(&self) -> Option<usize> {
self.window.focused_view_id
}
pub fn window_bounds(&self) -> WindowBounds { pub fn window_bounds(&self) -> WindowBounds {
self.window.platform_window.bounds() self.window.platform_window.bounds()
} }
@ -754,330 +764,48 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
} }
} }
pub struct LayoutContext<'a> { pub trait RenderedView {
window_id: usize, fn layout(
rendered_views: &'a mut HashMap<usize, ElementBox>, &self,
view_stack: Vec<usize>, view_id: usize,
pub font_cache: &'a Arc<FontCache>, constraint: SizeConstraint,
pub font_system: Arc<dyn FontSystem>, cx: &mut WindowContext,
pub text_layout_cache: &'a TextLayoutCache, ) -> Vector2F;
pub asset_cache: &'a AssetCache, fn paint(
pub app: &'a mut AppContext, &self,
pub refreshing: bool, view_id: usize,
pub window_size: Vector2F, scene: &mut SceneBuilder,
titlebar_height: f32, bounds: RectF,
appearance: Appearance, visible_bounds: RectF,
hovered_region_ids: HashSet<MouseRegionId>, cx: &mut WindowContext,
clicked_region_ids: Option<(HashSet<MouseRegionId>, MouseButton)>, );
} }
impl<'a> LayoutContext<'a> { impl<V: View> RenderedView for ElementBox<V> {
pub fn mouse_state<Tag: 'static>(&self, region_id: usize) -> MouseState { fn layout(
let view_id = self.view_stack.last().unwrap(); &self,
view_id: usize,
let region_id = MouseRegionId::new::<Tag>(*view_id, region_id); constraint: SizeConstraint,
MouseState { cx: &mut WindowContext,
hovered: self.hovered_region_ids.contains(&region_id), ) -> Vector2F {
clicked: self.clicked_region_ids.as_ref().and_then(|(ids, button)| { cx.update_view_for_id(view_id, |view, cx| self.layout(view, constraint, cx))
if ids.contains(&region_id) { .unwrap()
Some(*button)
} else {
None
}
}),
accessed_hovered: false,
accessed_clicked: false,
}
} }
fn layout(&mut self, view_id: usize, constraint: SizeConstraint) -> Vector2F { fn paint(
let print_error = |view_id| { &self,
format!( view_id: usize,
"{} with id {}", scene: &mut SceneBuilder,
self.app.name_for_view(self.window_id, view_id).unwrap(), bounds: RectF,
view_id, visible_bounds: RectF,
) cx: &mut WindowContext,
};
match (
self.view_stack.last(),
self.app.parents.get(&(self.window_id, view_id)),
) { ) {
(Some(layout_parent), Some(ParentId::View(app_parent))) => { cx.update_view_for_id(view_id, |view, cx| {
if layout_parent != app_parent { self.paint(view, scene, bounds, visible_bounds, cx)
panic!(
"View {} was laid out with parent {} when it was constructed with parent {}",
print_error(view_id),
print_error(*layout_parent),
print_error(*app_parent))
}
}
(None, Some(ParentId::View(app_parent))) => panic!(
"View {} was laid out without a parent when it was constructed with parent {}",
print_error(view_id),
print_error(*app_parent)
),
(Some(layout_parent), Some(ParentId::Root)) => panic!(
"View {} was laid out with parent {} when it was constructed as a window root",
print_error(view_id),
print_error(*layout_parent),
),
(_, None) => panic!(
"View {} did not have a registered parent in the app context",
print_error(view_id),
),
_ => {}
}
self.view_stack.push(view_id);
let mut rendered_view = self.rendered_views.remove(&view_id).unwrap();
let size = rendered_view.layout(constraint, self);
self.rendered_views.insert(view_id, rendered_view);
self.view_stack.pop();
size
}
pub fn render<F, V, T>(&mut self, handle: &ViewHandle<V>, f: F) -> T
where
F: FnOnce(&mut V, &mut RenderContext<V>) -> T,
V: View,
{
handle.update(self.app, |view, cx| {
let mut render_cx = RenderContext {
app: cx,
window_id: handle.window_id(),
view_id: handle.id(),
view_type: PhantomData,
titlebar_height: self.titlebar_height,
hovered_region_ids: self.hovered_region_ids.clone(),
clicked_region_ids: self.clicked_region_ids.clone(),
refreshing: self.refreshing,
appearance: self.appearance,
};
f(view, &mut render_cx)
}) })
} }
} }
impl<'a> Deref for LayoutContext<'a> {
type Target = AppContext;
fn deref(&self) -> &Self::Target {
self.app
}
}
impl<'a> DerefMut for LayoutContext<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.app
}
}
impl<'a> ReadView for LayoutContext<'a> {
fn read_view<T: View>(&self, handle: &ViewHandle<T>) -> &T {
self.app.read_view(handle)
}
}
impl<'a> ReadModel for LayoutContext<'a> {
fn read_model<T: Entity>(&self, handle: &ModelHandle<T>) -> &T {
self.app.read_model(handle)
}
}
impl<'a> UpgradeModelHandle for LayoutContext<'a> {
fn upgrade_model_handle<T: Entity>(
&self,
handle: &WeakModelHandle<T>,
) -> Option<ModelHandle<T>> {
self.app.upgrade_model_handle(handle)
}
fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
self.app.model_handle_is_upgradable(handle)
}
fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
self.app.upgrade_any_model_handle(handle)
}
}
impl<'a> UpgradeViewHandle for LayoutContext<'a> {
fn upgrade_view_handle<T: View>(&self, handle: &WeakViewHandle<T>) -> Option<ViewHandle<T>> {
self.app.upgrade_view_handle(handle)
}
fn upgrade_any_view_handle(&self, handle: &crate::AnyWeakViewHandle) -> Option<AnyViewHandle> {
self.app.upgrade_any_view_handle(handle)
}
}
pub struct PaintContext<'a> {
rendered_views: &'a mut HashMap<usize, ElementBox>,
view_stack: Vec<usize>,
pub window_size: Vector2F,
pub scene: &'a mut SceneBuilder,
pub font_cache: &'a FontCache,
pub text_layout_cache: &'a TextLayoutCache,
pub app: &'a AppContext,
}
impl<'a> PaintContext<'a> {
fn paint(&mut self, view_id: usize, origin: Vector2F, visible_bounds: RectF) {
if let Some(mut tree) = self.rendered_views.remove(&view_id) {
self.view_stack.push(view_id);
tree.paint(origin, visible_bounds, self);
self.rendered_views.insert(view_id, tree);
self.view_stack.pop();
}
}
#[inline]
pub fn paint_stacking_context<F>(
&mut self,
clip_bounds: Option<RectF>,
z_index: Option<usize>,
f: F,
) where
F: FnOnce(&mut Self),
{
self.scene.push_stacking_context(clip_bounds, z_index);
f(self);
self.scene.pop_stacking_context();
}
#[inline]
pub fn paint_layer<F>(&mut self, clip_bounds: Option<RectF>, f: F)
where
F: FnOnce(&mut Self),
{
self.scene.push_layer(clip_bounds);
f(self);
self.scene.pop_layer();
}
pub fn current_view_id(&self) -> usize {
*self.view_stack.last().unwrap()
}
}
impl<'a> Deref for PaintContext<'a> {
type Target = AppContext;
fn deref(&self) -> &Self::Target {
self.app
}
}
pub struct EventContext<'a> {
pub font_cache: &'a FontCache,
pub text_layout_cache: &'a TextLayoutCache,
pub app: &'a mut AppContext,
pub window_id: usize,
pub notify_count: usize,
view_stack: Vec<usize>,
handled: bool,
notified_views: &'a mut HashSet<usize>,
}
impl<'a> EventContext<'a> {
fn with_current_view<F, T>(&mut self, view_id: usize, f: F) -> T
where
F: FnOnce(&mut Self) -> T,
{
self.view_stack.push(view_id);
let result = f(self);
self.view_stack.pop();
result
}
pub fn window_id(&self) -> usize {
self.window_id
}
pub fn view_id(&self) -> Option<usize> {
self.view_stack.last().copied()
}
pub fn is_parent_view_focused(&self) -> bool {
if let Some(parent_view_id) = self.view_stack.last() {
self.app.focused_view_id(self.window_id) == Some(*parent_view_id)
} else {
false
}
}
pub fn focus_parent_view(&mut self) {
if let Some(parent_view_id) = self.view_stack.last() {
self.app.focus(self.window_id, Some(*parent_view_id))
}
}
pub fn dispatch_any_action(&mut self, action: Box<dyn Action>) {
self.app
.dispatch_any_action_at(self.window_id, *self.view_stack.last().unwrap(), action)
}
pub fn dispatch_action<A: Action>(&mut self, action: A) {
self.dispatch_any_action(Box::new(action));
}
pub fn notify(&mut self) {
self.notify_count += 1;
if let Some(view_id) = self.view_stack.last() {
self.notified_views.insert(*view_id);
}
}
pub fn notify_count(&self) -> usize {
self.notify_count
}
pub fn propagate_event(&mut self) {
self.handled = false;
}
}
impl<'a> Deref for EventContext<'a> {
type Target = AppContext;
fn deref(&self) -> &Self::Target {
self.app
}
}
impl<'a> DerefMut for EventContext<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.app
}
}
pub struct MeasurementContext<'a> {
app: &'a AppContext,
rendered_views: &'a HashMap<usize, ElementBox>,
pub window_id: usize,
}
impl<'a> Deref for MeasurementContext<'a> {
type Target = AppContext;
fn deref(&self) -> &Self::Target {
self.app
}
}
impl<'a> MeasurementContext<'a> {
fn rect_for_text_range(&self, view_id: usize, range_utf16: Range<usize>) -> Option<RectF> {
let element = self.rendered_views.get(&view_id)?;
element.rect_for_text_range(range_utf16, self)
}
}
pub struct DebugContext<'a> {
rendered_views: &'a HashMap<usize, ElementBox>,
pub font_cache: &'a FontCache,
pub app: &'a AppContext,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum Axis { pub enum Axis {
#[default] #[default]
@ -1235,88 +963,88 @@ impl ChildView {
} }
} }
impl Element for ChildView { // impl Element for ChildView {
type LayoutState = bool; // type LayoutState = bool;
type PaintState = (); // type PaintState = ();
fn layout( // fn layout(
&mut self, // &mut self,
constraint: SizeConstraint, // constraint: SizeConstraint,
cx: &mut LayoutContext, // cx: &mut LayoutContext,
) -> (Vector2F, Self::LayoutState) { // ) -> (Vector2F, Self::LayoutState) {
if cx.rendered_views.contains_key(&self.view.id()) { // if cx.rendered_views.contains_key(&self.view.id()) {
let size = cx.layout(self.view.id(), constraint); // let size = cx.layout(self.view.id(), constraint);
(size, true) // (size, true)
} else { // } else {
log::error!( // log::error!(
"layout called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})", // "layout called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})",
self.view.id(), // self.view.id(),
self.view_name // self.view_name
); // );
(Vector2F::zero(), false) // (Vector2F::zero(), false)
} // }
} // }
fn paint( // fn paint(
&mut self, // &mut self,
bounds: RectF, // bounds: RectF,
visible_bounds: RectF, // visible_bounds: RectF,
view_is_valid: &mut Self::LayoutState, // view_is_valid: &mut Self::LayoutState,
cx: &mut PaintContext, // cx: &mut PaintContext,
) { // ) {
if *view_is_valid { // if *view_is_valid {
cx.paint(self.view.id(), bounds.origin(), visible_bounds); // cx.paint(self.view.id(), bounds.origin(), visible_bounds);
} else { // } else {
log::error!( // log::error!(
"paint called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})", // "paint called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})",
self.view.id(), // self.view.id(),
self.view_name // self.view_name
); // );
} // }
} // }
fn rect_for_text_range( // fn rect_for_text_range(
&self, // &self,
range_utf16: Range<usize>, // range_utf16: Range<usize>,
_: RectF, // _: RectF,
_: RectF, // _: RectF,
view_is_valid: &Self::LayoutState, // view_is_valid: &Self::LayoutState,
_: &Self::PaintState, // _: &Self::PaintState,
cx: &MeasurementContext, // cx: &MeasurementContext,
) -> Option<RectF> { // ) -> Option<RectF> {
if *view_is_valid { // if *view_is_valid {
cx.rect_for_text_range(self.view.id(), range_utf16) // cx.rect_for_text_range(self.view.id(), range_utf16)
} else { // } else {
log::error!( // log::error!(
"rect_for_text_range called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})", // "rect_for_text_range called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})",
self.view.id(), // self.view.id(),
self.view_name // self.view_name
); // );
None // None
} // }
} // }
fn debug( // fn debug(
&self, // &self,
bounds: RectF, // bounds: RectF,
_: &Self::LayoutState, // _: &Self::LayoutState,
_: &Self::PaintState, // _: &Self::PaintState,
cx: &DebugContext, // cx: &DebugContext,
) -> serde_json::Value { // ) -> serde_json::Value {
json!({ // json!({
"type": "ChildView", // "type": "ChildView",
"view_id": self.view.id(), // "view_id": self.view.id(),
"bounds": bounds.to_json(), // "bounds": bounds.to_json(),
"view": if let Some(view) = self.view.upgrade(cx.app) { // "view": if let Some(view) = self.view.upgrade(cx.app) {
view.debug_json(cx.app) // view.debug_json(cx.app)
} else { // } else {
json!(null) // json!(null)
}, // },
"child": if let Some(view) = cx.rendered_views.get(&self.view.id()) { // "child": if let Some(view) = cx.rendered_views.get(&self.view.id()) {
view.debug(cx) // view.debug(cx)
} else { // } else {
json!(null) // json!(null)
} // }
}) // })
} // }
} // }

View file

@ -32,42 +32,52 @@ use crate::{
rect::RectF, rect::RectF,
vector::{vec2f, Vector2F}, vector::{vec2f, Vector2F},
}, },
json, Action, DebugContext, EventContext, LayoutContext, PaintContext, RenderContext, json, Action, RenderContext, SceneBuilder, SizeConstraint, View, ViewContext,
SizeConstraint, View,
}; };
use core::panic; use core::panic;
use json::ToJson; use json::ToJson;
use std::{ use std::{any::Any, borrow::Cow, cell::RefCell, marker::PhantomData, mem, ops::Range};
any::Any,
borrow::Cow, trait AnyElement<V: View> {
cell::RefCell, fn layout(
mem, &mut self,
ops::{Deref, DerefMut, Range}, view: &mut V,
rc::Rc, constraint: SizeConstraint,
}; cx: &mut ViewContext<V>,
) -> Vector2F;
fn paint(
&mut self,
view: &mut V,
scene: &mut SceneBuilder,
origin: Vector2F,
visible_bounds: RectF,
cx: &mut ViewContext<V>,
);
trait AnyElement {
fn layout(&mut self, constraint: SizeConstraint, cx: &mut LayoutContext) -> Vector2F;
fn paint(&mut self, origin: Vector2F, visible_bounds: RectF, cx: &mut PaintContext);
fn rect_for_text_range( fn rect_for_text_range(
&self, &self,
view: &V,
range_utf16: Range<usize>, range_utf16: Range<usize>,
cx: &MeasurementContext, cx: &ViewContext<V>,
) -> Option<RectF>; ) -> Option<RectF>;
fn debug(&self, cx: &DebugContext) -> serde_json::Value;
fn debug(&self, view: &V, cx: &mut ViewContext<V>) -> serde_json::Value;
fn size(&self) -> Vector2F; fn size(&self) -> Vector2F;
fn metadata(&self) -> Option<&dyn Any>; fn metadata(&self) -> Option<&dyn Any>;
} }
pub trait Element { pub trait Element<V: View> {
type LayoutState; type LayoutState;
type PaintState; type PaintState;
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
cx: &mut LayoutContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState); ) -> (Vector2F, Self::LayoutState);
fn paint( fn paint(
@ -75,7 +85,8 @@ pub trait Element {
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
layout: &mut Self::LayoutState, layout: &mut Self::LayoutState,
cx: &mut PaintContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState; ) -> Self::PaintState;
fn rect_for_text_range( fn rect_for_text_range(
@ -85,7 +96,8 @@ pub trait Element {
visible_bounds: RectF, visible_bounds: RectF,
layout: &Self::LayoutState, layout: &Self::LayoutState,
paint: &Self::PaintState, paint: &Self::PaintState,
cx: &MeasurementContext, view: &V,
cx: &ViewContext<V>,
) -> Option<RectF>; ) -> Option<RectF>;
fn metadata(&self) -> Option<&dyn Any> { fn metadata(&self) -> Option<&dyn Any> {
@ -97,27 +109,28 @@ pub trait Element {
bounds: RectF, bounds: RectF,
layout: &Self::LayoutState, layout: &Self::LayoutState,
paint: &Self::PaintState, paint: &Self::PaintState,
cx: &DebugContext, view: &V,
cx: &ViewContext<V>,
) -> serde_json::Value; ) -> serde_json::Value;
fn boxed(self) -> ElementBox fn boxed(self) -> ElementBox<V>
where where
Self: 'static + Sized, Self: 'static + Sized,
{ {
ElementBox(ElementRc { ElementBox {
element: RefCell::new(Lifecycle::Init { element: self }),
name: None, name: None,
element: Rc::new(RefCell::new(Lifecycle::Init { element: self })), }
})
} }
fn named(self, name: impl Into<Cow<'static, str>>) -> ElementBox fn named(self, name: impl Into<Cow<'static, str>>) -> ElementBox<V>
where where
Self: 'static + Sized, Self: 'static + Sized,
{ {
ElementBox(ElementRc { ElementBox {
element: RefCell::new(Lifecycle::Init { element: self }),
name: Some(name.into()), name: Some(name.into()),
element: Rc::new(RefCell::new(Lifecycle::Init { element: self })), }
})
} }
fn constrained(self) -> ConstrainedBox fn constrained(self) -> ConstrainedBox
@ -205,44 +218,41 @@ pub trait Element {
} }
} }
pub enum Lifecycle<T: Element> { pub enum Lifecycle<V: View, E: Element<V>> {
Empty, Empty,
Init { Init {
element: T, element: E,
}, },
PostLayout { PostLayout {
element: T, element: E,
constraint: SizeConstraint, constraint: SizeConstraint,
size: Vector2F, size: Vector2F,
layout: T::LayoutState, layout: E::LayoutState,
}, },
PostPaint { PostPaint {
element: T, element: E,
constraint: SizeConstraint, constraint: SizeConstraint,
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
layout: T::LayoutState, layout: E::LayoutState,
paint: T::PaintState, paint: E::PaintState,
}, },
} }
pub struct ElementBox(ElementRc); impl<V: View, E: Element<V>> AnyElement<V> for Lifecycle<V, E> {
fn layout(
#[derive(Clone)] &mut self,
pub struct ElementRc { view: &mut V,
name: Option<Cow<'static, str>>, constraint: SizeConstraint,
element: Rc<RefCell<dyn AnyElement>>, cx: &mut ViewContext<V>,
} ) -> Vector2F {
impl<T: Element> AnyElement for Lifecycle<T> {
fn layout(&mut self, constraint: SizeConstraint, cx: &mut LayoutContext) -> Vector2F {
let result; let result;
*self = match mem::take(self) { *self = match mem::take(self) {
Lifecycle::Empty => unreachable!(), Lifecycle::Empty => unreachable!(),
Lifecycle::Init { mut element } Lifecycle::Init { mut element }
| Lifecycle::PostLayout { mut element, .. } | Lifecycle::PostLayout { mut element, .. }
| Lifecycle::PostPaint { mut element, .. } => { | Lifecycle::PostPaint { mut element, .. } => {
let (size, layout) = element.layout(constraint, cx); let (size, layout) = element.layout(view, constraint, cx);
debug_assert!(size.x().is_finite()); debug_assert!(size.x().is_finite());
debug_assert!(size.y().is_finite()); debug_assert!(size.y().is_finite());
@ -258,7 +268,13 @@ impl<T: Element> AnyElement for Lifecycle<T> {
result result
} }
fn paint(&mut self, origin: Vector2F, visible_bounds: RectF, cx: &mut PaintContext) { fn paint(
&mut self,
view: &mut V,
origin: Vector2F,
visible_bounds: RectF,
cx: &mut ViewContext<V>,
) {
*self = match mem::take(self) { *self = match mem::take(self) {
Lifecycle::PostLayout { Lifecycle::PostLayout {
mut element, mut element,
@ -267,7 +283,7 @@ impl<T: Element> AnyElement for Lifecycle<T> {
mut layout, mut layout,
} => { } => {
let bounds = RectF::new(origin, size); let bounds = RectF::new(origin, size);
let paint = element.paint(bounds, visible_bounds, &mut layout, cx); let paint = element.paint(view, bounds, visible_bounds, &mut layout, cx);
Lifecycle::PostPaint { Lifecycle::PostPaint {
element, element,
constraint, constraint,
@ -285,7 +301,7 @@ impl<T: Element> AnyElement for Lifecycle<T> {
.. ..
} => { } => {
let bounds = RectF::new(origin, bounds.size()); let bounds = RectF::new(origin, bounds.size());
let paint = element.paint(bounds, visible_bounds, &mut layout, cx); let paint = element.paint(view, bounds, visible_bounds, &mut layout, cx);
Lifecycle::PostPaint { Lifecycle::PostPaint {
element, element,
constraint, constraint,
@ -304,8 +320,9 @@ impl<T: Element> AnyElement for Lifecycle<T> {
fn rect_for_text_range( fn rect_for_text_range(
&self, &self,
view: &V,
range_utf16: Range<usize>, range_utf16: Range<usize>,
cx: &MeasurementContext, cx: &mut ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
if let Lifecycle::PostPaint { if let Lifecycle::PostPaint {
element, element,
@ -316,7 +333,15 @@ impl<T: Element> AnyElement for Lifecycle<T> {
.. ..
} = self } = self
{ {
element.rect_for_text_range(range_utf16, *bounds, *visible_bounds, layout, paint, cx) element.rect_for_text_range(
view,
range_utf16,
*bounds,
*visible_bounds,
layout,
paint,
cx,
)
} else { } else {
None None
} }
@ -339,7 +364,7 @@ impl<T: Element> AnyElement for Lifecycle<T> {
} }
} }
fn debug(&self, cx: &DebugContext) -> serde_json::Value { fn debug(&self, view: &V, cx: &ViewContext<V>) -> serde_json::Value {
match self { match self {
Lifecycle::PostPaint { Lifecycle::PostPaint {
element, element,
@ -349,7 +374,7 @@ impl<T: Element> AnyElement for Lifecycle<T> {
layout, layout,
paint, paint,
} => { } => {
let mut value = element.debug(*bounds, layout, paint, cx); let mut value = element.debug(view, *bounds, layout, paint, cx);
if let json::Value::Object(map) = &mut value { if let json::Value::Object(map) = &mut value {
let mut new_map: crate::json::Map<String, serde_json::Value> = let mut new_map: crate::json::Map<String, serde_json::Value> =
Default::default(); Default::default();
@ -371,72 +396,67 @@ impl<T: Element> AnyElement for Lifecycle<T> {
} }
} }
impl<T: Element> Default for Lifecycle<T> { impl<V: View, E: Element<V>> Default for Lifecycle<V, E> {
fn default() -> Self { fn default() -> Self {
Self::Empty Self::Empty
} }
} }
impl ElementBox { pub struct ElementBox<V: View> {
element: RefCell<dyn AnyElement<V>>,
view_type: PhantomData<V>,
name: Option<Cow<'static, str>>,
}
impl<V: View> ElementBox<V> {
pub fn name(&self) -> Option<&str> { pub fn name(&self) -> Option<&str> {
self.0.name.as_deref() self.0.name.as_deref()
} }
pub fn metadata<T: 'static>(&self) -> Option<&T> { pub fn metadata<T: 'static>(&self) -> Option<&T> {
let element = unsafe { &*self.0.element.as_ptr() }; // let element = unsafe { &*self.0.element.as_ptr() };
element.metadata().and_then(|m| m.downcast_ref()) // element.metadata().and_then(|m| m.downcast_ref())
}
} }
impl Clone for ElementBox { pub fn layout(
fn clone(&self) -> Self { &self,
ElementBox(self.0.clone()) view: &mut V,
} constraint: SizeConstraint,
cx: &mut ViewContext<V>,
) -> Vector2F {
self.element.borrow_mut().layout(view, constraint, cx)
} }
impl From<ElementBox> for ElementRc { pub fn paint(
fn from(val: ElementBox) -> Self { &self,
val.0 view: &mut V,
} scene: &mut SceneBuilder,
} origin: Vector2F,
visible_bounds: RectF,
impl Deref for ElementBox { cx: &mut ViewContext<V>,
type Target = ElementRc; ) {
self.element
fn deref(&self) -> &Self::Target { .borrow_mut()
&self.0 .paint(view, scene, origin, visible_bounds, cx);
}
}
impl DerefMut for ElementBox {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl ElementRc {
pub fn layout(&mut self, constraint: SizeConstraint, cx: &mut LayoutContext) -> Vector2F {
self.element.borrow_mut().layout(constraint, cx)
}
pub fn paint(&mut self, origin: Vector2F, visible_bounds: RectF, cx: &mut PaintContext) {
self.element.borrow_mut().paint(origin, visible_bounds, cx);
} }
pub fn rect_for_text_range( pub fn rect_for_text_range(
&self, &self,
view: &V,
range_utf16: Range<usize>, range_utf16: Range<usize>,
cx: &MeasurementContext, cx: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
self.element.borrow().rect_for_text_range(range_utf16, cx) self.element
.borrow()
.rect_for_text_range(view, range_utf16, cx)
} }
pub fn size(&self) -> Vector2F { pub fn size(&self) -> Vector2F {
self.element.borrow().size() self.element.borrow().size()
} }
pub fn debug(&self, cx: &DebugContext) -> json::Value { pub fn debug(&self, view: &V, cx: &ViewContext<V>) -> json::Value {
let mut value = self.element.borrow().debug(cx); let mut value = self.element.borrow().debug(view, cx);
if let Some(name) = &self.name { if let Some(name) = &self.name {
if let json::Value::Object(map) = &mut value { if let json::Value::Object(map) = &mut value {
@ -460,26 +480,26 @@ impl ElementRc {
} }
} }
pub trait ParentElement<'a>: Extend<ElementBox> + Sized { pub trait ParentElement<'a, V: View>: Extend<ElementBox<V>> + Sized {
fn add_children(&mut self, children: impl IntoIterator<Item = ElementBox>) { fn add_children(&mut self, children: impl IntoIterator<Item = ElementBox<V>>) {
self.extend(children); self.extend(children);
} }
fn add_child(&mut self, child: ElementBox) { fn add_child(&mut self, child: ElementBox<V>) {
self.add_children(Some(child)); self.add_children(Some(child));
} }
fn with_children(mut self, children: impl IntoIterator<Item = ElementBox>) -> Self { fn with_children(mut self, children: impl IntoIterator<Item = ElementBox<V>>) -> Self {
self.add_children(children); self.add_children(children);
self self
} }
fn with_child(self, child: ElementBox) -> Self { fn with_child(self, child: ElementBox<V>) -> Self {
self.with_children(Some(child)) self.with_children(Some(child))
} }
} }
impl<'a, T> ParentElement<'a> for T where T: Extend<ElementBox> {} impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<ElementBox<V>> {}
pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F { pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
if max_size.x().is_infinite() && max_size.y().is_infinite() { if max_size.x().is_infinite() && max_size.y().is_infinite() {

View file

@ -1,20 +1,18 @@
use crate::{ use crate::{
geometry::{rect::RectF, vector::Vector2F}, geometry::{rect::RectF, vector::Vector2F},
json, json, Element, ElementBox, SceneBuilder, SizeConstraint, View, ViewContext,
window::MeasurementContext,
DebugContext, Element, ElementBox, LayoutContext, PaintContext, SizeConstraint,
}; };
use json::ToJson; use json::ToJson;
use serde_json::json; use serde_json::json;
pub struct Align { pub struct Align<V: View> {
child: ElementBox, child: ElementBox<V>,
alignment: Vector2F, alignment: Vector2F,
} }
impl Align { impl<V: View> Align<V> {
pub fn new(child: ElementBox) -> Self { pub fn new(child: ElementBox<V>) -> Self {
Self { Self {
child, child,
alignment: Vector2F::zero(), alignment: Vector2F::zero(),
@ -42,18 +40,19 @@ impl Align {
} }
} }
impl Element for Align { impl<V: View> Element<V> for Align<V> {
type LayoutState = (); type LayoutState = ();
type PaintState = (); type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
view: &mut V,
mut constraint: SizeConstraint, mut constraint: SizeConstraint,
cx: &mut LayoutContext, cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let mut size = constraint.max; let mut size = constraint.max;
constraint.min = Vector2F::zero(); constraint.min = Vector2F::zero();
let child_size = self.child.layout(constraint, cx); let child_size = self.child.layout(view, constraint, cx);
if size.x().is_infinite() { if size.x().is_infinite() {
size.set_x(child_size.x()); size.set_x(child_size.x());
} }
@ -65,10 +64,12 @@ impl Element for Align {
fn paint( fn paint(
&mut self, &mut self,
view: &mut V,
scene: &mut SceneBuilder,
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
cx: &mut PaintContext, cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
let my_center = bounds.size() / 2.; let my_center = bounds.size() / 2.;
let my_target = my_center + my_center * self.alignment; let my_target = my_center + my_center * self.alignment;
@ -77,6 +78,8 @@ impl Element for Align {
let child_target = child_center + child_center * self.alignment; let child_target = child_center + child_center * self.alignment;
self.child.paint( self.child.paint(
view,
scene,
bounds.origin() - (child_target - my_target), bounds.origin() - (child_target - my_target),
visible_bounds, visible_bounds,
cx, cx,
@ -85,28 +88,30 @@ impl Element for Align {
fn rect_for_text_range( fn rect_for_text_range(
&self, &self,
view: &V,
range_utf16: std::ops::Range<usize>, range_utf16: std::ops::Range<usize>,
_: RectF, _: RectF,
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &MeasurementContext, cx: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx) self.child.rect_for_text_range(view, range_utf16, cx)
} }
fn debug( fn debug(
&self, &self,
view: &V,
bounds: pathfinder_geometry::rect::RectF, bounds: pathfinder_geometry::rect::RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &DebugContext, cx: &ViewContext<V>,
) -> json::Value { ) -> json::Value {
json!({ json!({
"type": "Align", "type": "Align",
"bounds": bounds.to_json(), "bounds": bounds.to_json(),
"alignment": self.alignment.to_json(), "alignment": self.alignment.to_json(),
"child": self.child.debug(cx), "child": self.child.debug(view, cx),
}) })
} }
} }

View file

@ -1,8 +1,7 @@
use super::Element; use super::Element;
use crate::{ use crate::{
json::{self, json}, json::{self, json},
window::MeasurementContext, SceneBuilder, View, ViewContext,
DebugContext, PaintContext,
}; };
use json::ToJson; use json::ToJson;
use pathfinder_geometry::{ use pathfinder_geometry::{
@ -10,20 +9,20 @@ use pathfinder_geometry::{
vector::{vec2f, Vector2F}, vector::{vec2f, Vector2F},
}; };
pub struct Canvas<F>(F); pub struct Canvas<V, F>(F);
impl<F> Canvas<F> impl<V: View, F> Canvas<V, F>
where where
F: FnMut(RectF, RectF, &mut PaintContext), F: FnMut(&mut SceneBuilder, RectF, RectF, &mut V, &mut ViewContext<V>),
{ {
pub fn new(f: F) -> Self { pub fn new(f: F) -> Self {
Self(f) Self(f)
} }
} }
impl<F> Element for Canvas<F> impl<V, F> Element<V> for Canvas<V, F>
where where
F: FnMut(RectF, RectF, &mut PaintContext), F: FnMut(RectF, RectF, &mut ViewContext<V>),
{ {
type LayoutState = (); type LayoutState = ();
type PaintState = (); type PaintState = ();
@ -48,10 +47,12 @@ where
fn paint( fn paint(
&mut self, &mut self,
scene: &mut SceneBuilder,
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
cx: &mut PaintContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
self.0(bounds, visible_bounds, cx) self.0(bounds, visible_bounds, cx)
} }
@ -63,7 +64,8 @@ where
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
_: &MeasurementContext, _: &V,
_: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
None None
} }
@ -73,7 +75,8 @@ where
bounds: RectF, bounds: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
_: &DebugContext, _: &V,
_: &ViewContext<V>,
) -> json::Value { ) -> json::Value {
json!({"type": "Canvas", "bounds": bounds.to_json()}) json!({"type": "Canvas", "bounds": bounds.to_json()})
} }

View file

@ -3,42 +3,43 @@ use std::ops::Range;
use pathfinder_geometry::{rect::RectF, vector::Vector2F}; use pathfinder_geometry::{rect::RectF, vector::Vector2F};
use serde_json::json; use serde_json::json;
use crate::{ use crate::{json, Element, ElementBox, SceneBuilder, SizeConstraint, View, ViewContext};
json, DebugContext, Element, ElementBox, LayoutContext, MeasurementContext, PaintContext,
SizeConstraint,
};
pub struct Clipped { pub struct Clipped<V: View> {
child: ElementBox, child: ElementBox<V>,
} }
impl Clipped { impl<V: View> Clipped<V> {
pub fn new(child: ElementBox) -> Self { pub fn new(child: ElementBox<V>) -> Self {
Self { child } Self { child }
} }
} }
impl Element for Clipped { impl<V: View> Element<V> for Clipped<V> {
type LayoutState = (); type LayoutState = ();
type PaintState = (); type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
cx: &mut LayoutContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
(self.child.layout(constraint, cx), ()) (self.child.layout(constraint, view, cx), ())
} }
fn paint( fn paint(
&mut self, &mut self,
scene: &mut SceneBuilder,
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
cx: &mut PaintContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
cx.scene.push_layer(Some(bounds)); cx.scene.push_layer(Some(bounds));
self.child.paint(bounds.origin(), visible_bounds, cx); self.child
.paint(scene, bounds.origin(), visible_bounds, view, cx);
cx.scene.pop_layer(); cx.scene.pop_layer();
} }
@ -49,9 +50,10 @@ impl Element for Clipped {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &MeasurementContext, view: &V,
cx: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx) self.child.rect_for_text_range(range_utf16, view, cx)
} }
fn debug( fn debug(
@ -59,11 +61,12 @@ impl Element for Clipped {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &DebugContext, view: &V,
cx: &ViewContext<V>,
) -> json::Value { ) -> json::Value {
json!({ json!({
"type": "Clipped", "type": "Clipped",
"child": self.child.debug(cx) "child": self.child.debug(view, cx)
}) })
} }
} }

View file

@ -5,22 +5,20 @@ use serde_json::json;
use crate::{ use crate::{
geometry::{rect::RectF, vector::Vector2F}, geometry::{rect::RectF, vector::Vector2F},
json, json, Element, ElementBox, SceneBuilder, SizeConstraint, View, ViewContext,
window::MeasurementContext,
DebugContext, Element, ElementBox, LayoutContext, PaintContext, SizeConstraint,
}; };
pub struct ConstrainedBox { pub struct ConstrainedBox<V: View> {
child: ElementBox, child: ElementBox<V>,
constraint: Constraint, constraint: Constraint<V>,
} }
pub enum Constraint { pub enum Constraint<V: View> {
Static(SizeConstraint), Static(SizeConstraint),
Dynamic(Box<dyn FnMut(SizeConstraint, &mut LayoutContext) -> SizeConstraint>), Dynamic(Box<dyn FnMut(SizeConstraint, &mut V, &mut ViewContext<V>) -> SizeConstraint>),
} }
impl ToJson for Constraint { impl<V: View> ToJson for Constraint<V> {
fn to_json(&self) -> serde_json::Value { fn to_json(&self) -> serde_json::Value {
match self { match self {
Constraint::Static(constraint) => constraint.to_json(), Constraint::Static(constraint) => constraint.to_json(),
@ -29,8 +27,8 @@ impl ToJson for Constraint {
} }
} }
impl ConstrainedBox { impl<V: View> ConstrainedBox<V> {
pub fn new(child: ElementBox) -> Self { pub fn new(child: ElementBox<V>) -> Self {
Self { Self {
child, child,
constraint: Constraint::Static(Default::default()), constraint: Constraint::Static(Default::default()),
@ -39,7 +37,7 @@ impl ConstrainedBox {
pub fn dynamically( pub fn dynamically(
mut self, mut self,
constraint: impl 'static + FnMut(SizeConstraint, &mut LayoutContext) -> SizeConstraint, constraint: impl 'static + FnMut(SizeConstraint, &mut V, &mut ViewContext<V>) -> SizeConstraint,
) -> Self { ) -> Self {
self.constraint = Constraint::Dynamic(Box::new(constraint)); self.constraint = Constraint::Dynamic(Box::new(constraint));
self self
@ -120,41 +118,48 @@ impl ConstrainedBox {
fn constraint( fn constraint(
&mut self, &mut self,
input_constraint: SizeConstraint, input_constraint: SizeConstraint,
cx: &mut LayoutContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> SizeConstraint { ) -> SizeConstraint {
match &mut self.constraint { match &mut self.constraint {
Constraint::Static(constraint) => *constraint, Constraint::Static(constraint) => *constraint,
Constraint::Dynamic(compute_constraint) => compute_constraint(input_constraint, cx), Constraint::Dynamic(compute_constraint) => {
compute_constraint(input_constraint, view, cx)
}
} }
} }
} }
impl Element for ConstrainedBox { impl<V: View> Element<V> for ConstrainedBox<V> {
type LayoutState = (); type LayoutState = ();
type PaintState = (); type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
mut parent_constraint: SizeConstraint, mut parent_constraint: SizeConstraint,
cx: &mut LayoutContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let constraint = self.constraint(parent_constraint, cx); let constraint = self.constraint(parent_constraint, view, cx);
parent_constraint.min = parent_constraint.min.max(constraint.min); parent_constraint.min = parent_constraint.min.max(constraint.min);
parent_constraint.max = parent_constraint.max.min(constraint.max); parent_constraint.max = parent_constraint.max.min(constraint.max);
parent_constraint.max = parent_constraint.max.max(parent_constraint.min); parent_constraint.max = parent_constraint.max.max(parent_constraint.min);
let size = self.child.layout(parent_constraint, cx); let size = self.child.layout(parent_constraint, view, cx);
(size, ()) (size, ())
} }
fn paint( fn paint(
&mut self, &mut self,
scene: &mut SceneBuilder,
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
cx: &mut PaintContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
cx.paint_layer(Some(visible_bounds), |cx| { cx.paint_layer(Some(visible_bounds), |cx| {
self.child.paint(bounds.origin(), visible_bounds, cx); self.child
.paint(scene, bounds.origin(), visible_bounds, view, cx);
}) })
} }
@ -165,9 +170,10 @@ impl Element for ConstrainedBox {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &MeasurementContext, view: &V,
cx: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx) self.child.rect_for_text_range(range_utf16, view, cx)
} }
fn debug( fn debug(
@ -175,8 +181,9 @@ impl Element for ConstrainedBox {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &DebugContext, view: &V,
cx: &ViewContext<V>,
) -> json::Value { ) -> json::Value {
json!({"type": "ConstrainedBox", "assigned_constraint": self.constraint.to_json(), "child": self.child.debug(cx)}) json!({"type": "ConstrainedBox", "assigned_constraint": self.constraint.to_json(), "child": self.child.debug(view, cx)})
} }
} }

View file

@ -10,8 +10,7 @@ use crate::{
json::ToJson, json::ToJson,
platform::CursorStyle, platform::CursorStyle,
scene::{self, Border, CursorRegion, Quad}, scene::{self, Border, CursorRegion, Quad},
window::MeasurementContext, Element, ElementBox, SceneBuilder, SizeConstraint, View, ViewContext,
Element, ElementBox, LayoutContext, PaintContext, SizeConstraint,
}; };
use serde::Deserialize; use serde::Deserialize;
use serde_json::json; use serde_json::json;
@ -36,13 +35,13 @@ pub struct ContainerStyle {
pub cursor: Option<CursorStyle>, pub cursor: Option<CursorStyle>,
} }
pub struct Container { pub struct Container<V: View> {
child: ElementBox, child: ElementBox<V>,
style: ContainerStyle, style: ContainerStyle,
} }
impl Container { impl<V: View> Container<V> {
pub fn new(child: ElementBox) -> Self { pub fn new(child: ElementBox<V>) -> Self {
Self { Self {
child, child,
style: Default::default(), style: Default::default(),
@ -185,14 +184,15 @@ impl Container {
} }
} }
impl Element for Container { impl<V: View> Element<V> for Container<V> {
type LayoutState = (); type LayoutState = ();
type PaintState = (); type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
cx: &mut LayoutContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let mut size_buffer = self.margin_size() + self.padding_size(); let mut size_buffer = self.margin_size() + self.padding_size();
if !self.style.border.overlay { if !self.style.border.overlay {
@ -202,16 +202,18 @@ impl Element for Container {
min: (constraint.min - size_buffer).max(Vector2F::zero()), min: (constraint.min - size_buffer).max(Vector2F::zero()),
max: (constraint.max - size_buffer).max(Vector2F::zero()), max: (constraint.max - size_buffer).max(Vector2F::zero()),
}; };
let child_size = self.child.layout(child_constraint, cx); let child_size = self.child.layout(child_constraint, view, cx);
(child_size + size_buffer, ()) (child_size + size_buffer, ())
} }
fn paint( fn paint(
&mut self, &mut self,
scene: &mut SceneBuilder,
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
cx: &mut PaintContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
let quad_bounds = RectF::from_points( let quad_bounds = RectF::from_points(
bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top), bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top),
@ -247,7 +249,8 @@ impl Element for Container {
corner_radius: self.style.corner_radius, corner_radius: self.style.corner_radius,
}); });
self.child.paint(child_origin, visible_bounds, cx); self.child
.paint(scene, child_origin, visible_bounds, view, cx);
cx.scene.push_layer(None); cx.scene.push_layer(None);
cx.scene.push_quad(Quad { cx.scene.push_quad(Quad {
@ -270,7 +273,8 @@ impl Element for Container {
self.style.border.left_width(), self.style.border.left_width(),
self.style.border.top_width(), self.style.border.top_width(),
); );
self.child.paint(child_origin, visible_bounds, cx); self.child
.paint(scene, child_origin, visible_bounds, view, cx);
if self.style.overlay_color.is_some() { if self.style.overlay_color.is_some() {
cx.scene.push_layer(None); cx.scene.push_layer(None);
@ -292,9 +296,10 @@ impl Element for Container {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &MeasurementContext, view: &V,
cx: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx) self.child.rect_for_text_range(range_utf16, view, cx)
} }
fn debug( fn debug(
@ -302,13 +307,14 @@ impl Element for Container {
bounds: RectF, bounds: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
view: &V,
cx: &crate::DebugContext, cx: &crate::DebugContext,
) -> serde_json::Value { ) -> serde_json::Value {
json!({ json!({
"type": "Container", "type": "Container",
"bounds": bounds.to_json(), "bounds": bounds.to_json(),
"details": self.style.to_json(), "details": self.style.to_json(),
"child": self.child.debug(cx), "child": self.child.debug(view, cx),
}) })
} }
} }

View file

@ -6,10 +6,9 @@ use crate::{
vector::{vec2f, Vector2F}, vector::{vec2f, Vector2F},
}, },
json::{json, ToJson}, json::{json, ToJson},
window::MeasurementContext, SceneBuilder, View, ViewContext,
DebugContext,
}; };
use crate::{Element, LayoutContext, PaintContext, SizeConstraint}; use crate::{Element, SizeConstraint};
#[derive(Default)] #[derive(Default)]
pub struct Empty { pub struct Empty {
@ -27,14 +26,15 @@ impl Empty {
} }
} }
impl Element for Empty { impl<V: View> Element<V> for Empty {
type LayoutState = (); type LayoutState = ();
type PaintState = (); type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
_: &mut LayoutContext, _: &V,
_: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let x = if constraint.max.x().is_finite() && !self.collapsed { let x = if constraint.max.x().is_finite() && !self.collapsed {
constraint.max.x() constraint.max.x()
@ -52,10 +52,11 @@ impl Element for Empty {
fn paint( fn paint(
&mut self, &mut self,
_: &mut SceneBuilder,
_: RectF, _: RectF,
_: RectF, _: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
_: &mut PaintContext, _: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
} }
@ -66,7 +67,8 @@ impl Element for Empty {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
_: &MeasurementContext, _: &V,
_: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
None None
} }
@ -76,7 +78,8 @@ impl Element for Empty {
bounds: RectF, bounds: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
_: &DebugContext, _: &V,
_: &ViewContext<V>,
) -> serde_json::Value { ) -> serde_json::Value {
json!({ json!({
"type": "Empty", "type": "Empty",

View file

@ -2,20 +2,18 @@ use std::ops::Range;
use crate::{ use crate::{
geometry::{rect::RectF, vector::Vector2F}, geometry::{rect::RectF, vector::Vector2F},
json, json, Element, ElementBox, SceneBuilder, SizeConstraint, View, ViewContext,
window::MeasurementContext,
DebugContext, Element, ElementBox, LayoutContext, PaintContext, SizeConstraint,
}; };
use serde_json::json; use serde_json::json;
pub struct Expanded { pub struct Expanded<V: View> {
child: ElementBox, child: ElementBox<V>,
full_width: bool, full_width: bool,
full_height: bool, full_height: bool,
} }
impl Expanded { impl<V: View> Expanded<V> {
pub fn new(child: ElementBox) -> Self { pub fn new(child: ElementBox<V>) -> Self {
Self { Self {
child, child,
full_width: true, full_width: true,
@ -36,14 +34,15 @@ impl Expanded {
} }
} }
impl Element for Expanded { impl<V: View> Element<V> for Expanded<V> {
type LayoutState = (); type LayoutState = ();
type PaintState = (); type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
mut constraint: SizeConstraint, mut constraint: SizeConstraint,
cx: &mut LayoutContext, view: &mut V,
cx: &mut ViewContext<Self>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
if self.full_width { if self.full_width {
constraint.min.set_x(constraint.max.x()); constraint.min.set_x(constraint.max.x());
@ -51,18 +50,21 @@ impl Element for Expanded {
if self.full_height { if self.full_height {
constraint.min.set_y(constraint.max.y()); constraint.min.set_y(constraint.max.y());
} }
let size = self.child.layout(constraint, cx); let size = self.child.layout(constraint, view, cx);
(size, ()) (size, ())
} }
fn paint( fn paint(
&mut self, &mut self,
scene: &mut SceneBuilder,
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
cx: &mut PaintContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
self.child.paint(bounds.origin(), visible_bounds, cx); self.child
.paint(scene, bounds.origin(), visible_bounds, view, cx);
} }
fn rect_for_text_range( fn rect_for_text_range(
@ -72,9 +74,10 @@ impl Element for Expanded {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &MeasurementContext, view: &V,
cx: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx) self.child.rect_for_text_range(range_utf16, view, cx)
} }
fn debug( fn debug(
@ -82,13 +85,14 @@ impl Element for Expanded {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &DebugContext, view: &V,
cx: &ViewContext<V>,
) -> json::Value { ) -> json::Value {
json!({ json!({
"type": "Expanded", "type": "Expanded",
"full_width": self.full_width, "full_width": self.full_width,
"full_height": self.full_height, "full_height": self.full_height,
"child": self.child.debug(cx) "child": self.child.debug(view, cx)
}) })
} }
} }

View file

@ -2,9 +2,8 @@ use std::{any::Any, cell::Cell, f32::INFINITY, ops::Range, rc::Rc};
use crate::{ use crate::{
json::{self, ToJson, Value}, json::{self, ToJson, Value},
window::MeasurementContext, Axis, Element, ElementBox, ElementStateHandle, SceneBuilder, SizeConstraint, Vector2FExt, View,
Axis, DebugContext, Element, ElementBox, ElementStateHandle, LayoutContext, PaintContext, ViewContext,
RenderContext, SizeConstraint, Vector2FExt, View,
}; };
use pathfinder_geometry::{ use pathfinder_geometry::{
rect::RectF, rect::RectF,
@ -18,14 +17,14 @@ struct ScrollState {
scroll_position: Cell<f32>, scroll_position: Cell<f32>,
} }
pub struct Flex { pub struct Flex<V: View> {
axis: Axis, axis: Axis,
children: Vec<ElementBox>, children: Vec<ElementBox<V>>,
scroll_state: Option<(ElementStateHandle<Rc<ScrollState>>, usize)>, scroll_state: Option<(ElementStateHandle<Rc<ScrollState>>, usize)>,
child_alignment: f32, child_alignment: f32,
} }
impl Flex { impl<V: View> Flex<V> {
pub fn new(axis: Axis) -> Self { pub fn new(axis: Axis) -> Self {
Self { Self {
axis, axis,
@ -52,15 +51,14 @@ impl Flex {
self self
} }
pub fn scrollable<Tag, V>( pub fn scrollable<Tag>(
mut self, mut self,
element_id: usize, element_id: usize,
scroll_to: Option<usize>, scroll_to: Option<usize>,
cx: &mut RenderContext<V>, cx: &mut ViewContext<V>,
) -> Self ) -> Self
where where
Tag: 'static, Tag: 'static,
V: View,
{ {
let scroll_state = cx.default_element_state::<Tag, Rc<ScrollState>>(element_id); let scroll_state = cx.default_element_state::<Tag, Rc<ScrollState>>(element_id);
scroll_state.read(cx).scroll_to.set(scroll_to); scroll_state.read(cx).scroll_to.set(scroll_to);
@ -75,7 +73,8 @@ impl Flex {
remaining_space: &mut f32, remaining_space: &mut f32,
remaining_flex: &mut f32, remaining_flex: &mut f32,
cross_axis_max: &mut f32, cross_axis_max: &mut f32,
cx: &mut LayoutContext, view: &V,
cx: &mut ViewContext<V>,
) { ) {
let cross_axis = self.axis.invert(); let cross_axis = self.axis.invert();
for child in &mut self.children { for child in &mut self.children {
@ -112,20 +111,21 @@ impl Flex {
} }
} }
impl Extend<ElementBox> for Flex { impl<V: View> Extend<ElementBox<V>> for Flex<V> {
fn extend<T: IntoIterator<Item = ElementBox>>(&mut self, children: T) { fn extend<T: IntoIterator<Item = ElementBox<V>>>(&mut self, children: T) {
self.children.extend(children); self.children.extend(children);
} }
} }
impl Element for Flex { impl<V: View> Element<V> for Flex<V> {
type LayoutState = f32; type LayoutState = f32;
type PaintState = (); type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
cx: &mut LayoutContext, view: &V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let mut total_flex = None; let mut total_flex = None;
let mut fixed_space = 0.0; let mut fixed_space = 0.0;
@ -150,7 +150,7 @@ impl Element for Flex {
vec2f(constraint.max.x(), INFINITY), vec2f(constraint.max.x(), INFINITY),
), ),
}; };
let size = child.layout(child_constraint, cx); let size = child.layout(child_constraint, view, cx);
fixed_space += size.along(self.axis); fixed_space += size.along(self.axis);
cross_axis_max = cross_axis_max.max(size.along(cross_axis)); cross_axis_max = cross_axis_max.max(size.along(cross_axis));
} }
@ -168,6 +168,7 @@ impl Element for Flex {
&mut remaining_space, &mut remaining_space,
&mut remaining_flex, &mut remaining_flex,
&mut cross_axis_max, &mut cross_axis_max,
view,
cx, cx,
); );
self.layout_flex_children( self.layout_flex_children(
@ -176,6 +177,7 @@ impl Element for Flex {
&mut remaining_space, &mut remaining_space,
&mut remaining_flex, &mut remaining_flex,
&mut cross_axis_max, &mut cross_axis_max,
view,
cx, cx,
); );
@ -247,10 +249,12 @@ impl Element for Flex {
fn paint( fn paint(
&mut self, &mut self,
scene: &mut SceneBuilder,
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
remaining_space: &mut Self::LayoutState, remaining_space: &mut Self::LayoutState,
cx: &mut PaintContext, view: &V,
cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
@ -343,7 +347,7 @@ impl Element for Flex {
aligned_child_origin aligned_child_origin
}; };
child.paint(aligned_child_origin, visible_bounds, cx); child.paint(scene, aligned_child_origin, visible_bounds, view, cx);
match self.axis { match self.axis {
Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0), Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
@ -363,11 +367,12 @@ impl Element for Flex {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &MeasurementContext, view: &V,
cx: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
self.children self.children
.iter() .iter()
.find_map(|child| child.rect_for_text_range(range_utf16.clone(), cx)) .find_map(|child| child.rect_for_text_range(view, range_utf16.clone(), cx))
} }
fn debug( fn debug(
@ -375,13 +380,14 @@ impl Element for Flex {
bounds: RectF, bounds: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &DebugContext, view: &V,
cx: &ViewContext<V>,
) -> json::Value { ) -> json::Value {
json!({ json!({
"type": "Flex", "type": "Flex",
"bounds": bounds.to_json(), "bounds": bounds.to_json(),
"axis": self.axis.to_json(), "axis": self.axis.to_json(),
"children": self.children.iter().map(|child| child.debug(cx)).collect::<Vec<json::Value>>() "children": self.children.iter().map(|child| child.debug(view, cx)).collect::<Vec<json::Value>>()
}) })
} }
} }
@ -391,13 +397,13 @@ struct FlexParentData {
float: bool, float: bool,
} }
pub struct FlexItem { pub struct FlexItem<V: View> {
metadata: FlexParentData, metadata: FlexParentData,
child: ElementBox, child: ElementBox<V>,
} }
impl FlexItem { impl<V: View> FlexItem<V> {
pub fn new(child: ElementBox) -> Self { pub fn new(child: ElementBox<V>) -> Self {
FlexItem { FlexItem {
metadata: FlexParentData { metadata: FlexParentData {
flex: None, flex: None,
@ -418,14 +424,15 @@ impl FlexItem {
} }
} }
impl Element for FlexItem { impl<V: View> Element<V> for FlexItem<V> {
type LayoutState = (); type LayoutState = ();
type PaintState = (); type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
cx: &mut LayoutContext, view: &V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let size = self.child.layout(constraint, cx); let size = self.child.layout(constraint, cx);
(size, ()) (size, ())
@ -433,12 +440,15 @@ impl Element for FlexItem {
fn paint( fn paint(
&mut self, &mut self,
scene: &mut SceneBuilder,
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
view: &V,
cx: &mut PaintContext, cx: &mut PaintContext,
) -> Self::PaintState { ) -> Self::PaintState {
self.child.paint(bounds.origin(), visible_bounds, cx) self.child
.paint(scene, bounds.origin(), visible_bounds, view, cx)
} }
fn rect_for_text_range( fn rect_for_text_range(
@ -448,9 +458,10 @@ impl Element for FlexItem {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &MeasurementContext, view: &V,
cx: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx) self.child.rect_for_text_range(range_utf16, view, cx)
} }
fn metadata(&self) -> Option<&dyn Any> { fn metadata(&self) -> Option<&dyn Any> {
@ -462,12 +473,14 @@ impl Element for FlexItem {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &DebugContext, view: &V,
cx: &ViewContext<V>,
) -> Value { ) -> Value {
json!({ json!({
"type": "Flexible", "type": "Flexible",
"flex": self.metadata.flex, "flex": self.metadata.flex,
"child": self.child.debug(cx) "child": self.child.debug(view, cx)
}) })
} }
} }

View file

@ -3,17 +3,16 @@ use std::ops::Range;
use crate::{ use crate::{
geometry::{rect::RectF, vector::Vector2F}, geometry::{rect::RectF, vector::Vector2F},
json::json, json::json,
window::MeasurementContext, Element, ElementBox, SceneBuilder, SizeConstraint, View, ViewContext,
DebugContext, Element, ElementBox, LayoutContext, PaintContext, SizeConstraint,
}; };
pub struct Hook { pub struct Hook<V: View> {
child: ElementBox, child: ElementBox<V>,
after_layout: Option<Box<dyn FnMut(Vector2F, &mut LayoutContext)>>, after_layout: Option<Box<dyn FnMut(Vector2F, &mut ViewContext<V>)>>,
} }
impl Hook { impl<V: View> Hook<V> {
pub fn new(child: ElementBox) -> Self { pub fn new(child: ElementBox<V>) -> Self {
Self { Self {
child, child,
after_layout: None, after_layout: None,
@ -22,23 +21,24 @@ impl Hook {
pub fn on_after_layout( pub fn on_after_layout(
mut self, mut self,
f: impl 'static + FnMut(Vector2F, &mut LayoutContext), f: impl 'static + FnMut(Vector2F, &mut ViewContext<V>),
) -> Self { ) -> Self {
self.after_layout = Some(Box::new(f)); self.after_layout = Some(Box::new(f));
self self
} }
} }
impl Element for Hook { impl<V: View> Element<V> for Hook<V> {
type LayoutState = (); type LayoutState = ();
type PaintState = (); type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
cx: &mut LayoutContext, view: &V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let size = self.child.layout(constraint, cx); let size = self.child.layout(constraint, view, cx);
if let Some(handler) = self.after_layout.as_mut() { if let Some(handler) = self.after_layout.as_mut() {
handler(size, cx); handler(size, cx);
} }
@ -47,12 +47,15 @@ impl Element for Hook {
fn paint( fn paint(
&mut self, &mut self,
scene: &mut SceneBuilder,
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
cx: &mut PaintContext, view: &mut V,
cx: &mut ViewContext<V>,
) { ) {
self.child.paint(bounds.origin(), visible_bounds, cx); self.child
.paint(scene, bounds.origin(), visible_bounds, view, cx);
} }
fn rect_for_text_range( fn rect_for_text_range(
@ -62,9 +65,10 @@ impl Element for Hook {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &MeasurementContext, view: &V,
cx: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx) self.child.rect_for_text_range(range_utf16, view, cx)
} }
fn debug( fn debug(
@ -72,11 +76,12 @@ impl Element for Hook {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &DebugContext, view: &V,
cx: &ViewContext<V>,
) -> serde_json::Value { ) -> serde_json::Value {
json!({ json!({
"type": "Hooks", "type": "Hooks",
"child": self.child.debug(cx), "child": self.child.debug(view, cx),
}) })
} }
} }

View file

@ -5,9 +5,7 @@ use crate::{
vector::{vec2f, Vector2F}, vector::{vec2f, Vector2F},
}, },
json::{json, ToJson}, json::{json, ToJson},
scene, scene, Border, Element, ImageData, SceneBuilder, SizeConstraint, View, ViewContext,
window::MeasurementContext,
Border, DebugContext, Element, ImageData, LayoutContext, PaintContext, SizeConstraint,
}; };
use serde::Deserialize; use serde::Deserialize;
use std::{ops::Range, sync::Arc}; use std::{ops::Range, sync::Arc};
@ -57,14 +55,15 @@ impl Image {
} }
} }
impl Element for Image { impl<V: View> Element<V> for Image {
type LayoutState = Option<Arc<ImageData>>; type LayoutState = Option<Arc<ImageData>>;
type PaintState = (); type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
cx: &mut LayoutContext, view: &V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let data = match &self.source { let data = match &self.source {
ImageSource::Path(path) => match cx.asset_cache.png(path) { ImageSource::Path(path) => match cx.asset_cache.png(path) {
@ -91,13 +90,15 @@ impl Element for Image {
fn paint( fn paint(
&mut self, &mut self,
scene: &mut SceneBuilder,
bounds: RectF, bounds: RectF,
_: RectF, _: RectF,
layout: &mut Self::LayoutState, layout: &mut Self::LayoutState,
cx: &mut PaintContext, _: &V,
cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
if let Some(data) = layout { if let Some(data) = layout {
cx.scene.push_image(scene::Image { scene.push_image(scene::Image {
bounds, bounds,
border: self.style.border, border: self.style.border,
corner_radius: self.style.corner_radius, corner_radius: self.style.corner_radius,
@ -114,7 +115,8 @@ impl Element for Image {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
_: &MeasurementContext, _: &V,
_: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
None None
} }
@ -124,7 +126,8 @@ impl Element for Image {
bounds: RectF, bounds: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
_: &DebugContext, _: &V,
_: &ViewContext<V>,
) -> serde_json::Value { ) -> serde_json::Value {
json!({ json!({
"type": "Image", "type": "Image",

View file

@ -2,7 +2,7 @@ use crate::{
elements::*, elements::*,
fonts::TextStyle, fonts::TextStyle,
geometry::{rect::RectF, vector::Vector2F}, geometry::{rect::RectF, vector::Vector2F},
Action, ElementBox, LayoutContext, PaintContext, SizeConstraint, Action, ElementBox, SizeConstraint,
}; };
use serde_json::json; use serde_json::json;
@ -34,15 +34,16 @@ impl KeystrokeLabel {
} }
} }
impl Element for KeystrokeLabel { impl<V: View> Element<V> for KeystrokeLabel {
type LayoutState = ElementBox; type LayoutState = ElementBox<V>;
type PaintState = (); type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
cx: &mut LayoutContext, view: &V,
) -> (Vector2F, ElementBox) { cx: &mut ViewContext<V>,
) -> (Vector2F, ElementBox<V>) {
let mut element = if let Some(keystrokes) = let mut element = if let Some(keystrokes) =
cx.app cx.app
.keystrokes_for_action(self.window_id, self.view_id, self.action.as_ref()) .keystrokes_for_action(self.window_id, self.view_id, self.action.as_ref())
@ -59,18 +60,20 @@ impl Element for KeystrokeLabel {
Empty::new().collapsed().boxed() Empty::new().collapsed().boxed()
}; };
let size = element.layout(constraint, cx); let size = element.layout(constraint, view, cx);
(size, element) (size, element)
} }
fn paint( fn paint(
&mut self, &mut self,
scene: &mut SceneBuilder,
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
element: &mut ElementBox, element: &mut ElementBox<V>,
cx: &mut PaintContext, view: &V,
cx: &mut ViewContext<V>,
) { ) {
element.paint(bounds.origin(), visible_bounds, cx); element.paint(scene, bounds.origin(), visible_bounds, view, cx);
} }
fn rect_for_text_range( fn rect_for_text_range(
@ -80,7 +83,8 @@ impl Element for KeystrokeLabel {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
_: &MeasurementContext, _: &V,
_: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
None None
} }
@ -88,14 +92,15 @@ impl Element for KeystrokeLabel {
fn debug( fn debug(
&self, &self,
_: RectF, _: RectF,
element: &ElementBox, element: &ElementBox<V>,
_: &(), _: &(),
cx: &crate::DebugContext, view: &V,
cx: &ViewContext<V>,
) -> serde_json::Value { ) -> serde_json::Value {
json!({ json!({
"type": "KeystrokeLabel", "type": "KeystrokeLabel",
"action": self.action.name(), "action": self.action.name(),
"child": element.debug(cx) "child": element.debug(view, cx)
}) })
} }
} }

View file

@ -8,8 +8,7 @@ use crate::{
}, },
json::{ToJson, Value}, json::{ToJson, Value},
text_layout::{Line, RunStyle}, text_layout::{Line, RunStyle},
window::MeasurementContext, Element, SceneBuilder, SizeConstraint, View, ViewContext,
DebugContext, Element, LayoutContext, PaintContext, SizeConstraint,
}; };
use serde::Deserialize; use serde::Deserialize;
use serde_json::json; use serde_json::json;
@ -128,14 +127,15 @@ impl Label {
} }
} }
impl Element for Label { impl<V: View> Element<V> for Label {
type LayoutState = Line; type LayoutState = Line;
type PaintState = (); type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
cx: &mut LayoutContext, view: &V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let runs = self.compute_runs(); let runs = self.compute_runs();
let line = let line =
@ -155,12 +155,20 @@ impl Element for Label {
fn paint( fn paint(
&mut self, &mut self,
scene: &mut SceneBuilder,
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
line: &mut Self::LayoutState, line: &mut Self::LayoutState,
cx: &mut PaintContext, _: &V,
cx: &mut ViewContext<Self>,
) -> Self::PaintState { ) -> Self::PaintState {
line.paint(bounds.origin(), visible_bounds, bounds.size().y(), cx) line.paint(
scene,
bounds.origin(),
visible_bounds,
bounds.size().y(),
cx,
)
} }
fn rect_for_text_range( fn rect_for_text_range(
@ -170,7 +178,8 @@ impl Element for Label {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
_: &MeasurementContext, _: &V,
_: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
None None
} }
@ -180,7 +189,8 @@ impl Element for Label {
bounds: RectF, bounds: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
_: &DebugContext, _: &V,
_: &ViewContext<V>,
) -> Value { ) -> Value {
json!({ json!({
"type": "Label", "type": "Label",

View file

@ -4,19 +4,17 @@ use crate::{
vector::{vec2f, Vector2F}, vector::{vec2f, Vector2F},
}, },
json::json, json::json,
window::MeasurementContext, Element, ElementBox, MouseRegion, SceneBuilder, SizeConstraint, View, ViewContext,
DebugContext, Element, ElementBox, ElementRc, EventContext, LayoutContext, MouseRegion,
PaintContext, RenderContext, SizeConstraint, View, ViewContext,
}; };
use std::{cell::RefCell, collections::VecDeque, ops::Range, rc::Rc}; use std::{cell::RefCell, collections::VecDeque, ops::Range, rc::Rc};
use sum_tree::{Bias, SumTree}; use sum_tree::{Bias, SumTree};
pub struct List { pub struct List<V: View> {
state: ListState, state: ListState<V>,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct ListState(Rc<RefCell<StateInner>>); pub struct ListState<V: View>(Rc<RefCell<StateInner<V>>>);
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Orientation { pub enum Orientation {
@ -24,16 +22,16 @@ pub enum Orientation {
Bottom, Bottom,
} }
struct StateInner { struct StateInner<V: View> {
last_layout_width: Option<f32>, last_layout_width: Option<f32>,
render_item: Box<dyn FnMut(usize, &mut LayoutContext) -> Option<ElementBox>>, render_item: Box<dyn FnMut(usize, &V, &mut ViewContext<V>) -> Option<ElementBox<V>>>,
rendered_range: Range<usize>, rendered_range: Range<usize>,
items: SumTree<ListItem>, items: SumTree<ListItem<V>>,
logical_scroll_top: Option<ListOffset>, logical_scroll_top: Option<ListOffset>,
orientation: Orientation, orientation: Orientation,
overdraw: f32, overdraw: f32,
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
scroll_handler: Option<Box<dyn FnMut(Range<usize>, &mut EventContext)>>, scroll_handler: Option<Box<dyn FnMut(Range<usize>, &mut V, &mut ViewContext<V>)>>,
} }
#[derive(Clone, Copy, Debug, Default, PartialEq)] #[derive(Clone, Copy, Debug, Default, PartialEq)]
@ -43,13 +41,13 @@ pub struct ListOffset {
} }
#[derive(Clone)] #[derive(Clone)]
enum ListItem { enum ListItem<V: View> {
Unrendered, Unrendered,
Rendered(ElementRc), Rendered(Rc<ElementBox<V>>),
Removed(f32), Removed(f32),
} }
impl std::fmt::Debug for ListItem { impl<V: View> std::fmt::Debug for ListItem<V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::Unrendered => write!(f, "Unrendered"), Self::Unrendered => write!(f, "Unrendered"),
@ -79,20 +77,21 @@ struct UnrenderedCount(usize);
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
struct Height(f32); struct Height(f32);
impl List { impl<V: View> List<V> {
pub fn new(state: ListState) -> Self { pub fn new(state: ListState<V>) -> Self {
Self { state } Self { state }
} }
} }
impl Element for List { impl<V: View> Element<V> for List<V> {
type LayoutState = ListOffset; type LayoutState = ListOffset;
type PaintState = (); type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
cx: &mut LayoutContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let state = &mut *self.state.0.borrow_mut(); let state = &mut *self.state.0.borrow_mut();
let size = constraint.max; let size = constraint.max;
@ -134,6 +133,7 @@ impl Element for List {
scroll_top.item_ix + ix, scroll_top.item_ix + ix,
existing_element, existing_element,
item_constraint, item_constraint,
view,
cx, cx,
) { ) {
rendered_height += element.size().y(); rendered_height += element.size().y();
@ -151,7 +151,7 @@ impl Element for List {
cursor.prev(&()); cursor.prev(&());
if cursor.item().is_some() { if cursor.item().is_some() {
if let Some(element) = if let Some(element) =
state.render_item(cursor.start().0, None, item_constraint, cx) state.render_item(cursor.start().0, None, item_constraint, view, cx)
{ {
rendered_height += element.size().y(); rendered_height += element.size().y();
rendered_items.push_front(ListItem::Rendered(element)); rendered_items.push_front(ListItem::Rendered(element));
@ -187,7 +187,7 @@ impl Element for List {
cursor.prev(&()); cursor.prev(&());
if let Some(item) = cursor.item() { if let Some(item) = cursor.item() {
if let Some(element) = if let Some(element) =
state.render_item(cursor.start().0, Some(item), item_constraint, cx) state.render_item(cursor.start().0, Some(item), item_constraint, view, cx)
{ {
leading_overdraw += element.size().y(); leading_overdraw += element.size().y();
rendered_items.push_front(ListItem::Rendered(element)); rendered_items.push_front(ListItem::Rendered(element));
@ -241,10 +241,12 @@ impl Element for List {
fn paint( fn paint(
&mut self, &mut self,
scene: &mut SceneBuilder,
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
scroll_top: &mut ListOffset, scroll_top: &mut ListOffset,
cx: &mut PaintContext, view: &V,
cx: &mut ViewContext<V>,
) { ) {
let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default(); let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
cx.scene.push_layer(Some(visible_bounds)); cx.scene.push_layer(Some(visible_bounds));
@ -268,7 +270,7 @@ impl Element for List {
let state = &mut *self.state.0.borrow_mut(); let state = &mut *self.state.0.borrow_mut();
for (mut element, origin) in state.visible_elements(bounds, scroll_top) { for (mut element, origin) in state.visible_elements(bounds, scroll_top) {
element.paint(origin, visible_bounds, cx); element.paint(scene, origin, visible_bounds, view, cx);
} }
cx.scene.pop_layer(); cx.scene.pop_layer();
@ -281,7 +283,8 @@ impl Element for List {
_: RectF, _: RectF,
scroll_top: &Self::LayoutState, scroll_top: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &MeasurementContext, view: &V,
cx: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
let state = self.state.0.borrow(); let state = self.state.0.borrow();
let mut item_origin = bounds.origin() - vec2f(0., scroll_top.offset_in_item); let mut item_origin = bounds.origin() - vec2f(0., scroll_top.offset_in_item);
@ -293,7 +296,7 @@ impl Element for List {
} }
if let ListItem::Rendered(element) = item { if let ListItem::Rendered(element) = item {
if let Some(rect) = element.rect_for_text_range(range_utf16.clone(), cx) { if let Some(rect) = element.rect_for_text_range(range_utf16.clone(), view, cx) {
return Some(rect); return Some(rect);
} }
@ -312,12 +315,13 @@ impl Element for List {
bounds: RectF, bounds: RectF,
scroll_top: &Self::LayoutState, scroll_top: &Self::LayoutState,
_: &(), _: &(),
cx: &DebugContext, view: &V,
cx: &ViewContext<V>,
) -> serde_json::Value { ) -> serde_json::Value {
let state = self.state.0.borrow_mut(); let state = self.state.0.borrow_mut();
let visible_elements = state let visible_elements = state
.visible_elements(bounds, scroll_top) .visible_elements(bounds, scroll_top)
.map(|e| e.0.debug(cx)) .map(|e| e.0.debug(view, cx))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let visible_range = scroll_top.item_ix..(scroll_top.item_ix + visible_elements.len()); let visible_range = scroll_top.item_ix..(scroll_top.item_ix + visible_elements.len());
json!({ json!({
@ -328,8 +332,8 @@ impl Element for List {
} }
} }
impl ListState { impl<V: View> ListState<V> {
pub fn new<F, V>( pub fn new<F>(
element_count: usize, element_count: usize,
orientation: Orientation, orientation: Orientation,
overdraw: f32, overdraw: f32,
@ -338,7 +342,7 @@ impl ListState {
) -> Self ) -> Self
where where
V: View, V: View,
F: 'static + FnMut(&mut V, usize, &mut RenderContext<V>) -> ElementBox, F: 'static + FnMut(&mut V, usize, &mut ViewContext<V>) -> ElementBox<V>,
{ {
let mut items = SumTree::new(); let mut items = SumTree::new();
items.extend((0..element_count).map(|_| ListItem::Unrendered), &()); items.extend((0..element_count).map(|_| ListItem::Unrendered), &());
@ -406,7 +410,7 @@ impl ListState {
pub fn set_scroll_handler( pub fn set_scroll_handler(
&mut self, &mut self,
handler: impl FnMut(Range<usize>, &mut EventContext) + 'static, handler: impl FnMut(Range<usize>, &mut V, &mut ViewContext<V>) + 'static,
) { ) {
self.0.borrow_mut().scroll_handler = Some(Box::new(handler)) self.0.borrow_mut().scroll_handler = Some(Box::new(handler))
} }
@ -426,14 +430,15 @@ impl ListState {
} }
} }
impl StateInner { impl<V: View> StateInner<V> {
fn render_item( fn render_item(
&mut self, &mut self,
ix: usize, ix: usize,
existing_element: Option<&ListItem>, existing_element: Option<&ListItem<V>>,
constraint: SizeConstraint, constraint: SizeConstraint,
cx: &mut LayoutContext, view: &mut V,
) -> Option<ElementRc> { cx: &mut ViewContext<V>,
) -> Option<Rc<ElementBox<V>>> {
if let Some(ListItem::Rendered(element)) = existing_element { if let Some(ListItem::Rendered(element)) = existing_element {
Some(element.clone()) Some(element.clone())
} else { } else {
@ -455,7 +460,7 @@ impl StateInner {
&'a self, &'a self,
bounds: RectF, bounds: RectF,
scroll_top: &ListOffset, scroll_top: &ListOffset,
) -> impl Iterator<Item = (ElementRc, Vector2F)> + 'a { ) -> impl Iterator<Item = (Rc<ElementBox>, Vector2F)> + 'a {
let mut item_origin = bounds.origin() - vec2f(0., scroll_top.offset_in_item); let mut item_origin = bounds.origin() - vec2f(0., scroll_top.offset_in_item);
let mut cursor = self.items.cursor::<Count>(); let mut cursor = self.items.cursor::<Count>();
cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &()); cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
@ -485,7 +490,8 @@ impl StateInner {
height: f32, height: f32,
mut delta: Vector2F, mut delta: Vector2F,
precise: bool, precise: bool,
cx: &mut EventContext, view: &mut V,
cx: &mut ViewContext<V>,
) { ) {
if !precise { if !precise {
delta *= 20.; delta *= 20.;
@ -538,7 +544,7 @@ impl StateInner {
} }
} }
impl ListItem { impl<V: View> ListItem<V> {
fn remove(&self) -> Self { fn remove(&self) -> Self {
match self { match self {
ListItem::Unrendered => ListItem::Unrendered, ListItem::Unrendered => ListItem::Unrendered,
@ -548,7 +554,7 @@ impl ListItem {
} }
} }
impl sum_tree::Item for ListItem { impl<V: View> sum_tree::Item for ListItem<V> {
type Summary = ListItemSummary; type Summary = ListItemSummary;
fn summary(&self) -> Self::Summary { fn summary(&self) -> Self::Summary {
@ -900,7 +906,7 @@ mod tests {
"TestView" "TestView"
} }
fn render(&mut self, _: &mut RenderContext<'_, Self>) -> ElementBox { fn render(&mut self, _: &mut ViewContext<Self>) -> ElementBox<Self> {
Empty::new().boxed() Empty::new().boxed()
} }
} }
@ -919,15 +925,28 @@ mod tests {
} }
} }
impl Element for TestElement { impl<V: View> Element<V> for TestElement {
type LayoutState = (); type LayoutState = ();
type PaintState = (); type PaintState = ();
fn layout(&mut self, _: SizeConstraint, _: &mut LayoutContext) -> (Vector2F, ()) { fn layout(
&mut self,
_: SizeConstraint,
_: &mut V,
_: &mut ViewContext<V>,
) -> (Vector2F, ()) {
(self.size, ()) (self.size, ())
} }
fn paint(&mut self, _: RectF, _: RectF, _: &mut (), _: &mut PaintContext) { fn paint(
&mut self,
_: &mut SceneBuilder,
_: RectF,
_: RectF,
_: &mut (),
_: &mut V,
_: &mut ViewContext<V>,
) {
todo!() todo!()
} }
@ -938,12 +957,13 @@ mod tests {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
_: &MeasurementContext, _: &V,
_: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
todo!() todo!()
} }
fn debug(&self, _: RectF, _: &(), _: &(), _: &DebugContext) -> serde_json::Value { fn debug(&self, _: RectF, _: &(), _: &(), _: &V, _: &ViewContext<V>) -> serde_json::Value {
self.id.into() self.id.into()
} }

View file

@ -10,14 +10,13 @@ use crate::{
CursorRegion, HandlerSet, MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseHover, CursorRegion, HandlerSet, MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseHover,
MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
}, },
DebugContext, Element, ElementBox, EventContext, LayoutContext, MeasurementContext, Element, ElementBox, MouseRegion, MouseState, SceneBuilder, SizeConstraint, View, ViewContext,
MouseRegion, MouseState, PaintContext, RenderContext, SizeConstraint, View,
}; };
use serde_json::json; use serde_json::json;
use std::{marker::PhantomData, ops::Range}; use std::{marker::PhantomData, ops::Range};
pub struct MouseEventHandler<Tag: 'static> { pub struct MouseEventHandler<Tag: 'static, V: View> {
child: ElementBox, child: ElementBox<V>,
region_id: usize, region_id: usize,
cursor_style: Option<CursorStyle>, cursor_style: Option<CursorStyle>,
handlers: HandlerSet, handlers: HandlerSet,
@ -31,14 +30,14 @@ pub struct MouseEventHandler<Tag: 'static> {
/// Element which provides a render_child callback with a MouseState and paints a mouse /// Element which provides a render_child callback with a MouseState and paints a mouse
/// region under (or above) it for easy mouse event handling. /// region under (or above) it for easy mouse event handling.
impl<Tag> MouseEventHandler<Tag> { impl<Tag, V: View> MouseEventHandler<Tag, V> {
pub fn new<V, F>(region_id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self pub fn new<F>(region_id: usize, view: &mut V, cx: &mut ViewContext<V>, render_child: F) -> Self
where where
V: View, V: View,
F: FnOnce(&mut MouseState, &mut RenderContext<V>) -> ElementBox, F: FnOnce(&mut MouseState, &mut V, &mut ViewContext<V>) -> ElementBox<V>,
{ {
let mut mouse_state = cx.mouse_state::<Tag>(region_id); 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, view, cx);
let notify_on_hover = mouse_state.accessed_hovered(); let notify_on_hover = mouse_state.accessed_hovered();
let notify_on_click = mouse_state.accessed_clicked(); let notify_on_click = mouse_state.accessed_clicked();
Self { Self {
@ -58,12 +57,17 @@ impl<Tag> MouseEventHandler<Tag> {
/// Modifies the MouseEventHandler to render the MouseRegion above the child element. Useful /// 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 /// for drag and drop handling and similar events which should be captured before the child
/// gets the opportunity /// gets the opportunity
pub fn above<V, F>(region_id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self pub fn above<F>(
region_id: usize,
view: &mut V,
cx: &mut ViewContext<V>,
render_child: F,
) -> Self
where where
V: View, V: View,
F: FnOnce(&mut MouseState, &mut RenderContext<V>) -> ElementBox, F: FnOnce(&mut MouseState, &mut V, &mut ViewContext<V>) -> ElementBox<V>,
{ {
let mut handler = Self::new(region_id, cx, render_child); let mut handler = Self::new(region_id, view, cx, render_child);
handler.above = true; handler.above = true;
handler handler
} }
@ -78,14 +82,14 @@ impl<Tag> MouseEventHandler<Tag> {
self self
} }
pub fn on_move(mut self, handler: impl Fn(MouseMove, &mut EventContext) + 'static) -> Self { pub fn on_move(mut self, handler: impl Fn(MouseMove, &mut ViewContext<V>) + 'static) -> Self {
self.handlers = self.handlers.on_move(handler); self.handlers = self.handlers.on_move(handler);
self self
} }
pub fn on_move_out( pub fn on_move_out(
mut self, mut self,
handler: impl Fn(MouseMoveOut, &mut EventContext) + 'static, handler: impl Fn(MouseMoveOut, &mut ViewContext<V>) + 'static,
) -> Self { ) -> Self {
self.handlers = self.handlers.on_move_out(handler); self.handlers = self.handlers.on_move_out(handler);
self self
@ -94,7 +98,7 @@ impl<Tag> MouseEventHandler<Tag> {
pub fn on_down( pub fn on_down(
mut self, mut self,
button: MouseButton, button: MouseButton,
handler: impl Fn(MouseDown, &mut EventContext) + 'static, handler: impl Fn(MouseDown, &mut ViewContext<V>) + 'static,
) -> Self { ) -> Self {
self.handlers = self.handlers.on_down(button, handler); self.handlers = self.handlers.on_down(button, handler);
self self
@ -103,7 +107,7 @@ impl<Tag> MouseEventHandler<Tag> {
pub fn on_up( pub fn on_up(
mut self, mut self,
button: MouseButton, button: MouseButton,
handler: impl Fn(MouseUp, &mut EventContext) + 'static, handler: impl Fn(MouseUp, &mut ViewContext<V>) + 'static,
) -> Self { ) -> Self {
self.handlers = self.handlers.on_up(button, handler); self.handlers = self.handlers.on_up(button, handler);
self self
@ -112,7 +116,7 @@ impl<Tag> MouseEventHandler<Tag> {
pub fn on_click( pub fn on_click(
mut self, mut self,
button: MouseButton, button: MouseButton,
handler: impl Fn(MouseClick, &mut EventContext) + 'static, handler: impl Fn(MouseClick, &mut ViewContext<V>) + 'static,
) -> Self { ) -> Self {
self.handlers = self.handlers.on_click(button, handler); self.handlers = self.handlers.on_click(button, handler);
self self
@ -121,7 +125,7 @@ impl<Tag> MouseEventHandler<Tag> {
pub fn on_down_out( pub fn on_down_out(
mut self, mut self,
button: MouseButton, button: MouseButton,
handler: impl Fn(MouseDownOut, &mut EventContext) + 'static, handler: impl Fn(MouseDownOut, &mut ViewContext<V>) + 'static,
) -> Self { ) -> Self {
self.handlers = self.handlers.on_down_out(button, handler); self.handlers = self.handlers.on_down_out(button, handler);
self self
@ -130,7 +134,7 @@ impl<Tag> MouseEventHandler<Tag> {
pub fn on_up_out( pub fn on_up_out(
mut self, mut self,
button: MouseButton, button: MouseButton,
handler: impl Fn(MouseUpOut, &mut EventContext) + 'static, handler: impl Fn(MouseUpOut, &mut ViewContext<V>) + 'static,
) -> Self { ) -> Self {
self.handlers = self.handlers.on_up_out(button, handler); self.handlers = self.handlers.on_up_out(button, handler);
self self
@ -139,20 +143,20 @@ impl<Tag> MouseEventHandler<Tag> {
pub fn on_drag( pub fn on_drag(
mut self, mut self,
button: MouseButton, button: MouseButton,
handler: impl Fn(MouseDrag, &mut EventContext) + 'static, handler: impl Fn(MouseDrag, &mut ViewContext<V>) + 'static,
) -> Self { ) -> Self {
self.handlers = self.handlers.on_drag(button, handler); self.handlers = self.handlers.on_drag(button, handler);
self self
} }
pub fn on_hover(mut self, handler: impl Fn(MouseHover, &mut EventContext) + 'static) -> Self { pub fn on_hover(mut self, handler: impl Fn(MouseHover, &mut ViewContext<V>) + 'static) -> Self {
self.handlers = self.handlers.on_hover(handler); self.handlers = self.handlers.on_hover(handler);
self self
} }
pub fn on_scroll( pub fn on_scroll(
mut self, mut self,
handler: impl Fn(MouseScrollWheel, &mut EventContext) + 'static, handler: impl Fn(MouseScrollWheel, &mut ViewContext<V>) + 'static,
) -> Self { ) -> Self {
self.handlers = self.handlers.on_scroll(handler); self.handlers = self.handlers.on_scroll(handler);
self self
@ -176,7 +180,13 @@ impl<Tag> MouseEventHandler<Tag> {
.round_out() .round_out()
} }
fn paint_regions(&self, bounds: RectF, visible_bounds: RectF, cx: &mut PaintContext) { fn paint_regions(
&self,
scene: &mut SceneBuilder,
bounds: RectF,
visible_bounds: RectF,
cx: &mut ViewContext<V>,
) {
let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default(); let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
let hit_bounds = self.hit_bounds(visible_bounds); let hit_bounds = self.hit_bounds(visible_bounds);
@ -200,34 +210,37 @@ impl<Tag> MouseEventHandler<Tag> {
} }
} }
impl<Tag> Element for MouseEventHandler<Tag> { impl<Tag, V: View> Element<V> for MouseEventHandler<Tag, V> {
type LayoutState = (); type LayoutState = ();
type PaintState = (); type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
cx: &mut LayoutContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
(self.child.layout(constraint, cx), ()) (self.child.layout(constraint, view, cx), ())
} }
fn paint( fn paint(
&mut self, &mut self,
scene: &mut SceneBuilder,
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
_: &mut Self::LayoutState, _: &mut Self::LayoutState,
cx: &mut PaintContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
if self.above { if self.above {
self.child.paint(bounds.origin(), visible_bounds, cx); self.child.paint(bounds.origin(), visible_bounds, view, cx);
cx.paint_layer(None, |cx| { cx.paint_layer(None, |cx| {
self.paint_regions(bounds, visible_bounds, cx); self.paint_regions(bounds, visible_bounds, cx);
}); });
} else { } else {
self.paint_regions(bounds, visible_bounds, cx); self.paint_regions(bounds, visible_bounds, cx);
self.child.paint(bounds.origin(), visible_bounds, cx); self.child.paint(bounds.origin(), visible_bounds, view, cx);
} }
} }
@ -238,9 +251,10 @@ impl<Tag> Element for MouseEventHandler<Tag> {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &MeasurementContext, view: &V,
cx: &ViewContext<Self>,
) -> Option<RectF> { ) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx) self.child.rect_for_text_range(range_utf16, view, cx)
} }
fn debug( fn debug(
@ -248,11 +262,12 @@ impl<Tag> Element for MouseEventHandler<Tag> {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &DebugContext, view: &V,
cx: &ViewContext<V>,
) -> serde_json::Value { ) -> serde_json::Value {
json!({ json!({
"type": "MouseEventHandler", "type": "MouseEventHandler",
"child": self.child.debug(cx), "child": self.child.debug(view, cx),
}) })
} }
} }

View file

@ -3,14 +3,12 @@ use std::ops::Range;
use crate::{ use crate::{
geometry::{rect::RectF, vector::Vector2F}, geometry::{rect::RectF, vector::Vector2F},
json::ToJson, json::ToJson,
window::MeasurementContext, Axis, Element, ElementBox, MouseRegion, SceneBuilder, SizeConstraint, View, ViewContext,
Axis, DebugContext, Element, ElementBox, LayoutContext, MouseRegion, PaintContext,
SizeConstraint,
}; };
use serde_json::json; use serde_json::json;
pub struct Overlay { pub struct Overlay<V: View> {
child: ElementBox, child: ElementBox<V>,
anchor_position: Option<Vector2F>, anchor_position: Option<Vector2F>,
anchor_corner: AnchorCorner, anchor_corner: AnchorCorner,
fit_mode: OverlayFitMode, fit_mode: OverlayFitMode,
@ -74,8 +72,8 @@ impl AnchorCorner {
} }
} }
impl Overlay { impl<V: View> Overlay<V> {
pub fn new(child: ElementBox) -> Self { pub fn new(child: ElementBox<V>) -> Self {
Self { Self {
child, child,
anchor_position: None, anchor_position: None,
@ -118,14 +116,15 @@ impl Overlay {
} }
} }
impl Element for Overlay { impl<V: View> Element<V> for Overlay<V> {
type LayoutState = Vector2F; type LayoutState = Vector2F;
type PaintState = (); type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
cx: &mut LayoutContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
let constraint = if self.anchor_position.is_some() { let constraint = if self.anchor_position.is_some() {
SizeConstraint::new(Vector2F::zero(), cx.window_size) SizeConstraint::new(Vector2F::zero(), cx.window_size)
@ -138,10 +137,12 @@ impl Element for Overlay {
fn paint( fn paint(
&mut self, &mut self,
scene: SceneBuilder,
bounds: RectF, bounds: RectF,
_: RectF, _: RectF,
size: &mut Self::LayoutState, size: &mut Self::LayoutState,
cx: &mut PaintContext, view: &mut V,
cx: &mut ViewContext<V>,
) { ) {
let (anchor_position, mut bounds) = match self.position_mode { let (anchor_position, mut bounds) = match self.position_mode {
OverlayPositionMode::Window => { OverlayPositionMode::Window => {
@ -224,8 +225,10 @@ impl Element for Overlay {
} }
self.child.paint( self.child.paint(
scene,
bounds.origin(), bounds.origin(),
RectF::new(Vector2F::zero(), cx.window_size), RectF::new(Vector2F::zero(), cx.window_size),
view,
cx, cx,
); );
}); });
@ -238,9 +241,10 @@ impl Element for Overlay {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &MeasurementContext, view: &V,
cx: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
self.child.rect_for_text_range(range_utf16, cx) self.child.rect_for_text_range(range_utf16, view, cx)
} }
fn debug( fn debug(
@ -248,12 +252,13 @@ impl Element for Overlay {
_: RectF, _: RectF,
_: &Self::LayoutState, _: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &DebugContext, view: &V,
cx: &ViewContext<V>,
) -> serde_json::Value { ) -> serde_json::Value {
json!({ json!({
"type": "Overlay", "type": "Overlay",
"abs_position": self.anchor_position.to_json(), "abs_position": self.anchor_position.to_json(),
"child": self.child.debug(cx), "child": self.child.debug(view, cx),
}) })
} }
} }

View file

@ -1,4 +1,4 @@
use super::{Element, EventContext, LayoutContext, PaintContext, SizeConstraint}; use super::{Element, SizeConstraint};
use crate::{ use crate::{
geometry::{ geometry::{
rect::RectF, rect::RectF,
@ -7,8 +7,7 @@ use crate::{
json::{self, json}, json::{self, json},
platform::ScrollWheelEvent, platform::ScrollWheelEvent,
scene::MouseScrollWheel, scene::MouseScrollWheel,
window::MeasurementContext, ElementBox, MouseRegion, SceneBuilder, View, ViewContext,
ElementBox, MouseRegion, RenderContext, View,
}; };
use json::ToJson; use json::ToJson;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc}; use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@ -38,33 +37,33 @@ struct StateInner {
scroll_to: Option<ScrollTarget>, scroll_to: Option<ScrollTarget>,
} }
pub struct LayoutState { pub struct LayoutState<V: View> {
scroll_max: f32, scroll_max: f32,
item_height: f32, item_height: f32,
items: Vec<ElementBox>, items: Vec<ElementBox<V>>,
} }
pub struct UniformList { pub struct UniformList<V: View> {
state: UniformListState, state: UniformListState,
item_count: usize, item_count: usize,
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
append_items: Box<dyn Fn(Range<usize>, &mut Vec<ElementBox>, &mut LayoutContext)>, append_items: Box<dyn Fn(Range<usize>, &mut Vec<ElementBox<V>>, &mut V, &mut ViewContext<V>)>,
padding_top: f32, padding_top: f32,
padding_bottom: f32, padding_bottom: f32,
get_width_from_item: Option<usize>, get_width_from_item: Option<usize>,
view_id: usize, view_id: usize,
} }
impl UniformList { impl<V: View> UniformList<V> {
pub fn new<F, V>( pub fn new<F>(
state: UniformListState, state: UniformListState,
item_count: usize, item_count: usize,
cx: &mut RenderContext<V>, cx: &mut ViewContext<V>,
append_items: F, append_items: F,
) -> Self ) -> Self
where where
V: View, V: View,
F: 'static + Fn(&mut V, Range<usize>, &mut Vec<ElementBox>, &mut RenderContext<V>), F: 'static + Fn(&mut V, Range<usize>, &mut Vec<ElementBox<V>>, &mut V, &mut ViewContext<V>),
{ {
let handle = cx.handle(); let handle = cx.handle();
Self { Self {
@ -160,14 +159,15 @@ impl UniformList {
} }
} }
impl Element for UniformList { impl<V: View> Element<V> for UniformList<V> {
type LayoutState = LayoutState; type LayoutState = LayoutState<V>;
type PaintState = (); type PaintState = ();
fn layout( fn layout(
&mut self, &mut self,
constraint: SizeConstraint, constraint: SizeConstraint,
cx: &mut LayoutContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> (Vector2F, Self::LayoutState) { ) -> (Vector2F, Self::LayoutState) {
if constraint.max.y().is_infinite() { if constraint.max.y().is_infinite() {
unimplemented!( unimplemented!(
@ -262,7 +262,7 @@ impl Element for UniformList {
} }
for item in &mut items { for item in &mut items {
let item_size = item.layout(item_constraint, cx); let item_size = item.layout(item_constraint, view, cx);
if item_size.x() > size.x() { if item_size.x() > size.x() {
size.set_x(item_size.x()); size.set_x(item_size.x());
} }
@ -280,10 +280,12 @@ impl Element for UniformList {
fn paint( fn paint(
&mut self, &mut self,
scene: &mut SceneBuilder,
bounds: RectF, bounds: RectF,
visible_bounds: RectF, visible_bounds: RectF,
layout: &mut Self::LayoutState, layout: &mut Self::LayoutState,
cx: &mut PaintContext, view: &mut V,
cx: &mut ViewContext<V>,
) -> Self::PaintState { ) -> Self::PaintState {
let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default(); let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
@ -322,7 +324,7 @@ impl Element for UniformList {
); );
for item in &mut layout.items { for item in &mut layout.items {
item.paint(item_origin, visible_bounds, cx); item.paint(scene, item_origin, visible_bounds, view, cx);
item_origin += vec2f(0.0, layout.item_height); item_origin += vec2f(0.0, layout.item_height);
} }
@ -336,12 +338,13 @@ impl Element for UniformList {
_: RectF, _: RectF,
layout: &Self::LayoutState, layout: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &MeasurementContext, view: &V,
cx: &ViewContext<V>,
) -> Option<RectF> { ) -> Option<RectF> {
layout layout
.items .items
.iter() .iter()
.find_map(|child| child.rect_for_text_range(range.clone(), cx)) .find_map(|child| child.rect_for_text_range(range.clone(), view, cx))
} }
fn debug( fn debug(
@ -349,14 +352,15 @@ impl Element for UniformList {
bounds: RectF, bounds: RectF,
layout: &Self::LayoutState, layout: &Self::LayoutState,
_: &Self::PaintState, _: &Self::PaintState,
cx: &crate::DebugContext, view: &V,
cx: &ViewContext<V>,
) -> json::Value { ) -> json::Value {
json!({ json!({
"type": "UniformList", "type": "UniformList",
"bounds": bounds.to_json(), "bounds": bounds.to_json(),
"scroll_max": layout.scroll_max, "scroll_max": layout.scroll_max,
"item_height": layout.item_height, "item_height": layout.item_height,
"items": layout.items.iter().map(|item| item.debug(cx)).collect::<Vec<json::Value>>() "items": layout.items.iter().map(|item| item.debug(view, cx)).collect::<Vec<json::Value>>()
}) })
} }

View file

@ -7,7 +7,9 @@ use crate::{
}, },
platform, platform,
platform::FontSystem, platform::FontSystem,
scene, PaintContext, scene,
window::WindowContext,
AppContext, PaintContext, SceneBuilder,
}; };
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
@ -271,10 +273,11 @@ impl Line {
pub fn paint( pub fn paint(
&self, &self,
scene: &SceneBuilder,
origin: Vector2F, origin: Vector2F,
visible_bounds: RectF, visible_bounds: RectF,
line_height: f32, line_height: f32,
cx: &mut PaintContext, cx: &mut WindowContext,
) { ) {
let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.; let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
let baseline_offset = vec2f(0., padding_top + self.layout.ascent); let baseline_offset = vec2f(0., padding_top + self.layout.ascent);

View file

@ -12,8 +12,8 @@ use gpui::{
actions, actions,
elements::*, elements::*,
platform::{CursorStyle, MouseButton}, platform::{CursorStyle, MouseButton},
Action, AnyViewHandle, AppContext, ElementBox, Entity, ModelContext, ModelHandle, Action, AnyViewHandle, AppContext, Entity, ModelContext, ModelHandle, RenderContext,
RenderContext, Subscription, Task, View, ViewContext, ViewHandle, WeakModelHandle, Subscription, Task, View, ViewContext, ElementBox, ViewHandle, WeakModelHandle,
WeakViewHandle, WeakViewHandle,
}; };
use menu::Confirm; use menu::Confirm;

View file

@ -2,8 +2,8 @@ use gpui::{
actions, actions,
color::Color, color::Color,
elements::{ elements::{
Canvas, Container, ContainerStyle, ElementBox, Flex, Label, Margin, MouseEventHandler, Canvas, Container, ContainerStyle, Flex, Label, Margin, MouseEventHandler, Padding,
Padding, ParentElement, ParentElement, ElementBox,
}, },
fonts::TextStyle, fonts::TextStyle,
AppContext, Border, Element, Entity, ModelHandle, Quad, RenderContext, Task, View, ViewContext, AppContext, Border, Element, Entity, ModelHandle, Quad, RenderContext, Task, View, ViewContext,

View file

@ -1,7 +1,7 @@
use crate::{ItemHandle, Pane}; use crate::{ItemHandle, Pane};
use gpui::{ use gpui::{
elements::*, platform::CursorStyle, platform::MouseButton, Action, AnyViewHandle, AppContext, elements::*, platform::CursorStyle, platform::MouseButton, Action, AnyViewHandle, AppContext,
ElementBox, Entity, RenderContext, View, ViewContext, ViewHandle, WeakViewHandle, Entity, RenderContext, View, ViewContext, ElementBox, ViewHandle, WeakViewHandle,
}; };
use settings::Settings; use settings::Settings;