From c866c211b53c7bb2bfd96af69f2144073aa8f101 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 17 Nov 2023 13:48:01 -0700 Subject: [PATCH] Make static str and SharedString implement Element --- crates/gpui2/src/elements/text.rs | 138 +++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 2 deletions(-) diff --git a/crates/gpui2/src/elements/text.rs b/crates/gpui2/src/elements/text.rs index 6849a8971107f011ee3e5ee06b186ada3da78ed4..9e8256014eeaba623174cb619b021f1181499b47 100644 --- a/crates/gpui2/src/elements/text.rs +++ b/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 Element for &'static str { + type ElementState = TextState; + + fn element_id(&self) -> Option { + None + } + + fn layout( + &mut self, + _: &mut V, + _: Option, + cx: &mut ViewContext, + ) -> (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, + _: &mut V, + state: &mut TextState, + cx: &mut ViewContext, + ) { + state.paint(bounds, self, cx) + } +} + +impl Element for SharedString { + type ElementState = TextState; + + fn element_id(&self) -> Option { + Some(self.clone().into()) + } + + fn layout( + &mut self, + _: &mut V, + _: Option, + cx: &mut ViewContext, + ) -> (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, + _: &mut V, + state: &mut TextState, + cx: &mut ViewContext, + ) { + let text_str: &str = self.as_ref(); + state.paint(bounds, text_str, cx) + } +} + pub struct Text { text: SharedString, runs: Option>, @@ -109,7 +169,7 @@ impl Element 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> { self.0.lock() } + + fn layout( + &mut self, + text: SharedString, + runs: Option>, + 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 = 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, 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 {