use emath::{Align2, Vec2};
use epaint::{Color32, Margin};
use crate::{
Button, ComboBox, Context, Frame, TextEdit, TextStyle,
class::HasClasses as _,
theme::StyleProvider,
widget_style::{
AtomLayoutStyle, ButtonStyle, CheckboxStyle, SeparatorStyle, StyleArgs, TextEditStyle,
TextVisuals, WidgetState,
},
};
#[derive(Debug, Clone)]
pub struct DefaultStyle;
impl DefaultStyle {
pub fn register(ctx: &Context) {
ctx.add_widget_theme::<ButtonStyle>(Self);
ctx.add_widget_theme::<SeparatorStyle>(Self);
ctx.add_widget_theme::<CheckboxStyle>(Self);
ctx.add_widget_theme::<TextEditStyle>(Self);
}
}
impl StyleProvider<ButtonStyle> for DefaultStyle {
fn style(&mut self, modifiers: &StyleArgs<'_>) -> ButtonStyle {
let StyleArgs {
classes,
style,
state,
..
} = modifiers;
let spacing = &style.spacing;
let mut widget_visuals = if classes.has_class(&ComboBox::CLASS_OPEN) {
style.visuals.widgets.open
} else {
*style.visuals.widgets.state(*state)
};
if classes.has_class(&Button::CLASS_SELECTED) {
let visuals = &style.visuals;
widget_visuals.weak_bg_fill = visuals.selection.bg_fill;
widget_visuals.bg_fill = visuals.selection.bg_fill;
widget_visuals.fg_stroke = visuals.selection.stroke;
}
let mut inner_margin: Margin = spacing.button_padding.into();
if classes.has_class(&Button::CLASS_SMALL) {
inner_margin.top = 0;
inner_margin.bottom = 0;
}
let painted_frame = Frame {
fill: widget_visuals.weak_bg_fill,
corner_radius: widget_visuals.corner_radius,
inner_margin,
..Default::default()
}
.apply_stroke_and_expansion_without_layout_shift(
widget_visuals.bg_stroke,
widget_visuals.expansion,
);
let has_frame = classes.has_class(&Button::CLASS_FRAME)
|| (!classes.has_class(&Button::CLASS_NO_FRAME) && style.visuals.button_frame);
let frame = if !has_frame {
Frame::new()
} else if classes.has_class(&Button::CLASS_HIDE_FRAME_WHEN_INACTIVE)
&& *state == WidgetState::Inactive
{
painted_frame.invisible()
} else {
painted_frame
};
let text_style = TextVisuals::from_widget_visuals(style, TextStyle::Body, &widget_visuals);
let image_tint = if classes.has_class(&Button::CLASS_IMAGE_TINT_FOLLOWS_TEXT_COLOR) {
text_style.color
} else {
Color32::WHITE
};
ButtonStyle {
atom_layout: AtomLayoutStyle {
min_size: if classes.has_class(&Button::CLASS_SMALL) {
Vec2::ZERO
} else {
Vec2::new(0.0, spacing.interact_size.y)
},
gap: spacing.icon_spacing,
frame,
text_style,
image_tint,
..Default::default()
},
}
}
}
impl StyleProvider<TextEditStyle> for DefaultStyle {
fn style(&mut self, modifiers: &StyleArgs<'_>) -> TextEditStyle {
let StyleArgs {
classes,
style,
state,
..
} = modifiers;
let widget_visuals = style.visuals.widgets.state(*state);
let read_only = classes.has_class(&TextEdit::CLASS_READ_ONLY);
let fill = if read_only {
Color32::TRANSPARENT
} else {
style.visuals.text_edit_bg_color()
};
let stroke = if read_only {
style.visuals.widgets.inactive.bg_stroke
} else if *state == WidgetState::Active {
style.visuals.selection.stroke
} else {
widget_visuals.bg_stroke
};
let text = TextVisuals::from_widget_visuals(
style,
TextStyle::Body,
&style.visuals.widgets.inactive,
);
TextEditStyle {
atom_layout: AtomLayoutStyle {
frame: Frame {
fill,
corner_radius: widget_visuals.corner_radius,
inner_margin: style.spacing.button_padding.into(),
..Default::default()
}
.apply_stroke_and_expansion_without_layout_shift(stroke, widget_visuals.expansion),
min_size: Vec2::new(0.0, style.spacing.interact_size.y),
gap: style.spacing.icon_spacing,
text_style: text,
align2: Some(Align2::LEFT_TOP),
..Default::default()
},
hint_text_color: style.visuals.weak_text_color(),
prefix_suffix_color: style.visuals.text_color(),
}
}
}
impl StyleProvider<CheckboxStyle> for DefaultStyle {
fn style(&mut self, modifiers: &StyleArgs<'_>) -> CheckboxStyle {
let StyleArgs { style, state, .. } = modifiers;
let spacing = &style.spacing;
let widget_visuals = *style.visuals.widgets.state(*state);
CheckboxStyle {
atom_layout: AtomLayoutStyle {
min_size: Vec2::splat(spacing.interact_size.y),
gap: spacing.icon_spacing,
frame: Frame::new(),
text_style: TextVisuals::from_widget_visuals(
style,
TextStyle::Body,
&widget_visuals,
),
..Default::default()
},
checkbox_size: spacing.icon_width,
check_size: spacing.icon_width_inner,
checkbox_frame: Frame {
fill: widget_visuals.bg_fill,
corner_radius: widget_visuals
.corner_radius
.at_most((0.3 * spacing.icon_width).round() as u8),
stroke: widget_visuals.bg_stroke,
..Default::default()
},
check_stroke: widget_visuals.fg_stroke,
}
}
}
impl StyleProvider<SeparatorStyle> for DefaultStyle {
fn style(&mut self, modifiers: &StyleArgs<'_>) -> SeparatorStyle {
let StyleArgs { style, .. } = modifiers;
SeparatorStyle {
spacing: 6.0,
stroke: style.visuals.widgets.noninteractive.bg_stroke,
}
}
}