#![cfg_attr(not(feature = "experimental"), allow(dead_code, unused_imports))]
use core::fmt::Debug;
use emath::{Align2, Vec2};
use epaint::{Color32, FontId, Stroke};
use crate::{
Context, FontSelection, Frame, Response, Style, UiStack, WidgetAtom,
class::{Classes, HasClasses as _},
style::{WidgetVisuals, Widgets},
};
pub trait WidgetStyle: Debug + Clone + Send + Sync + core::any::Any + 'static {}
#[derive(Debug, Clone)]
pub struct TextVisuals {
pub font_id: FontId,
pub color: Color32,
}
impl TextVisuals {
pub fn new(style: &Style, font: impl Into<FontSelection>, color: Color32) -> Self {
Self {
color,
font_id: style
.override_font_id
.clone()
.unwrap_or_else(|| font.into().resolve(style)),
}
}
pub fn from_widget_visuals(
style: &Style,
font: impl Into<FontSelection>,
widget_visuals: &WidgetVisuals,
) -> Self {
Self::new(style, font, widget_visuals.text_color())
}
}
#[derive(Debug, Clone)]
pub struct AtomLayoutStyle {
pub align2: Option<Align2>,
pub min_size: Vec2,
pub gap: f32,
pub frame: Frame,
pub text_style: TextVisuals,
pub image_tint: Color32,
}
impl Default for AtomLayoutStyle {
fn default() -> Self {
Self {
align2: None,
min_size: Vec2::ZERO,
gap: 0.0,
frame: Frame::default(),
text_style: TextVisuals {
font_id: FontId::default(),
color: Color32::WHITE,
},
image_tint: Color32::WHITE,
}
}
}
impl AtomLayoutStyle {
pub fn apply(self, mut layout: WidgetAtom<'_>) -> WidgetAtom<'_> {
let Self {
align2,
min_size,
gap,
frame,
text_style,
image_tint,
} = self;
layout.map_images(|image| {
let current_tint = image.image_options().tint;
image.tint(current_tint * image_tint)
});
let layout = layout
.min_size(min_size)
.fallback_gap(gap)
.frame(frame)
.fallback_font(text_style.font_id)
.fallback_text_color(text_style.color);
if let Some(align2) = align2 {
layout.align2(align2)
} else {
layout
}
}
}
#[derive(Debug, Clone)]
pub struct ButtonStyle {
pub atom_layout: AtomLayoutStyle,
}
impl WidgetStyle for ButtonStyle {}
#[derive(Debug, Clone)]
pub struct TextEditStyle {
pub atom_layout: AtomLayoutStyle,
pub hint_text_color: Color32,
pub prefix_suffix_color: Color32,
}
impl WidgetStyle for TextEditStyle {}
#[derive(Debug, Clone)]
pub struct CheckboxStyle {
pub atom_layout: AtomLayoutStyle,
pub checkbox_size: f32,
pub check_size: f32,
pub checkbox_frame: Frame,
pub check_stroke: Stroke,
}
impl WidgetStyle for CheckboxStyle {}
#[derive(Debug, Clone)]
pub struct SeparatorStyle {
pub spacing: f32,
pub stroke: Stroke,
}
impl WidgetStyle for SeparatorStyle {}
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum WidgetState {
Noninteractive,
#[default]
Inactive,
Hovered,
Active,
}
impl Widgets {
pub fn state(&self, state: WidgetState) -> &WidgetVisuals {
match state {
WidgetState::Noninteractive => &self.noninteractive,
WidgetState::Inactive => &self.inactive,
WidgetState::Hovered => &self.hovered,
WidgetState::Active => &self.active,
}
}
}
impl Response {
pub fn widget_state(&self) -> WidgetState {
if !self.sense.interactive() {
WidgetState::Noninteractive
} else if self.is_pointer_button_down_on() || self.has_focus() || self.clicked() {
WidgetState::Active
} else if self.hovered() || self.highlighted() {
WidgetState::Hovered
} else {
WidgetState::Inactive
}
}
}
pub struct StyleArgs<'a> {
pub classes: &'a Classes,
pub state: WidgetState,
pub stack: &'a UiStack,
pub style: &'a Style,
pub ctx: &'a Context,
}
impl StyleArgs<'_> {
pub fn has_class(&self, class: &str) -> bool {
self.classes.has_class(class) || self.stack.has_class(class)
}
}