Merge branch 'main' into welcome

This commit is contained in:
Mikayla
2023-11-27 18:55:23 -08:00
188 changed files with 6979 additions and 6715 deletions
+21 -2
View File
@@ -10,6 +10,7 @@ pub use entity_map::*;
pub use model_context::*;
use refineable::Refineable;
use smallvec::SmallVec;
use smol::future::FutureExt;
#[cfg(any(test, feature = "test-support"))]
pub use test_context::*;
@@ -579,7 +580,7 @@ impl AppContext {
.windows
.iter()
.filter_map(|(_, window)| {
let window = window.as_ref().unwrap();
let window = window.as_ref()?;
if window.dirty {
Some(window.handle.clone())
} else {
@@ -983,6 +984,22 @@ impl AppContext {
pub fn all_action_names(&self) -> &[SharedString] {
self.actions.all_action_names()
}
pub fn on_app_quit<Fut>(
&mut self,
mut on_quit: impl FnMut(&mut AppContext) -> Fut + 'static,
) -> Subscription
where
Fut: 'static + Future<Output = ()>,
{
self.quit_observers.insert(
(),
Box::new(move |cx| {
let future = on_quit(cx);
async move { future.await }.boxed_local()
}),
)
}
}
impl Context for AppContext {
@@ -1032,7 +1049,9 @@ impl Context for AppContext {
let root_view = window.root_view.clone().unwrap();
let result = update(root_view, &mut WindowContext::new(cx, &mut window));
if !window.removed {
if window.removed {
cx.windows.remove(handle.id);
} else {
cx.windows
.get_mut(handle.id)
.ok_or_else(|| anyhow!("window not found"))?
+4 -4
View File
@@ -1,7 +1,7 @@
use crate::{
AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, Context, FocusableView,
ForegroundExecutor, Manager, Model, ModelContext, Render, Result, Task, View, ViewContext,
VisualContext, WindowContext, WindowHandle,
AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, Context, DismissEvent,
FocusableView, ForegroundExecutor, Model, ModelContext, Render, Result, Task, View,
ViewContext, VisualContext, WindowContext, WindowHandle,
};
use anyhow::{anyhow, Context as _};
use derive_more::{Deref, DerefMut};
@@ -326,7 +326,7 @@ impl VisualContext for AsyncWindowContext {
V: crate::ManagedView,
{
self.window.update(self, |_, cx| {
view.update(cx, |_, cx| cx.emit(Manager::Dismiss))
view.update(cx, |_, cx| cx.emit(DismissEvent::Dismiss))
})
}
}
+1 -1
View File
@@ -611,7 +611,7 @@ impl<'a> VisualContext for VisualTestContext<'a> {
{
self.window
.update(self.cx, |_, cx| {
view.update(cx, |_, cx| cx.emit(crate::Manager::Dismiss))
view.update(cx, |_, cx| cx.emit(crate::DismissEvent::Dismiss))
})
.unwrap()
}
+31 -34
View File
@@ -12,15 +12,15 @@ pub trait Render: 'static + Sized {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element;
}
pub trait RenderOnce: Sized {
pub trait IntoElement: Sized {
type Element: Element + 'static;
fn element_id(&self) -> Option<ElementId>;
fn render_once(self) -> Self::Element;
fn into_element(self) -> Self::Element;
fn render_into_any(self) -> AnyElement {
self.render_once().into_any()
fn into_any_element(self) -> AnyElement {
self.into_element().into_any()
}
fn draw<T, R>(
@@ -33,7 +33,7 @@ pub trait RenderOnce: Sized {
where
T: Clone + Default + Debug + Into<AvailableSpace>,
{
let element = self.render_once();
let element = self.into_element();
let element_id = element.element_id();
let element = DrawableElement {
element: Some(element),
@@ -57,7 +57,7 @@ pub trait RenderOnce: Sized {
fn map<U>(self, f: impl FnOnce(Self) -> U) -> U
where
Self: Sized,
U: RenderOnce,
U: IntoElement,
{
f(self)
}
@@ -83,7 +83,7 @@ pub trait RenderOnce: Sized {
}
}
pub trait Element: 'static + RenderOnce {
pub trait Element: 'static + IntoElement {
type State: 'static;
fn layout(
@@ -99,30 +99,30 @@ pub trait Element: 'static + RenderOnce {
}
}
pub trait Component: 'static {
type Rendered: RenderOnce;
pub trait RenderOnce: 'static {
type Rendered: IntoElement;
fn render(self, cx: &mut WindowContext) -> Self::Rendered;
}
pub struct CompositeElement<C> {
pub struct Component<C> {
component: Option<C>,
}
pub struct CompositeElementState<C: Component> {
rendered_element: Option<<C::Rendered as RenderOnce>::Element>,
rendered_element_state: <<C::Rendered as RenderOnce>::Element as Element>::State,
pub struct CompositeElementState<C: RenderOnce> {
rendered_element: Option<<C::Rendered as IntoElement>::Element>,
rendered_element_state: <<C::Rendered as IntoElement>::Element as Element>::State,
}
impl<C> CompositeElement<C> {
impl<C> Component<C> {
pub fn new(component: C) -> Self {
CompositeElement {
Component {
component: Some(component),
}
}
}
impl<C: Component> Element for CompositeElement<C> {
impl<C: RenderOnce> Element for Component<C> {
type State = CompositeElementState<C>;
fn layout(
@@ -130,7 +130,7 @@ impl<C: Component> Element for CompositeElement<C> {
state: Option<Self::State>,
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
let mut element = self.component.take().unwrap().render(cx).render_once();
let mut element = self.component.take().unwrap().render(cx).into_element();
let (layout_id, state) = element.layout(state.map(|s| s.rendered_element_state), cx);
let state = CompositeElementState {
rendered_element: Some(element),
@@ -148,14 +148,14 @@ impl<C: Component> Element for CompositeElement<C> {
}
}
impl<C: Component> RenderOnce for CompositeElement<C> {
impl<C: RenderOnce> IntoElement for Component<C> {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
None
}
fn render_once(self) -> Self::Element {
fn into_element(self) -> Self::Element {
self
}
}
@@ -166,23 +166,20 @@ pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
pub trait ParentElement {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]>;
fn child(mut self, child: impl RenderOnce) -> Self
fn child(mut self, child: impl IntoElement) -> Self
where
Self: Sized,
{
self.children_mut().push(child.render_once().into_any());
self.children_mut().push(child.into_element().into_any());
self
}
fn children(mut self, children: impl IntoIterator<Item = impl RenderOnce>) -> Self
fn children(mut self, children: impl IntoIterator<Item = impl IntoElement>) -> Self
where
Self: Sized,
{
self.children_mut().extend(
children
.into_iter()
.map(|child| child.render_once().into_any()),
);
self.children_mut()
.extend(children.into_iter().map(|child| child.into_any_element()));
self
}
}
@@ -432,10 +429,6 @@ impl AnyElement {
AnyElement(Box::new(Some(DrawableElement::new(element))) as Box<dyn ElementObject>)
}
pub fn element_id(&self) -> Option<ElementId> {
self.0.element_id()
}
pub fn layout(&mut self, cx: &mut WindowContext) -> LayoutId {
self.0.layout(cx)
}
@@ -467,6 +460,10 @@ impl AnyElement {
pub fn into_any(self) -> AnyElement {
AnyElement::new(self)
}
pub fn inner_id(&self) -> Option<ElementId> {
self.0.element_id()
}
}
impl Element for AnyElement {
@@ -486,14 +483,14 @@ impl Element for AnyElement {
}
}
impl RenderOnce for AnyElement {
impl IntoElement for AnyElement {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
AnyElement::element_id(self)
None
}
fn render_once(self) -> Self::Element {
fn into_element(self) -> Self::Element {
self
}
}
+9 -9
View File
@@ -1,9 +1,9 @@
use crate::{
point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusEvent, FocusHandle,
KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent,
MouseUpEvent, ParentElement, Pixels, Point, Render, RenderOnce, ScrollWheelEvent, SharedString,
Size, Style, StyleRefinement, Styled, Task, View, Visibility, WindowContext,
IntoElement, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent,
MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent,
SharedString, Size, Style, StyleRefinement, Styled, Task, View, Visibility, WindowContext,
};
use collections::HashMap;
use refineable::Refineable;
@@ -666,14 +666,14 @@ impl Element for Div {
}
}
impl RenderOnce for Div {
impl IntoElement for Div {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
self.interactivity.element_id.clone()
}
fn render_once(self) -> Self::Element {
fn into_element(self) -> Self::Element {
self
}
}
@@ -1278,7 +1278,7 @@ where
}
}
impl<E> RenderOnce for Focusable<E>
impl<E> IntoElement for Focusable<E>
where
E: Element,
{
@@ -1288,7 +1288,7 @@ where
self.element.element_id()
}
fn render_once(self) -> Self::Element {
fn into_element(self) -> Self::Element {
self.element
}
}
@@ -1352,7 +1352,7 @@ where
}
}
impl<E> RenderOnce for Stateful<E>
impl<E> IntoElement for Stateful<E>
where
E: Element,
{
@@ -1362,7 +1362,7 @@ where
self.element.element_id()
}
fn render_once(self) -> Self::Element {
fn into_element(self) -> Self::Element {
self
}
}
+62 -28
View File
@@ -1,30 +1,59 @@
use std::sync::Arc;
use crate::{
Bounds, Element, InteractiveElement, InteractiveElementState, Interactivity, LayoutId, Pixels,
RenderOnce, SharedString, StyleRefinement, Styled, WindowContext,
Bounds, Element, ImageData, InteractiveElement, InteractiveElementState, Interactivity,
IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled, WindowContext,
};
use futures::FutureExt;
use util::ResultExt;
#[derive(Clone, Debug)]
pub enum ImageSource {
/// Image content will be loaded from provided URI at render time.
Uri(SharedString),
Data(Arc<ImageData>),
}
impl From<SharedString> for ImageSource {
fn from(value: SharedString) -> Self {
Self::Uri(value)
}
}
impl From<Arc<ImageData>> for ImageSource {
fn from(value: Arc<ImageData>) -> Self {
Self::Data(value)
}
}
pub struct Img {
interactivity: Interactivity,
uri: Option<SharedString>,
source: Option<ImageSource>,
grayscale: bool,
}
pub fn img() -> Img {
Img {
interactivity: Interactivity::default(),
uri: None,
source: None,
grayscale: false,
}
}
impl Img {
pub fn uri(mut self, uri: impl Into<SharedString>) -> Self {
self.uri = Some(uri.into());
self.source = Some(ImageSource::from(uri.into()));
self
}
pub fn data(mut self, data: Arc<ImageData>) -> Self {
self.source = Some(ImageSource::from(data));
self
}
pub fn source(mut self, source: impl Into<ImageSource>) -> Self {
self.source = Some(source.into());
self
}
pub fn grayscale(mut self, grayscale: bool) -> Self {
self.grayscale = grayscale;
self
@@ -58,42 +87,47 @@ impl Element for Img {
|style, _scroll_offset, cx| {
let corner_radii = style.corner_radii;
if let Some(uri) = self.uri.clone() {
// eprintln!(">>> image_cache.get({uri}");
let image_future = cx.image_cache.get(uri.clone());
// eprintln!("<<< image_cache.get({uri}");
if let Some(data) = image_future
.clone()
.now_or_never()
.and_then(|result| result.ok())
{
let corner_radii = corner_radii.to_pixels(bounds.size, cx.rem_size());
cx.with_z_index(1, |cx| {
cx.paint_image(bounds, corner_radii, data, self.grayscale)
.log_err()
});
} else {
cx.spawn(|mut cx| async move {
if image_future.await.ok().is_some() {
cx.on_next_frame(|cx| cx.notify());
if let Some(source) = self.source {
let image = match source {
ImageSource::Uri(uri) => {
let image_future = cx.image_cache.get(uri.clone());
if let Some(data) = image_future
.clone()
.now_or_never()
.and_then(|result| result.ok())
{
data
} else {
cx.spawn(|mut cx| async move {
if image_future.await.ok().is_some() {
cx.on_next_frame(|cx| cx.notify());
}
})
.detach();
return;
}
})
.detach()
}
}
ImageSource::Data(image) => image,
};
let corner_radii = corner_radii.to_pixels(bounds.size, cx.rem_size());
cx.with_z_index(1, |cx| {
cx.paint_image(bounds, corner_radii, image, self.grayscale)
.log_err()
});
}
},
)
}
}
impl RenderOnce for Img {
impl IntoElement for Img {
type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> {
self.interactivity.element_id.clone()
}
fn render_once(self) -> Self::Element {
fn into_element(self) -> Self::Element {
self
}
}
+9 -7
View File
@@ -2,8 +2,8 @@ use smallvec::SmallVec;
use taffy::style::{Display, Position};
use crate::{
point, AnyElement, BorrowWindow, Bounds, Element, LayoutId, ParentElement, Pixels, Point,
RenderOnce, Size, Style, WindowContext,
point, AnyElement, BorrowWindow, Bounds, Element, IntoElement, LayoutId, ParentElement, Pixels,
Point, Size, Style, WindowContext,
};
pub struct OverlayState {
@@ -144,21 +144,23 @@ impl Element for Overlay {
}
cx.with_element_offset(desired.origin - bounds.origin, |cx| {
for child in self.children {
child.paint(cx);
}
cx.break_content_mask(|cx| {
for child in self.children {
child.paint(cx);
}
})
})
}
}
impl RenderOnce for Overlay {
impl IntoElement for Overlay {
type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> {
None
}
fn render_once(self) -> Self::Element {
fn into_element(self) -> Self::Element {
self
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
use crate::{
Bounds, Element, ElementId, InteractiveElement, InteractiveElementState, Interactivity,
LayoutId, Pixels, RenderOnce, SharedString, StyleRefinement, Styled, WindowContext,
IntoElement, LayoutId, Pixels, SharedString, StyleRefinement, Styled, WindowContext,
};
use util::ResultExt;
@@ -49,14 +49,14 @@ impl Element for Svg {
}
}
impl RenderOnce for Svg {
impl IntoElement for Svg {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
self.interactivity.element_id.clone()
}
fn render_once(self) -> Self::Element {
fn into_element(self) -> Self::Element {
self
}
}
+138 -36
View File
@@ -1,11 +1,11 @@
use crate::{
Bounds, Element, ElementId, LayoutId, Pixels, RenderOnce, SharedString, Size, TextRun,
WindowContext, WrappedLine,
Bounds, DispatchPhase, Element, ElementId, IntoElement, LayoutId, MouseDownEvent, MouseUpEvent,
Pixels, Point, SharedString, Size, TextRun, WhiteSpace, WindowContext, WrappedLine,
};
use anyhow::anyhow;
use parking_lot::{Mutex, MutexGuard};
use smallvec::SmallVec;
use std::{cell::Cell, rc::Rc, sync::Arc};
use std::{cell::Cell, ops::Range, rc::Rc, sync::Arc};
use util::ResultExt;
impl Element for &'static str {
@@ -26,14 +26,14 @@ impl Element for &'static str {
}
}
impl RenderOnce for &'static str {
impl IntoElement for &'static str {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
None
}
fn render_once(self) -> Self::Element {
fn into_element(self) -> Self::Element {
self
}
}
@@ -57,35 +57,40 @@ impl Element for SharedString {
}
}
impl RenderOnce for SharedString {
impl IntoElement for SharedString {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
None
}
fn render_once(self) -> Self::Element {
fn into_element(self) -> Self::Element {
self
}
}
/// Renders text with runs of different styles.
///
/// Callers are responsible for setting the correct style for each run.
/// For text with a uniform style, you can usually avoid calling this constructor
/// and just pass text directly.
pub struct StyledText {
text: SharedString,
runs: Option<Vec<TextRun>>,
}
impl StyledText {
/// Renders text with runs of different styles.
///
/// Callers are responsible for setting the correct style for each run.
/// For text with a uniform style, you can usually avoid calling this constructor
/// and just pass text directly.
pub fn new(text: SharedString, runs: Vec<TextRun>) -> Self {
pub fn new(text: impl Into<SharedString>) -> Self {
StyledText {
text,
runs: Some(runs),
text: text.into(),
runs: None,
}
}
pub fn with_runs(mut self, runs: Vec<TextRun>) -> Self {
self.runs = Some(runs);
self
}
}
impl Element for StyledText {
@@ -106,14 +111,14 @@ impl Element for StyledText {
}
}
impl RenderOnce for StyledText {
impl IntoElement for StyledText {
type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> {
None
}
fn render_once(self) -> Self::Element {
fn into_element(self) -> Self::Element {
self
}
}
@@ -159,10 +164,14 @@ impl TextState {
let element_state = self.clone();
move |known_dimensions, available_space| {
let wrap_width = known_dimensions.width.or(match available_space.width {
crate::AvailableSpace::Definite(x) => Some(x),
_ => None,
});
let wrap_width = if text_style.white_space == WhiteSpace::Normal {
known_dimensions.width.or(match available_space.width {
crate::AvailableSpace::Definite(x) => Some(x),
_ => None,
})
} else {
None
};
if let Some(text_state) = element_state.0.lock().as_ref() {
if text_state.size.is_some()
@@ -174,10 +183,7 @@ impl TextState {
let Some(lines) = text_system
.shape_text(
&text,
font_size,
&runs[..],
wrap_width, // Wrap if we know the width.
&text, font_size, &runs, wrap_width, // Wrap if we know the width.
)
.log_err()
else {
@@ -194,7 +200,7 @@ impl TextState {
for line in &lines {
let line_size = line.size(line_height);
size.height += line_size.height;
size.width = size.width.max(line_size.width);
size.width = size.width.max(line_size.width).ceil();
}
element_state.lock().replace(TextStateInner {
@@ -225,16 +231,77 @@ impl TextState {
line_origin.y += line.size(line_height).height;
}
}
fn index_for_position(&self, bounds: Bounds<Pixels>, position: Point<Pixels>) -> Option<usize> {
if !bounds.contains_point(&position) {
return None;
}
let element_state = self.lock();
let element_state = element_state
.as_ref()
.expect("measurement has not been performed");
let line_height = element_state.line_height;
let mut line_origin = bounds.origin;
let mut line_start_ix = 0;
for line in &element_state.lines {
let line_bottom = line_origin.y + line.size(line_height).height;
if position.y > line_bottom {
line_origin.y = line_bottom;
line_start_ix += line.len() + 1;
} else {
let position_within_line = position - line_origin;
let index_within_line =
line.index_for_position(position_within_line, line_height)?;
return Some(line_start_ix + index_within_line);
}
}
None
}
}
struct InteractiveText {
pub struct InteractiveText {
element_id: ElementId,
text: StyledText,
click_listener: Option<Box<dyn Fn(InteractiveTextClickEvent, &mut WindowContext<'_>)>>,
}
struct InteractiveTextState {
struct InteractiveTextClickEvent {
mouse_down_index: usize,
mouse_up_index: usize,
}
pub struct InteractiveTextState {
text_state: TextState,
clicked_range_ixs: Rc<Cell<SmallVec<[usize; 1]>>>,
mouse_down_index: Rc<Cell<Option<usize>>>,
}
impl InteractiveText {
pub fn new(id: impl Into<ElementId>, text: StyledText) -> Self {
Self {
element_id: id.into(),
text,
click_listener: None,
}
}
pub fn on_click(
mut self,
ranges: Vec<Range<usize>>,
listener: impl Fn(usize, &mut WindowContext<'_>) + 'static,
) -> Self {
self.click_listener = Some(Box::new(move |event, cx| {
for (range_ix, range) in ranges.iter().enumerate() {
if range.contains(&event.mouse_down_index) && range.contains(&event.mouse_up_index)
{
listener(range_ix, cx);
}
}
}));
self
}
}
impl Element for InteractiveText {
@@ -246,39 +313,74 @@ impl Element for InteractiveText {
cx: &mut WindowContext,
) -> (LayoutId, Self::State) {
if let Some(InteractiveTextState {
text_state,
clicked_range_ixs,
mouse_down_index, ..
}) = state
{
let (layout_id, text_state) = self.text.layout(Some(text_state), cx);
let (layout_id, text_state) = self.text.layout(None, cx);
let element_state = InteractiveTextState {
text_state,
clicked_range_ixs,
mouse_down_index,
};
(layout_id, element_state)
} else {
let (layout_id, text_state) = self.text.layout(None, cx);
let element_state = InteractiveTextState {
text_state,
clicked_range_ixs: Rc::default(),
mouse_down_index: Rc::default(),
};
(layout_id, element_state)
}
}
fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
if let Some(click_listener) = self.click_listener {
let text_state = state.text_state.clone();
let mouse_down = state.mouse_down_index.clone();
if let Some(mouse_down_index) = mouse_down.get() {
cx.on_mouse_event(move |event: &MouseUpEvent, phase, cx| {
if phase == DispatchPhase::Bubble {
if let Some(mouse_up_index) =
text_state.index_for_position(bounds, event.position)
{
click_listener(
InteractiveTextClickEvent {
mouse_down_index,
mouse_up_index,
},
cx,
)
}
mouse_down.take();
cx.notify();
}
});
} else {
cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
if phase == DispatchPhase::Bubble {
if let Some(mouse_down_index) =
text_state.index_for_position(bounds, event.position)
{
mouse_down.set(Some(mouse_down_index));
cx.notify();
}
}
});
}
}
self.text.paint(bounds, &mut state.text_state, cx)
}
}
impl RenderOnce for InteractiveText {
impl IntoElement for InteractiveText {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
Some(self.element_id.clone())
}
fn render_once(self) -> Self::Element {
fn into_element(self) -> Self::Element {
self
}
}
+30 -31
View File
@@ -1,7 +1,7 @@
use crate::{
point, px, size, AnyElement, AvailableSpace, Bounds, Element, ElementId, InteractiveElement,
InteractiveElementState, Interactivity, LayoutId, Pixels, Point, Render, RenderOnce, Size,
StyleRefinement, Styled, View, ViewContext, WindowContext,
point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element,
ElementId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId,
Pixels, Point, Render, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
};
use smallvec::SmallVec;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@@ -9,7 +9,7 @@ use taffy::style::Overflow;
/// uniform_list provides lazy rendering for a set of items that are of uniform height.
/// When rendered into a container with overflow-y: hidden and a fixed (or max) height,
/// uniform_list will only render the visibile subset of items.
/// uniform_list will only render the visible subset of items.
pub fn uniform_list<I, R, V>(
view: View<V>,
id: I,
@@ -18,30 +18,30 @@ pub fn uniform_list<I, R, V>(
) -> UniformList
where
I: Into<ElementId>,
R: RenderOnce,
R: IntoElement,
V: Render,
{
let id = id.into();
let mut style = StyleRefinement::default();
style.overflow.y = Some(Overflow::Hidden);
let mut base_style = StyleRefinement::default();
base_style.overflow.y = Some(Overflow::Scroll);
let render_range = move |range, cx: &mut WindowContext| {
view.update(cx, |this, cx| {
f(this, range, cx)
.into_iter()
.map(|component| component.render_into_any())
.map(|component| component.into_any_element())
.collect()
})
};
UniformList {
id: id.clone(),
style,
item_count,
item_to_measure_index: 0,
render_items: Box::new(render_range),
interactivity: Interactivity {
element_id: Some(id.into()),
base_style,
..Default::default()
},
scroll_handle: None,
@@ -50,7 +50,6 @@ where
pub struct UniformList {
id: ElementId,
style: StyleRefinement,
item_count: usize,
item_to_measure_index: usize,
render_items:
@@ -91,7 +90,7 @@ impl UniformListScrollHandle {
impl Styled for UniformList {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.style
&mut self.interactivity.base_style
}
}
@@ -211,31 +210,31 @@ impl Element for UniformList {
scroll_offset: shared_scroll_offset,
});
}
let visible_item_count = if item_height > px(0.) {
(padded_bounds.size.height / item_height).ceil() as usize + 1
} else {
0
};
let first_visible_element_ix =
(-scroll_offset.y / item_height).floor() as usize;
let last_visible_element_ix =
((-scroll_offset.y + padded_bounds.size.height) / item_height).ceil()
as usize;
let visible_range = first_visible_element_ix
..cmp::min(
first_visible_element_ix + visible_item_count,
self.item_count,
);
..cmp::min(last_visible_element_ix, self.item_count);
let items = (self.render_items)(visible_range.clone(), cx);
cx.with_z_index(1, |cx| {
for (item, ix) in items.into_iter().zip(visible_range) {
let item_origin = padded_bounds.origin
+ point(px(0.), item_height * ix + scroll_offset.y);
let available_space = size(
AvailableSpace::Definite(padded_bounds.size.width),
AvailableSpace::Definite(item_height),
);
item.draw(item_origin, available_space, cx);
}
let content_mask = ContentMask {
bounds: padded_bounds,
};
cx.with_content_mask(Some(content_mask), |cx| {
for (item, ix) in items.into_iter().zip(visible_range) {
let item_origin = padded_bounds.origin
+ point(px(0.), item_height * ix + scroll_offset.y);
let available_space = size(
AvailableSpace::Definite(padded_bounds.size.width),
AvailableSpace::Definite(item_height),
);
item.draw(item_origin, available_space, cx);
}
});
});
}
})
@@ -244,14 +243,14 @@ impl Element for UniformList {
}
}
impl RenderOnce for UniformList {
impl IntoElement for UniformList {
type Element = Self;
fn element_id(&self) -> Option<crate::ElementId> {
Some(self.id.clone())
}
fn render_once(self) -> Self::Element {
fn into_element(self) -> Self::Element {
self
}
}
+6 -6
View File
@@ -1,6 +1,6 @@
use crate::{
div, point, Div, Element, FocusHandle, Keystroke, Modifiers, Pixels, Point, Render, RenderOnce,
ViewContext,
div, point, Div, Element, FocusHandle, IntoElement, Keystroke, Modifiers, Pixels, Point,
Render, ViewContext,
};
use smallvec::SmallVec;
use std::{any::Any, fmt::Debug, marker::PhantomData, ops::Deref, path::PathBuf};
@@ -64,7 +64,7 @@ pub struct Drag<S, R, V, E>
where
R: Fn(&mut V, &mut ViewContext<V>) -> E,
V: 'static,
E: RenderOnce,
E: IntoElement,
{
pub state: S,
pub render_drag_handle: R,
@@ -286,8 +286,8 @@ pub struct FocusEvent {
#[cfg(test)]
mod test {
use crate::{
self as gpui, div, Div, FocusHandle, InteractiveElement, KeyBinding, Keystroke,
ParentElement, Render, RenderOnce, Stateful, TestAppContext, VisualContext,
self as gpui, div, Div, FocusHandle, InteractiveElement, IntoElement, KeyBinding,
Keystroke, ParentElement, Render, Stateful, TestAppContext, VisualContext,
};
struct TestView {
@@ -315,7 +315,7 @@ mod test {
div()
.key_context("nested")
.track_focus(&self.focus_handle)
.render_once(),
.into_element(),
),
)
}
+3
View File
@@ -683,6 +683,9 @@ impl Drop for MacWindow {
this.executor
.spawn(async move {
unsafe {
// todo!() this panic()s when you click the red close button
// unless should_close returns false.
// (luckliy in zed it always returns false)
window.close();
}
})
+3 -3
View File
@@ -1,5 +1,5 @@
pub use crate::{
BorrowAppContext, BorrowWindow, Component, Context, Element, FocusableElement,
InteractiveElement, ParentElement, Refineable, Render, RenderOnce, StatefulInteractiveElement,
Styled, VisualContext,
BorrowAppContext, BorrowWindow, Context, Element, FocusableElement, InteractiveElement,
IntoElement, ParentElement, Refineable, Render, RenderOnce, StatefulInteractiveElement, Styled,
VisualContext,
};
+182 -2
View File
@@ -1,9 +1,12 @@
use std::{iter, mem, ops::Range};
use crate::{
black, phi, point, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds, ContentMask,
Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement, Font,
FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
SharedString, Size, SizeRefinement, Styled, TextRun, WindowContext,
};
use collections::HashSet;
use refineable::{Cascade, Refineable};
use smallvec::SmallVec;
pub use taffy::style::{
@@ -128,6 +131,13 @@ pub struct BoxShadow {
pub spread_radius: Pixels,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum WhiteSpace {
#[default]
Normal,
Nowrap,
}
#[derive(Refineable, Clone, Debug)]
#[refineable(Debug)]
pub struct TextStyle {
@@ -138,7 +148,9 @@ pub struct TextStyle {
pub line_height: DefiniteLength,
pub font_weight: FontWeight,
pub font_style: FontStyle,
pub background_color: Option<Hsla>,
pub underline: Option<UnderlineStyle>,
pub white_space: WhiteSpace,
}
impl Default for TextStyle {
@@ -151,13 +163,16 @@ impl Default for TextStyle {
line_height: phi(),
font_weight: FontWeight::default(),
font_style: FontStyle::default(),
background_color: None,
underline: None,
white_space: WhiteSpace::Normal,
}
}
}
impl TextStyle {
pub fn highlight(mut self, style: HighlightStyle) -> Self {
pub fn highlight(mut self, style: impl Into<HighlightStyle>) -> Self {
let style = style.into();
if let Some(weight) = style.font_weight {
self.font_weight = weight;
}
@@ -173,6 +188,10 @@ impl TextStyle {
self.color.fade_out(factor);
}
if let Some(background_color) = style.background_color {
self.background_color = Some(background_color);
}
if let Some(underline) = style.underline {
self.underline = Some(underline);
}
@@ -203,7 +222,7 @@ impl TextStyle {
style: self.font_style,
},
color: self.color,
background_color: None,
background_color: self.background_color,
underline: self.underline.clone(),
}
}
@@ -214,6 +233,7 @@ pub struct HighlightStyle {
pub color: Option<Hsla>,
pub font_weight: Option<FontWeight>,
pub font_style: Option<FontStyle>,
pub background_color: Option<Hsla>,
pub underline: Option<UnderlineStyle>,
pub fade_out: Option<f32>,
}
@@ -432,6 +452,7 @@ impl From<&TextStyle> for HighlightStyle {
color: Some(other.color),
font_weight: Some(other.font_weight),
font_style: Some(other.font_style),
background_color: other.background_color,
underline: other.underline.clone(),
fade_out: None,
}
@@ -458,6 +479,10 @@ impl HighlightStyle {
self.font_style = other.font_style;
}
if other.background_color.is_some() {
self.background_color = other.background_color;
}
if other.underline.is_some() {
self.underline = other.underline;
}
@@ -481,6 +506,24 @@ impl From<Hsla> for HighlightStyle {
}
}
impl From<FontWeight> for HighlightStyle {
fn from(font_weight: FontWeight) -> Self {
Self {
font_weight: Some(font_weight),
..Default::default()
}
}
}
impl From<FontStyle> for HighlightStyle {
fn from(font_style: FontStyle) -> Self {
Self {
font_style: Some(font_style),
..Default::default()
}
}
}
impl From<Rgba> for HighlightStyle {
fn from(color: Rgba) -> Self {
Self {
@@ -489,3 +532,140 @@ impl From<Rgba> for HighlightStyle {
}
}
}
pub fn combine_highlights(
a: impl IntoIterator<Item = (Range<usize>, HighlightStyle)>,
b: impl IntoIterator<Item = (Range<usize>, HighlightStyle)>,
) -> impl Iterator<Item = (Range<usize>, HighlightStyle)> {
let mut endpoints = Vec::new();
let mut highlights = Vec::new();
for (range, highlight) in a.into_iter().chain(b) {
if !range.is_empty() {
let highlight_id = highlights.len();
endpoints.push((range.start, highlight_id, true));
endpoints.push((range.end, highlight_id, false));
highlights.push(highlight);
}
}
endpoints.sort_unstable_by_key(|(position, _, _)| *position);
let mut endpoints = endpoints.into_iter().peekable();
let mut active_styles = HashSet::default();
let mut ix = 0;
iter::from_fn(move || {
while let Some((endpoint_ix, highlight_id, is_start)) = endpoints.peek() {
let prev_index = mem::replace(&mut ix, *endpoint_ix);
if ix > prev_index && !active_styles.is_empty() {
let mut current_style = HighlightStyle::default();
for highlight_id in &active_styles {
current_style.highlight(highlights[*highlight_id]);
}
return Some((prev_index..ix, current_style));
}
if *is_start {
active_styles.insert(*highlight_id);
} else {
active_styles.remove(highlight_id);
}
endpoints.next();
}
None
})
}
#[cfg(test)]
mod tests {
use crate::{blue, green, red, yellow};
use super::*;
#[test]
fn test_combine_highlights() {
assert_eq!(
combine_highlights(
[
(0..5, green().into()),
(4..10, FontWeight::BOLD.into()),
(15..20, yellow().into()),
],
[
(2..6, FontStyle::Italic.into()),
(1..3, blue().into()),
(21..23, red().into()),
]
)
.collect::<Vec<_>>(),
[
(
0..1,
HighlightStyle {
color: Some(green()),
..Default::default()
}
),
(
1..2,
HighlightStyle {
color: Some(blue()),
..Default::default()
}
),
(
2..3,
HighlightStyle {
color: Some(blue()),
font_style: Some(FontStyle::Italic),
..Default::default()
}
),
(
3..4,
HighlightStyle {
color: Some(green()),
font_style: Some(FontStyle::Italic),
..Default::default()
}
),
(
4..5,
HighlightStyle {
color: Some(green()),
font_weight: Some(FontWeight::BOLD),
font_style: Some(FontStyle::Italic),
..Default::default()
}
),
(
5..6,
HighlightStyle {
font_weight: Some(FontWeight::BOLD),
font_style: Some(FontStyle::Italic),
..Default::default()
}
),
(
6..10,
HighlightStyle {
font_weight: Some(FontWeight::BOLD),
..Default::default()
}
),
(
15..20,
HighlightStyle {
color: Some(yellow()),
..Default::default()
}
),
(
21..23,
HighlightStyle {
color: Some(red()),
..Default::default()
}
)
]
);
}
}
+26 -1
View File
@@ -1,7 +1,7 @@
use crate::{
self as gpui, hsla, point, px, relative, rems, AbsoluteLength, AlignItems, CursorStyle,
DefiniteLength, Display, Fill, FlexDirection, Hsla, JustifyContent, Length, Position,
SharedString, StyleRefinement, Visibility,
SharedString, StyleRefinement, Visibility, WhiteSpace,
};
use crate::{BoxShadow, TextStyleRefinement};
use smallvec::{smallvec, SmallVec};
@@ -101,6 +101,24 @@ pub trait Styled: Sized {
self
}
/// Sets the whitespace of the element to `normal`.
/// [Docs](https://tailwindcss.com/docs/whitespace#normal)
fn whitespace_normal(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.white_space = Some(WhiteSpace::Normal);
self
}
/// Sets the whitespace of the element to `nowrap`.
/// [Docs](https://tailwindcss.com/docs/whitespace#nowrap)
fn whitespace_nowrap(mut self) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.white_space = Some(WhiteSpace::Nowrap);
self
}
/// Sets the flex direction of the element to `column`.
/// [Docs](https://tailwindcss.com/docs/flex-direction#column)
fn flex_col(mut self) -> Self {
@@ -343,6 +361,13 @@ pub trait Styled: Sized {
self
}
fn text_bg(mut self, bg: impl Into<Hsla>) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.background_color = Some(bg.into());
self
}
fn text_size(mut self, size: impl Into<AbsoluteLength>) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
+18 -3
View File
@@ -196,7 +196,10 @@ impl TextSystem {
let mut decoration_runs = SmallVec::<[DecorationRun; 32]>::new();
for run in runs {
if let Some(last_run) = decoration_runs.last_mut() {
if last_run.color == run.color && last_run.underline == run.underline {
if last_run.color == run.color
&& last_run.underline == run.underline
&& last_run.background_color == run.background_color
{
last_run.len += run.len as u32;
continue;
}
@@ -204,6 +207,7 @@ impl TextSystem {
decoration_runs.push(DecorationRun {
len: run.len as u32,
color: run.color,
background_color: run.background_color,
underline: run.underline.clone(),
});
}
@@ -254,13 +258,16 @@ impl TextSystem {
}
if decoration_runs.last().map_or(false, |last_run| {
last_run.color == run.color && last_run.underline == run.underline
last_run.color == run.color
&& last_run.underline == run.underline
&& last_run.background_color == run.background_color
}) {
decoration_runs.last_mut().unwrap().len += run_len_within_line as u32;
} else {
decoration_runs.push(DecorationRun {
len: run_len_within_line as u32,
color: run.color,
background_color: run.background_color,
underline: run.underline.clone(),
});
}
@@ -283,7 +290,15 @@ impl TextSystem {
text: SharedString::from(line_text),
});
line_start = line_end + 1; // Skip `\n` character.
// Skip `\n` character.
line_start = line_end + 1;
if let Some(run) = runs.peek_mut() {
run.len = run.len.saturating_sub(1);
if run.len == 0 {
runs.next();
}
}
font_runs.clear();
}
+74 -14
View File
@@ -1,6 +1,7 @@
use crate::{
black, point, px, BorrowWindow, Bounds, Hsla, LineLayout, Pixels, Point, Result, SharedString,
UnderlineStyle, WindowContext, WrapBoundary, WrappedLineLayout,
black, point, px, size, transparent_black, BorrowWindow, Bounds, Corners, Edges, Hsla,
LineLayout, Pixels, Point, Result, SharedString, UnderlineStyle, WindowContext, WrapBoundary,
WrappedLineLayout,
};
use derive_more::{Deref, DerefMut};
use smallvec::SmallVec;
@@ -10,6 +11,7 @@ use std::sync::Arc;
pub struct DecorationRun {
pub len: u32,
pub color: Hsla,
pub background_color: Option<Hsla>,
pub underline: Option<UnderlineStyle>,
}
@@ -38,7 +40,6 @@ impl ShapedLine {
&self.layout,
line_height,
&self.decoration_runs,
None,
&[],
cx,
)?;
@@ -72,7 +73,6 @@ impl WrappedLine {
&self.layout.unwrapped_layout,
line_height,
&self.decoration_runs,
self.wrap_width,
&self.wrap_boundaries,
cx,
)?;
@@ -86,7 +86,6 @@ fn paint_line(
layout: &LineLayout,
line_height: Pixels,
decoration_runs: &[DecorationRun],
wrap_width: Option<Pixels>,
wrap_boundaries: &[WrapBoundary],
cx: &mut WindowContext<'_>,
) -> Result<()> {
@@ -97,6 +96,7 @@ fn paint_line(
let mut run_end = 0;
let mut color = black();
let mut current_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
let mut current_background: Option<(Point<Pixels>, Hsla)> = None;
let text_system = cx.text_system().clone();
let mut glyph_origin = origin;
let mut prev_glyph_position = Point::default();
@@ -110,12 +110,28 @@ fn paint_line(
if wraps.peek() == Some(&&WrapBoundary { run_ix, glyph_ix }) {
wraps.next();
if let Some((underline_origin, underline_style)) = current_underline.take() {
if let Some((background_origin, background_color)) = current_background.as_mut() {
cx.paint_quad(
Bounds {
origin: *background_origin,
size: size(glyph_origin.x - background_origin.x, line_height),
},
Corners::default(),
*background_color,
Edges::default(),
transparent_black(),
);
background_origin.x = origin.x;
background_origin.y += line_height;
}
if let Some((underline_origin, underline_style)) = current_underline.as_mut() {
cx.paint_underline(
underline_origin,
*underline_origin,
glyph_origin.x - underline_origin.x,
&underline_style,
)?;
underline_style,
);
underline_origin.x = origin.x;
underline_origin.y += line_height;
}
glyph_origin.x = origin.x;
@@ -123,9 +139,20 @@ fn paint_line(
}
prev_glyph_position = glyph.position;
let mut finished_background: Option<(Point<Pixels>, Hsla)> = None;
let mut finished_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
if glyph.index >= run_end {
if let Some(style_run) = decoration_runs.next() {
if let Some((_, background_color)) = &mut current_background {
if style_run.background_color.as_ref() != Some(background_color) {
finished_background = current_background.take();
}
}
if let Some(run_background) = style_run.background_color {
current_background
.get_or_insert((point(glyph_origin.x, glyph_origin.y), run_background));
}
if let Some((_, underline_style)) = &mut current_underline {
if style_run.underline.as_ref() != Some(underline_style) {
finished_underline = current_underline.take();
@@ -135,7 +162,7 @@ fn paint_line(
current_underline.get_or_insert((
point(
glyph_origin.x,
origin.y + baseline_offset.y + (layout.descent * 0.618),
glyph_origin.y + baseline_offset.y + (layout.descent * 0.618),
),
UnderlineStyle {
color: Some(run_underline.color.unwrap_or(style_run.color)),
@@ -149,16 +176,30 @@ fn paint_line(
color = style_run.color;
} else {
run_end = layout.len;
finished_background = current_background.take();
finished_underline = current_underline.take();
}
}
if let Some((background_origin, background_color)) = finished_background {
cx.paint_quad(
Bounds {
origin: background_origin,
size: size(glyph_origin.x - background_origin.x, line_height),
},
Corners::default(),
background_color,
Edges::default(),
transparent_black(),
);
}
if let Some((underline_origin, underline_style)) = finished_underline {
cx.paint_underline(
underline_origin,
glyph_origin.x - underline_origin.x,
&underline_style,
)?;
);
}
let max_glyph_bounds = Bounds {
@@ -188,13 +229,32 @@ fn paint_line(
}
}
let mut last_line_end_x = origin.x + layout.width;
if let Some(boundary) = wrap_boundaries.last() {
let run = &layout.runs[boundary.run_ix];
let glyph = &run.glyphs[boundary.glyph_ix];
last_line_end_x -= glyph.position.x;
}
if let Some((background_origin, background_color)) = current_background.take() {
cx.paint_quad(
Bounds {
origin: background_origin,
size: size(last_line_end_x - background_origin.x, line_height),
},
Corners::default(),
background_color,
Edges::default(),
transparent_black(),
);
}
if let Some((underline_start, underline_style)) = current_underline.take() {
let line_end_x = origin.x + wrap_width.unwrap_or(Pixels::MAX).min(layout.width);
cx.paint_underline(
underline_start,
line_end_x - underline_start.x,
last_line_end_x - underline_start.x,
&underline_style,
)?;
);
}
Ok(())
@@ -198,6 +198,41 @@ impl WrappedLineLayout {
pub fn runs(&self) -> &[ShapedRun] {
&self.unwrapped_layout.runs
}
pub fn index_for_position(
&self,
position: Point<Pixels>,
line_height: Pixels,
) -> Option<usize> {
let wrapped_line_ix = (position.y / line_height) as usize;
let wrapped_line_start_x = if wrapped_line_ix > 0 {
let wrap_boundary_ix = wrapped_line_ix - 1;
let wrap_boundary = self.wrap_boundaries[wrap_boundary_ix];
let run = &self.unwrapped_layout.runs[wrap_boundary.run_ix];
run.glyphs[wrap_boundary.glyph_ix].position.x
} else {
Pixels::ZERO
};
let wrapped_line_end_x = if wrapped_line_ix < self.wrap_boundaries.len() {
let next_wrap_boundary_ix = wrapped_line_ix;
let next_wrap_boundary = self.wrap_boundaries[next_wrap_boundary_ix];
let run = &self.unwrapped_layout.runs[next_wrap_boundary.run_ix];
run.glyphs[next_wrap_boundary.glyph_ix].position.x
} else {
self.unwrapped_layout.width
};
let mut position_in_unwrapped_line = position;
position_in_unwrapped_line.x += wrapped_line_start_x;
if position_in_unwrapped_line.x > wrapped_line_end_x {
None
} else {
self.unwrapped_layout
.index_for_x(position_in_unwrapped_line.x)
}
}
}
pub(crate) struct LineLayoutCache {
+15 -19
View File
@@ -1,7 +1,7 @@
use crate::{
private::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, BorrowWindow,
Bounds, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, LayoutId,
Model, Pixels, Point, Render, RenderOnce, Size, ViewContext, VisualContext, WeakModel,
Bounds, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, IntoElement,
LayoutId, Model, Pixels, Point, Render, Size, ViewContext, VisualContext, WeakModel,
WindowContext,
};
use anyhow::{Context, Result};
@@ -244,26 +244,26 @@ impl Element for AnyView {
}
}
impl<V: 'static + Render> RenderOnce for View<V> {
impl<V: 'static + Render> IntoElement for View<V> {
type Element = View<V>;
fn element_id(&self) -> Option<ElementId> {
Some(self.model.entity_id.into())
Some(ElementId::from_entity_id(self.model.entity_id))
}
fn render_once(self) -> Self::Element {
fn into_element(self) -> Self::Element {
self
}
}
impl RenderOnce for AnyView {
impl IntoElement for AnyView {
type Element = Self;
fn element_id(&self) -> Option<ElementId> {
Some(self.model.entity_id.into())
Some(ElementId::from_entity_id(self.model.entity_id))
}
fn render_once(self) -> Self::Element {
fn into_element(self) -> Self::Element {
self
}
}
@@ -308,27 +308,23 @@ where
}
mod any_view {
use crate::{AnyElement, AnyView, BorrowWindow, Element, LayoutId, Render, WindowContext};
use crate::{AnyElement, AnyView, Element, LayoutId, Render, WindowContext};
pub(crate) fn layout<V: 'static + Render>(
view: &AnyView,
cx: &mut WindowContext,
) -> (LayoutId, AnyElement) {
cx.with_element_id(Some(view.model.entity_id), |cx| {
let view = view.clone().downcast::<V>().unwrap();
let mut element = view.update(cx, |view, cx| view.render(cx).into_any());
let layout_id = element.layout(cx);
(layout_id, element)
})
let view = view.clone().downcast::<V>().unwrap();
let mut element = view.update(cx, |view, cx| view.render(cx).into_any());
let layout_id = element.layout(cx);
(layout_id, element)
}
pub(crate) fn paint<V: 'static + Render>(
view: &AnyView,
_view: &AnyView,
element: AnyElement,
cx: &mut WindowContext,
) {
cx.with_element_id(Some(view.model.entity_id), |cx| {
element.paint(cx);
})
element.paint(cx);
}
}
+88 -18
View File
@@ -193,11 +193,11 @@ pub trait FocusableView: 'static + Render {
/// ManagedView is a view (like a Modal, Popover, Menu, etc.)
/// where the lifecycle of the view is handled by another view.
pub trait ManagedView: FocusableView + EventEmitter<Manager> {}
pub trait ManagedView: FocusableView + EventEmitter<DismissEvent> {}
impl<M: FocusableView + EventEmitter<Manager>> ManagedView for M {}
impl<M: FocusableView + EventEmitter<DismissEvent>> ManagedView for M {}
pub enum Manager {
pub enum DismissEvent {
Dismiss,
}
@@ -230,9 +230,15 @@ pub struct Window {
pub(crate) focus: Option<FocusId>,
}
pub(crate) struct ElementStateBox {
inner: Box<dyn Any>,
#[cfg(debug_assertions)]
type_name: &'static str,
}
// #[derive(Default)]
pub(crate) struct Frame {
pub(crate) element_states: HashMap<GlobalElementId, Box<dyn Any>>,
pub(crate) element_states: HashMap<GlobalElementId, ElementStateBox>,
mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, AnyMouseListener)>>,
pub(crate) dispatch_tree: DispatchTree,
pub(crate) focus_listeners: Vec<AnyFocusListener>,
@@ -875,7 +881,7 @@ impl<'a> WindowContext<'a> {
origin: Point<Pixels>,
width: Pixels,
style: &UnderlineStyle,
) -> Result<()> {
) {
let scale_factor = self.scale_factor();
let height = if style.wavy {
style.thickness * 3.
@@ -899,7 +905,6 @@ impl<'a> WindowContext<'a> {
wavy: style.wavy,
},
);
Ok(())
}
/// Paint a monochrome (non-emoji) glyph into the scene for the current frame at the current z-index.
@@ -1512,6 +1517,13 @@ impl<'a> WindowContext<'a> {
.set_input_handler(Box::new(input_handler));
}
}
pub fn on_window_should_close(&mut self, f: impl Fn(&mut WindowContext) -> bool + 'static) {
let mut this = self.to_async();
self.window
.platform_window
.on_should_close(Box::new(move || this.update(|_, cx| f(cx)).unwrap_or(true)))
}
}
impl Context for WindowContext<'_> {
@@ -1658,7 +1670,7 @@ impl VisualContext for WindowContext<'_> {
where
V: ManagedView,
{
self.update_view(view, |_, cx| cx.emit(Manager::Dismiss))
self.update_view(view, |_, cx| cx.emit(DismissEvent::Dismiss))
}
}
@@ -1747,6 +1759,24 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
}
}
/// Invoke the given function with the content mask reset to that
/// of the window.
fn break_content_mask<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
let mask = ContentMask {
bounds: Bounds {
origin: Point::default(),
size: self.window().viewport_size,
},
};
self.window_mut()
.current_frame
.content_mask_stack
.push(mask);
let result = f(self);
self.window_mut().current_frame.content_mask_stack.pop();
result
}
/// Update the global element offset relative to the current offset. This is used to implement
/// scrolling.
fn with_element_offset<R>(
@@ -1815,10 +1845,37 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
.remove(&global_id)
})
{
let ElementStateBox {
inner,
#[cfg(debug_assertions)]
type_name
} = any;
// Using the extra inner option to avoid needing to reallocate a new box.
let mut state_box = any
let mut state_box = inner
.downcast::<Option<S>>()
.expect("invalid element state type for id");
.map_err(|_| {
#[cfg(debug_assertions)]
{
anyhow!(
"invalid element state type for id, requested_type {:?}, actual type: {:?}",
std::any::type_name::<S>(),
type_name
)
}
#[cfg(not(debug_assertions))]
{
anyhow!(
"invalid element state type for id, requested_type {:?}",
std::any::type_name::<S>(),
)
}
})
.unwrap();
// Actual: Option<AnyElement> <- View
// Requested: () <- AnyElemet
let state = state_box
.take()
.expect("element state is already on the stack");
@@ -1827,14 +1884,27 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
cx.window_mut()
.current_frame
.element_states
.insert(global_id, state_box);
.insert(global_id, ElementStateBox {
inner: state_box,
#[cfg(debug_assertions)]
type_name
});
result
} else {
let (result, state) = f(None, cx);
cx.window_mut()
.current_frame
.element_states
.insert(global_id, Box::new(Some(state)));
.insert(global_id,
ElementStateBox {
inner: Box::new(Some(state)),
#[cfg(debug_assertions)]
type_name: std::any::type_name::<S>()
}
);
result
}
})
@@ -2304,7 +2374,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
where
V: ManagedView,
{
self.defer(|_, cx| cx.emit(Manager::Dismiss))
self.defer(|_, cx| cx.emit(DismissEvent::Dismiss))
}
pub fn listener<E>(
@@ -2599,6 +2669,12 @@ pub enum ElementId {
FocusHandle(FocusId),
}
impl ElementId {
pub(crate) fn from_entity_id(entity_id: EntityId) -> Self {
ElementId::View(entity_id)
}
}
impl TryInto<SharedString> for ElementId {
type Error = anyhow::Error;
@@ -2611,12 +2687,6 @@ impl TryInto<SharedString> for ElementId {
}
}
impl From<EntityId> for ElementId {
fn from(id: EntityId) -> Self {
ElementId::View(id)
}
}
impl From<usize> for ElementId {
fn from(id: usize) -> Self {
ElementId::Integer(id)