Extract {Container,Label}Style structs from those elements

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