use egui::{
Align2, Color32, FontId, Label, Response, RichText, Shape, Stroke, TextWrapMode, Ui, Vec2,
WidgetText,
};
use crate::{
impl_style_builders,
style::shared_style::{
ResolvedBorder, SharedStyle, border_gradient_mesh, gradient_shape, inner_glow_shape,
paint_shadows, paint_side_borders, render_scoped,
},
};
use super::text_effects::{Glow, GlowQuality, TextEffects, TextShadow, paint_text_effects};
pub struct StyledLabel {
text: WidgetText,
bold: bool,
italics: bool,
wrap_mode: Option<TextWrapMode>,
font: Option<FontId>,
style: SharedStyle,
effects: TextEffects,
}
impl StyledLabel {
pub fn new(text: impl Into<WidgetText>) -> Self {
Self {
text: text.into(),
bold: false,
italics: false,
wrap_mode: None,
font: None,
style: SharedStyle::default(),
effects: TextEffects::default(),
}
}
pub fn bold(mut self) -> Self {
self.bold = true;
self
}
pub fn italics(mut self) -> Self {
self.italics = true;
self
}
pub fn wrap_mode(mut self, mode: TextWrapMode) -> Self {
self.wrap_mode = Some(mode);
self
}
pub fn extend(self) -> Self {
self.wrap_mode(TextWrapMode::Extend)
}
pub fn truncate(self) -> Self {
self.wrap_mode(TextWrapMode::Truncate)
}
pub fn wrap(self) -> Self {
self.wrap_mode(TextWrapMode::Wrap)
}
pub fn font(mut self, font: FontId) -> Self {
self.font = Some(font);
self
}
pub fn text_shadow(mut self, offset: Vec2, color: Color32) -> Self {
self.effects.shadows.push(TextShadow { offset, color });
self
}
pub fn outline(mut self, width: f32, color: Color32) -> Self {
self.effects.outline = Some((width, color));
self
}
pub fn glow(mut self, color: Color32, radius: f32, intensity: f32) -> Self {
self.effects.glow = Some(Glow {
color,
radius,
intensity,
});
self
}
pub fn glow_quality(mut self, samples: u32) -> Self {
self.effects.glow_quality = GlowQuality { samples };
self
}
pub fn scale(mut self, factor: f32, pivot: Align2) -> Self {
self.effects.scale = Some((factor, pivot));
self
}
pub fn show(self, ui: &mut Ui) -> Response {
let visible = self.style.visible != Some(false);
let mut rich = match self.text {
WidgetText::RichText(rt) => (*rt).clone(),
other => RichText::new(other.text().to_string()),
};
if let Some(color) = self.style.text_color {
rich = rich.color(color);
}
if let Some(font) = self.font.clone() {
rich = rich.font(font);
} else if let Some(size) = self.style.font_size {
rich = rich.size(size);
}
if self.bold {
rich = rich.strong();
}
if self.italics {
rich = rich.italics();
}
let visuals = ui.visuals().clone();
let wv = &visuals.widgets.inactive;
let bg = self.style.bg.unwrap_or(egui::Color32::TRANSPARENT);
let border = self.style.border.unwrap_or(Stroke::NONE);
let side_overrides = self.style.border_sides;
let has_border_overrides = side_overrides.any();
let border_sides = ResolvedBorder {
top: side_overrides.top.unwrap_or(border),
right: side_overrides.right.unwrap_or(border),
bottom: side_overrides.bottom.unwrap_or(border),
left: side_overrides.left.unwrap_or(border),
};
let bg_gradient = self.style.bg_gradient.clone();
let inner_glow = self.style.inner_glow;
let border_gradient = self.style.border_gradient;
let frame_bg = if bg_gradient.is_some() {
egui::Color32::TRANSPARENT
} else {
bg
};
let frame_border = if has_border_overrides || border_gradient.is_some() {
Stroke::NONE
} else {
border
};
let corner_radius = self.style.corner_radius.unwrap_or(wv.corner_radius);
let padding = self.style.padding.unwrap_or_default();
let margin = self.style.margin.unwrap_or_default();
let has_effects = !self.effects.is_empty();
let effects = self.effects;
let wrap_mode = self.wrap_mode;
let style_shadows = self.style.shadows.clone();
let response = render_scoped(ui, visible, |ui| {
let shadow_idx = ui.painter().add(Shape::Noop);
let gradient_idx = ui.painter().add(Shape::Noop);
let response = egui::Frame::new()
.fill(frame_bg)
.stroke(frame_border)
.corner_radius(corner_radius)
.inner_margin(padding)
.outer_margin(margin)
.show(ui, |ui| {
self.style
.resolve_size(ui.available_width(), ui.available_height())
.apply_to_ui(ui);
if has_effects {
let mut label = Label::new(rich);
if let Some(mode) = wrap_mode {
label = label.wrap_mode(mode);
}
let (pos, galley, response) = label.layout_in_ui(ui);
let fallback = ui.visuals().widgets.inactive.text_color();
paint_text_effects(ui, pos, galley, fallback, &effects, response.rect);
response
} else {
let mut label = Label::new(rich);
if let Some(mode) = wrap_mode {
label = label.wrap_mode(mode);
}
ui.add(label)
}
})
.inner;
if let Some(g) = &bg_gradient {
let shapes = vec![
Shape::rect_filled(response.rect, corner_radius, bg),
gradient_shape(ui.ctx(), response.rect, corner_radius, g),
];
ui.painter().set(gradient_idx, Shape::Vec(shapes));
}
if let Some(bg) = border_gradient {
ui.painter()
.add(Shape::Mesh(border_gradient_mesh(response.rect, bg).into()));
} else if has_border_overrides {
paint_side_borders(ui.painter(), response.rect, border_sides);
}
if let Some(glow) = inner_glow
&& let Some(shape) = inner_glow_shape(response.rect, corner_radius, glow)
{
ui.painter().add(shape);
}
paint_shadows(ui, shadow_idx, response.rect, corner_radius, &style_shadows);
response
});
if let Some(icon) = self.style.cursor_icon
&& response.hovered()
{
ui.ctx().set_cursor_icon(icon);
}
response
}
}
impl_style_builders!(StyledLabel);
crate::impl_styled_widget!(StyledLabel);