Make static str and SharedString implement Element

Nathan Sobo created

Change summary

crates/gpui2/src/elements/text.rs | 138 ++++++++++++++++++++++++++++++++
1 file changed, 136 insertions(+), 2 deletions(-)

Detailed changes

crates/gpui2/src/elements/text.rs 🔗

@@ -1,12 +1,72 @@
 use crate::{
     AnyElement, BorrowWindow, Bounds, Component, Element, ElementId, LayoutId, Pixels,
-    SharedString, Size, TextRun, ViewContext, WrappedLine,
+    SharedString, Size, TextRun, ViewContext, WindowContext, WrappedLine,
 };
+use anyhow::anyhow;
 use parking_lot::{Mutex, MutexGuard};
 use smallvec::SmallVec;
 use std::{cell::Cell, rc::Rc, sync::Arc};
 use util::ResultExt;
 
+impl<V: 'static> Element<V> for &'static str {
+    type ElementState = TextState;
+
+    fn element_id(&self) -> Option<ElementId> {
+        None
+    }
+
+    fn layout(
+        &mut self,
+        _: &mut V,
+        _: Option<Self::ElementState>,
+        cx: &mut ViewContext<V>,
+    ) -> (LayoutId, Self::ElementState) {
+        let mut state = TextState::default();
+        let layout_id = state.layout(SharedString::from(*self), None, cx);
+        (layout_id, state)
+    }
+
+    fn paint(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        _: &mut V,
+        state: &mut TextState,
+        cx: &mut ViewContext<V>,
+    ) {
+        state.paint(bounds, self, cx)
+    }
+}
+
+impl<V: 'static> Element<V> for SharedString {
+    type ElementState = TextState;
+
+    fn element_id(&self) -> Option<ElementId> {
+        Some(self.clone().into())
+    }
+
+    fn layout(
+        &mut self,
+        _: &mut V,
+        _: Option<Self::ElementState>,
+        cx: &mut ViewContext<V>,
+    ) -> (LayoutId, Self::ElementState) {
+        let mut state = TextState::default();
+        let layout_id = state.layout(self.clone(), None, cx);
+        (layout_id, state)
+    }
+
+    fn paint(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        _: &mut V,
+        state: &mut TextState,
+        cx: &mut ViewContext<V>,
+    ) {
+        let text_str: &str = self.as_ref();
+        state.paint(bounds, text_str, cx)
+    }
+}
+
 pub struct Text {
     text: SharedString,
     runs: Option<Vec<TextRun>>,
@@ -109,7 +169,7 @@ impl<V: 'static> Element<V> for Text {
         let element_state = element_state.lock();
         let element_state = element_state
             .as_ref()
-            .ok_or_else(|| anyhow::anyhow!("measurement has not been performed on {}", &self.text))
+            .ok_or_else(|| anyhow!("measurement has not been performed on {}", &self.text))
             .unwrap();
 
         let line_height = element_state.line_height;
@@ -128,6 +188,80 @@ impl TextState {
     fn lock(&self) -> MutexGuard<Option<TextStateInner>> {
         self.0.lock()
     }
+
+    fn layout(
+        &mut self,
+        text: SharedString,
+        runs: Option<Vec<TextRun>>,
+        cx: &mut WindowContext,
+    ) -> LayoutId {
+        let text_system = cx.text_system().clone();
+        let text_style = cx.text_style();
+        let font_size = text_style.font_size.to_pixels(cx.rem_size());
+        let line_height = text_style
+            .line_height
+            .to_pixels(font_size.into(), cx.rem_size());
+        let text = SharedString::from(text);
+
+        let rem_size = cx.rem_size();
+
+        let runs = if let Some(runs) = runs {
+            runs
+        } else {
+            vec![text_style.to_run(text.len())]
+        };
+
+        let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
+            let element_state = self.clone();
+            move |known_dimensions, _| {
+                let Some(lines) = text_system
+                    .shape_text(
+                        &text,
+                        font_size,
+                        &runs[..],
+                        known_dimensions.width, // Wrap if we know the width.
+                    )
+                    .log_err()
+                else {
+                    element_state.lock().replace(TextStateInner {
+                        lines: Default::default(),
+                        line_height,
+                    });
+                    return Size::default();
+                };
+
+                let mut size: Size<Pixels> = Size::default();
+                for line in &lines {
+                    let line_size = line.size(line_height);
+                    size.height += line_size.height;
+                    size.width = size.width.max(line_size.width);
+                }
+
+                element_state
+                    .lock()
+                    .replace(TextStateInner { lines, line_height });
+
+                size
+            }
+        });
+
+        layout_id
+    }
+
+    fn paint(&mut self, bounds: Bounds<Pixels>, text: &str, cx: &mut WindowContext) {
+        let element_state = self.lock();
+        let element_state = element_state
+            .as_ref()
+            .ok_or_else(|| anyhow!("measurement has not been performed on {}", text))
+            .unwrap();
+
+        let line_height = element_state.line_height;
+        let mut line_origin = bounds.origin;
+        for line in &element_state.lines {
+            line.paint(line_origin, line_height, cx).log_err();
+            line_origin.y += line.size(line_height).height;
+        }
+    }
 }
 
 struct TextStateInner {