diff --git a/gpui/src/elements/container.rs b/gpui/src/elements/container.rs index c0b829fbe6f881b0cf24e8c80d2bf92a16cabcb2..9ae084193143a48db09a2d21af9305bd869dae90 100644 --- a/gpui/src/elements/container.rs +++ b/gpui/src/elements/container.rs @@ -10,53 +10,58 @@ use crate::{ SizeConstraint, }; -pub struct Container { +#[derive(Clone, Debug, Default)] +pub struct ContainerStyle { margin: Margin, padding: Padding, background_color: Option, border: Border, corner_radius: f32, shadow: Option, +} + +pub struct Container { child: ElementBox, + style: ContainerStyle, } impl Container { pub fn new(child: ElementBox) -> Self { Self { - margin: Margin::default(), - padding: Padding::default(), - background_color: None, - border: Border::default(), - corner_radius: 0.0, - shadow: None, child, + style: Default::default(), } } + pub fn with_style(mut self, style: &ContainerStyle) -> Self { + self.style = style.clone(); + self + } + pub fn with_margin_top(mut self, margin: f32) -> Self { - self.margin.top = margin; + self.style.margin.top = margin; self } pub fn with_margin_left(mut self, margin: f32) -> Self { - self.margin.left = margin; + self.style.margin.left = margin; self } pub fn with_horizontal_padding(mut self, padding: f32) -> Self { - self.padding.left = padding; - self.padding.right = padding; + self.style.padding.left = padding; + self.style.padding.right = padding; self } pub fn with_vertical_padding(mut self, padding: f32) -> Self { - self.padding.top = padding; - self.padding.bottom = padding; + self.style.padding.top = padding; + self.style.padding.bottom = padding; self } pub fn with_uniform_padding(mut self, padding: f32) -> Self { - self.padding = Padding { + self.style.padding = Padding { top: padding, left: padding, bottom: padding, @@ -66,32 +71,32 @@ impl Container { } pub fn with_padding_right(mut self, padding: f32) -> Self { - self.padding.right = padding; + self.style.padding.right = padding; self } pub fn with_padding_bottom(mut self, padding: f32) -> Self { - self.padding.bottom = padding; + self.style.padding.bottom = padding; self } pub fn with_background_color(mut self, color: impl Into) -> Self { - self.background_color = Some(color.into()); + self.style.background_color = Some(color.into()); self } pub fn with_border(mut self, border: Border) -> Self { - self.border = border; + self.style.border = border; self } pub fn with_corner_radius(mut self, radius: f32) -> Self { - self.corner_radius = radius; + self.style.corner_radius = radius; self } pub fn with_shadow(mut self, offset: Vector2F, blur: f32, color: impl Into) -> Self { - self.shadow = Some(Shadow { + self.style.shadow = Some(Shadow { offset, blur, color: color.into(), @@ -101,33 +106,33 @@ impl Container { fn margin_size(&self) -> Vector2F { vec2f( - self.margin.left + self.margin.right, - self.margin.top + self.margin.bottom, + self.style.margin.left + self.style.margin.right, + self.style.margin.top + self.style.margin.bottom, ) } fn padding_size(&self) -> Vector2F { vec2f( - self.padding.left + self.padding.right, - self.padding.top + self.padding.bottom, + self.style.padding.left + self.style.padding.right, + self.style.padding.top + self.style.padding.bottom, ) } fn border_size(&self) -> Vector2F { let mut x = 0.0; - if self.border.left { - x += self.border.width; + if self.style.border.left { + x += self.style.border.width; } - if self.border.right { - x += self.border.width; + if self.style.border.right { + x += self.style.border.width; } let mut y = 0.0; - if self.border.top { - y += self.border.width; + if self.style.border.top { + y += self.style.border.width; } - if self.border.bottom { - y += self.border.width; + if self.style.border.bottom { + y += self.style.border.width; } vec2f(x, y) @@ -168,28 +173,31 @@ impl Element for Container { cx: &mut PaintContext, ) -> Self::PaintState { let quad_bounds = RectF::from_points( - bounds.origin() + vec2f(self.margin.left, self.margin.top), - bounds.lower_right() - vec2f(self.margin.right, self.margin.bottom), + bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top), + bounds.lower_right() - vec2f(self.style.margin.right, self.style.margin.bottom), ); - if let Some(shadow) = self.shadow.as_ref() { + if let Some(shadow) = self.style.shadow.as_ref() { cx.scene.push_shadow(scene::Shadow { bounds: quad_bounds + shadow.offset, - corner_radius: self.corner_radius, + corner_radius: self.style.corner_radius, sigma: shadow.blur, color: shadow.color, }); } cx.scene.push_quad(Quad { bounds: quad_bounds, - background: self.background_color, - border: self.border, - corner_radius: self.corner_radius, + background: self.style.background_color, + border: self.style.border, + corner_radius: self.style.corner_radius, }); let child_origin = quad_bounds.origin() - + vec2f(self.padding.left, self.padding.top) - + vec2f(self.border.left_width(), self.border.top_width()); + + vec2f(self.style.padding.left, self.style.padding.top) + + vec2f( + self.style.border.left_width(), + self.style.border.top_width(), + ); self.child.paint(child_origin, cx); } @@ -214,20 +222,26 @@ impl Element for Container { json!({ "type": "Container", "bounds": bounds.to_json(), - "details": { - "margin": self.margin.to_json(), - "padding": self.padding.to_json(), - "background_color": self.background_color.to_json(), - "border": self.border.to_json(), - "corner_radius": self.corner_radius, - "shadow": self.shadow.to_json(), - }, + "details": self.style.to_json(), "child": self.child.debug(cx), }) } } -#[derive(Default)] +impl ToJson for ContainerStyle { + fn to_json(&self) -> serde_json::Value { + json!({ + "margin": self.margin.to_json(), + "padding": self.padding.to_json(), + "background_color": self.background_color.to_json(), + "border": self.border.to_json(), + "corner_radius": self.corner_radius, + "shadow": self.shadow.to_json(), + }) + } +} + +#[derive(Clone, Debug, Default)] pub struct Margin { top: f32, left: f32, @@ -254,7 +268,7 @@ impl ToJson for Margin { } } -#[derive(Default)] +#[derive(Clone, Debug, Default)] pub struct Padding { top: f32, left: f32, @@ -281,7 +295,7 @@ impl ToJson for Padding { } } -#[derive(Default)] +#[derive(Clone, Debug, Default)] pub struct Shadow { offset: Vector2F, blur: f32, diff --git a/gpui/src/elements/label.rs b/gpui/src/elements/label.rs index 6b6c3358f2742bd818841e34a81fd73b2eda6521..c64b1b8f00d22928d82512d51336aa040e5a84c6 100644 --- a/gpui/src/elements/label.rs +++ b/gpui/src/elements/label.rs @@ -18,16 +18,17 @@ use crate::{ pub struct Label { text: String, family_id: FamilyId, - font_properties: Properties, font_size: f32, - default_color: ColorU, - highlights: Option, + style: LabelStyle, + highlight_indices: Vec, } -pub struct Highlights { - color: ColorU, - indices: Vec, - font_properties: Properties, +#[derive(Clone, Debug, Default)] +pub struct LabelStyle { + pub default_color: ColorU, + pub highlight_color: ColorU, + pub font_properties: Properties, + pub highlight_font_properties: Properties, } impl Label { @@ -35,29 +36,24 @@ impl Label { Self { text, family_id, - font_properties: Properties::new(), font_size, - default_color: ColorU::black(), - highlights: None, + highlight_indices: Default::default(), + style: Default::default(), } } + pub fn with_style(mut self, style: &LabelStyle) -> Self { + self.style = style.clone(); + self + } + pub fn with_default_color(mut self, color: ColorU) -> Self { - self.default_color = color; + self.style.default_color = color; self } - pub fn with_highlights( - mut self, - color: ColorU, - font_properties: Properties, - indices: Vec, - ) -> Self { - self.highlights = Some(Highlights { - color, - font_properties, - indices, - }); + pub fn with_highlights(mut self, indices: Vec) -> Self { + self.highlight_indices = indices; self } @@ -66,46 +62,45 @@ impl Label { font_cache: &FontCache, font_id: FontId, ) -> SmallVec<[(usize, FontId, ColorU); 8]> { - if let Some(highlights) = self.highlights.as_ref() { - let highlight_font_id = font_cache - .select_font(self.family_id, &highlights.font_properties) - .unwrap_or(font_id); - - let mut highlight_indices = highlights.indices.iter().copied().peekable(); - let mut runs = SmallVec::new(); - - for (char_ix, c) in self.text.char_indices() { - let mut font_id = font_id; - let mut color = self.default_color; - if let Some(highlight_ix) = highlight_indices.peek() { - if char_ix == *highlight_ix { - font_id = highlight_font_id; - color = highlights.color; - highlight_indices.next(); - } - } + if self.highlight_indices.is_empty() { + return smallvec![(self.text.len(), font_id, self.style.default_color)]; + } - let push_new_run = - if let Some((last_len, last_font_id, last_color)) = runs.last_mut() { - if font_id == *last_font_id && color == *last_color { - *last_len += c.len_utf8(); - false - } else { - true - } - } else { - true - }; - - if push_new_run { - runs.push((c.len_utf8(), font_id, color)); + let highlight_font_id = font_cache + .select_font(self.family_id, &self.style.highlight_font_properties) + .unwrap_or(font_id); + + let mut highlight_indices = self.highlight_indices.iter().copied().peekable(); + let mut runs = SmallVec::new(); + + for (char_ix, c) in self.text.char_indices() { + let mut font_id = font_id; + let mut color = self.style.default_color; + if let Some(highlight_ix) = highlight_indices.peek() { + if char_ix == *highlight_ix { + font_id = highlight_font_id; + color = self.style.highlight_color; + highlight_indices.next(); } } - runs - } else { - smallvec![(self.text.len(), font_id, self.default_color)] + let push_new_run = if let Some((last_len, last_font_id, last_color)) = runs.last_mut() { + if font_id == *last_font_id && color == *last_color { + *last_len += c.len_utf8(); + false + } else { + true + } + } else { + true + }; + + if push_new_run { + runs.push((c.len_utf8(), font_id, color)); + } } + + runs } } @@ -120,7 +115,7 @@ impl Element for Label { ) -> (Vector2F, Self::LayoutState) { let font_id = cx .font_cache - .select_font(self.family_id, &self.font_properties) + .select_font(self.family_id, &self.style.font_properties) .unwrap(); let runs = self.compute_runs(&cx.font_cache, font_id); let line = @@ -172,21 +167,22 @@ impl Element for Label { json!({ "type": "Label", "bounds": bounds.to_json(), + "text": &self.text, + "highlight_indices": self.highlight_indices, "font_family": cx.font_cache.family_name(self.family_id).unwrap(), "font_size": self.font_size, - "font_properties": self.font_properties.to_json(), - "text": &self.text, - "highlights": self.highlights.to_json(), + "style": self.style.to_json(), }) } } -impl ToJson for Highlights { +impl ToJson for LabelStyle { fn to_json(&self) -> Value { json!({ - "color": self.color.to_json(), - "indices": self.indices, - "font_properties": self.font_properties.to_json(), + "default_color": self.default_color.to_json(), + "default_font_properties": self.font_properties.to_json(), + "highlight_color": self.highlight_color.to_json(), + "highlight_font_properties": self.highlight_font_properties.to_json(), }) } } @@ -211,17 +207,20 @@ mod tests { let black = ColorU::black(); let red = ColorU::new(255, 0, 0, 255); - let label = Label::new(".αβγδε.ⓐⓑⓒⓓⓔ.abcde.".to_string(), menlo, 12.0).with_highlights( - red, - *Properties::new().weight(Weight::BOLD), - vec![ + let label = Label::new(".αβγδε.ⓐⓑⓒⓓⓔ.abcde.".to_string(), menlo, 12.0) + .with_style(&LabelStyle { + default_color: black, + highlight_color: red, + highlight_font_properties: *Properties::new().weight(Weight::BOLD), + ..Default::default() + }) + .with_highlights(vec![ ".α".len(), ".αβ".len(), ".αβγδ".len(), ".αβγδε.ⓐ".len(), ".αβγδε.ⓐⓑ".len(), - ], - ); + ]); let runs = label.compute_runs(cx.font_cache().as_ref(), menlo_regular); assert_eq!( diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index 794b40facee69043e49dfcae8393909809a1b041..faecae42f47462e71b6565be39567b92d9ea291f 100644 --- a/zed/src/file_finder.rs +++ b/zed/src/file_finder.rs @@ -154,6 +154,12 @@ impl FileFinder { |(file_name, file_name_positions, full_path, full_path_positions)| { let bold = *Properties::new().weight(Weight::BOLD); let selected_index = self.selected_index(); + let label_style = LabelStyle { + default_color: theme.modal_match_text.0, + highlight_color: theme.modal_match_text_highlight.0, + highlight_font_properties: bold, + ..Default::default() + }; let mut container = Container::new( Flex::row() .with_child( @@ -178,12 +184,8 @@ impl FileFinder { settings.ui_font_family, settings.ui_font_size, ) - .with_default_color(theme.modal_match_text.0) - .with_highlights( - theme.modal_match_text_highlight.0, - bold, - file_name_positions, - ) + .with_style(&label_style) + .with_highlights(file_name_positions) .boxed(), ) .with_child( @@ -192,12 +194,8 @@ impl FileFinder { settings.ui_font_family, settings.ui_font_size, ) - .with_default_color(theme.modal_match_text.0) - .with_highlights( - theme.modal_match_text_highlight.0, - bold, - full_path_positions, - ) + .with_style(&label_style) + .with_highlights(full_path_positions) .boxed(), ) .boxed(), diff --git a/zed/src/theme_selector.rs b/zed/src/theme_selector.rs index 7b8269d6fdad968bcea4083824d28b24960c5524..55a32d94c5426c9f574e9dacab0506a6b1d63471 100644 --- a/zed/src/theme_selector.rs +++ b/zed/src/theme_selector.rs @@ -11,8 +11,8 @@ use futures::lock::Mutex; use gpui::{ color::ColorF, elements::{ - Align, ChildView, ConstrainedBox, Container, Expanded, Flex, Label, ParentElement, - UniformList, UniformListState, + Align, ChildView, ConstrainedBox, Container, Expanded, Flex, Label, LabelStyle, + ParentElement, UniformList, UniformListState, }, fonts::{Properties, Weight}, geometry::vector::vec2f, @@ -233,7 +233,6 @@ impl ThemeSelector { fn render_match(&self, theme_match: &StringMatch, index: usize) -> ElementBox { let settings = self.settings.borrow(); let theme = &settings.theme.ui; - let bold = *Properties::new().weight(Weight::BOLD); let mut container = Container::new( Label::new( @@ -241,12 +240,13 @@ impl ThemeSelector { settings.ui_font_family, settings.ui_font_size, ) - .with_default_color(theme.modal_match_text.0) - .with_highlights( - theme.modal_match_text_highlight.0, - bold, - theme_match.positions.clone(), - ) + .with_style(&LabelStyle { + default_color: theme.modal_match_text.0, + highlight_color: theme.modal_match_text_highlight.0, + highlight_font_properties: *Properties::new().weight(Weight::BOLD), + ..Default::default() + }) + .with_highlights(theme_match.positions.clone()) .boxed(), ) .with_uniform_padding(6.0)