Add Text::styled() and use it in command palette
Prevents jumping while typing
This commit is contained in:
parent
06f3c60be8
commit
c5878cbd5f
3 changed files with 41 additions and 16 deletions
|
@ -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()
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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],
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue