Add Text::styled() and use it in command palette

Prevents jumping while typing
This commit is contained in:
Conrad Irwin 2023-11-13 19:31:40 -07:00
parent 06f3c60be8
commit c5878cbd5f
3 changed files with 41 additions and 16 deletions

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
AnyElement, BorrowWindow, Bounds, Component, Element, LayoutId, Line, Pixels, SharedString, AnyElement, BorrowWindow, Bounds, Component, Element, LayoutId, Line, Pixels, SharedString,
Size, ViewContext, Size, TextRun, ViewContext,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -11,6 +11,7 @@ impl<V: 'static> Component<V> for SharedString {
fn render(self) -> AnyElement<V> { fn render(self) -> AnyElement<V> {
Text { Text {
text: self, text: self,
runs: None,
state_type: PhantomData, state_type: PhantomData,
} }
.render() .render()
@ -21,6 +22,7 @@ impl<V: 'static> Component<V> for &'static str {
fn render(self) -> AnyElement<V> { fn render(self) -> AnyElement<V> {
Text { Text {
text: self.into(), text: self.into(),
runs: None,
state_type: PhantomData, state_type: PhantomData,
} }
.render() .render()
@ -33,6 +35,7 @@ impl<V: 'static> Component<V> for String {
fn render(self) -> AnyElement<V> { fn render(self) -> AnyElement<V> {
Text { Text {
text: self.into(), text: self.into(),
runs: None,
state_type: PhantomData, state_type: PhantomData,
} }
.render() .render()
@ -41,9 +44,25 @@ impl<V: 'static> Component<V> for String {
pub struct Text<V> { pub struct Text<V> {
text: SharedString, text: SharedString,
runs: Option<Vec<TextRun>>,
state_type: PhantomData<V>, state_type: PhantomData<V>,
} }
impl<V: 'static> Text<V> {
/// styled renders text that has different runs of different styles.
/// callers are responsible for setting the correct style for each run.
////
/// For uniform text you can usually just pass a string as a child, and
/// cx.text_style() will be used automatically.
pub fn styled(text: SharedString, runs: Vec<TextRun>) -> Self {
Text {
text,
runs: Some(runs),
state_type: Default::default(),
}
}
}
impl<V: 'static> Component<V> for Text<V> { impl<V: 'static> Component<V> for Text<V> {
fn render(self) -> AnyElement<V> { fn render(self) -> AnyElement<V> {
AnyElement::new(self) AnyElement::new(self)
@ -82,6 +101,12 @@ impl<V: 'static> Element<V> for Text<V> {
let rem_size = cx.rem_size(); let rem_size = cx.rem_size();
let runs = if let Some(runs) = self.runs.take() {
runs
} else {
vec![text_style.to_run(text.len())]
};
let layout_id = cx.request_measured_layout(Default::default(), rem_size, { let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
let element_state = element_state.clone(); let element_state = element_state.clone();
move |known_dimensions, _| { move |known_dimensions, _| {
@ -89,7 +114,7 @@ impl<V: 'static> Element<V> for Text<V> {
.layout_text( .layout_text(
&text, &text,
font_size, font_size,
&[text_style.to_run(text.len())], &runs[..],
known_dimensions.width, // Wrap if we know the width. known_dimensions.width, // Wrap if we know the width.
) )
.log_err() .log_err()

View file

@ -368,6 +368,7 @@ impl Display for FontStyle {
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct TextRun { pub struct TextRun {
// number of utf8 bytes
pub len: usize, pub len: usize,
pub font: Font, pub font: Font,
pub color: Hsla, pub color: Hsla,

View file

@ -1,5 +1,4 @@
use gpui::{relative, Hsla, WindowContext}; use gpui::{relative, Hsla, Text, TextRun, WindowContext};
use smallvec::SmallVec;
use crate::prelude::*; use crate::prelude::*;
use crate::styled_ext::StyledExt; use crate::styled_ext::StyledExt;
@ -105,6 +104,8 @@ pub struct HighlightedLabel {
} }
impl HighlightedLabel { impl HighlightedLabel {
/// shows a label with the given characters highlighted.
/// characters are identified by utf8 byte position.
pub fn new(label: impl Into<SharedString>, highlight_indices: Vec<usize>) -> Self { pub fn new(label: impl Into<SharedString>, highlight_indices: Vec<usize>) -> Self {
Self { Self {
label: label.into(), label: label.into(),
@ -126,10 +127,11 @@ impl HighlightedLabel {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
let highlight_color = cx.theme().colors().text_accent; let highlight_color = cx.theme().colors().text_accent;
let mut text_style = cx.text_style().clone();
let mut highlight_indices = self.highlight_indices.iter().copied().peekable(); let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
let mut runs: SmallVec<[Run; 8]> = SmallVec::new(); let mut runs: Vec<TextRun> = Vec::new();
for (char_ix, char) in self.label.char_indices() { for (char_ix, char) in self.label.char_indices() {
let mut color = self.color.hsla(cx); let mut color = self.color.hsla(cx);
@ -137,16 +139,14 @@ impl HighlightedLabel {
if let Some(highlight_ix) = highlight_indices.peek() { if let Some(highlight_ix) = highlight_indices.peek() {
if char_ix == *highlight_ix { if char_ix == *highlight_ix {
color = highlight_color; color = highlight_color;
highlight_indices.next(); highlight_indices.next();
} }
} }
let last_run = runs.last_mut(); let last_run = runs.last_mut();
let start_new_run = if let Some(last_run) = last_run { let start_new_run = if let Some(last_run) = last_run {
if color == last_run.color { if color == last_run.color {
last_run.text.push(char); last_run.len += char.len_utf8();
false false
} else { } else {
true true
@ -156,10 +156,8 @@ impl HighlightedLabel {
}; };
if start_new_run { if start_new_run {
runs.push(Run { text_style.color = color;
text: char.to_string(), runs.push(text_style.to_run(char.len_utf8()))
color,
});
} }
} }
@ -176,10 +174,7 @@ impl HighlightedLabel {
.bg(LabelColor::Hidden.hsla(cx)), .bg(LabelColor::Hidden.hsla(cx)),
) )
}) })
.children( .child(Text::styled(self.label, runs))
runs.into_iter()
.map(|run| div().text_color(run.color).child(run.text)),
)
} }
} }
@ -213,6 +208,10 @@ mod stories {
"Hello, world!", "Hello, world!",
vec![0, 1, 2, 7, 8, 12], vec![0, 1, 2, 7, 8, 12],
)) ))
.child(HighlightedLabel::new(
"Héllo, world!",
vec![0, 1, 3, 8, 9, 13],
))
} }
} }
} }