use std::any::Any;
use crate::element::Element;
use crate::geometry::{Corners, Rect, Size};
use crate::paint::PaintOp;
use crate::semantics::Semantics;
use crate::style::{Edges, Style};
use crate::theme::Theme;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AvailableSpace {
Definite(f32),
MinContent,
MaxContent,
}
#[derive(Clone, Copy, Debug)]
pub struct MeasureInput {
pub known_width: Option<f32>,
pub known_height: Option<f32>,
pub available_width: AvailableSpace,
pub available_height: AvailableSpace,
}
pub trait Widget: Any + type_name::TypeName {
fn measure(&mut self, _input: MeasureInput) -> Option<Size> {
None
}
fn paint(&self, cx: &PaintCx<'_>, out: &mut Vec<PaintOp>);
fn ime_area(&self) -> Option<Rect> {
None
}
fn semantics(&self) -> Option<Semantics> {
None
}
}
pub(crate) mod type_name {
pub trait TypeName {
fn type_name(&self) -> &'static str;
}
impl<T> TypeName for T {
fn type_name(&self) -> &'static str {
std::any::type_name::<T>()
}
}
}
pub struct PaintCx<'a> {
pub rect: Rect,
pub content: Rect,
pub style: &'a Style,
pub theme: &'a Theme,
}
pub struct Panel;
pub fn panel() -> Element<Panel> {
Element::new(Panel)
}
impl Widget for Panel {
fn paint(&self, cx: &PaintCx<'_>, out: &mut Vec<PaintOp>) {
let paint = &cx.style.paint;
if let Some(background) = &paint.background {
out.push(PaintOp::FillRect {
rect: cx.rect,
radius: Corners::all(paint.corner_radius.resolve(cx.theme)),
color: background.resolve(cx.theme),
});
}
if let Some(border) = &paint.border_color {
let width = widest_side(cx.style.layout.border_width);
if width > 0.0 {
out.push(PaintOp::StrokeRect {
rect: cx.rect,
radius: Corners::all(paint.corner_radius.resolve(cx.theme)),
width,
color: border.resolve(cx.theme),
});
}
}
}
}
fn widest_side(border: Edges<f32>) -> f32 {
border
.top
.max(border.right)
.max(border.bottom)
.max(border.left)
}
#[cfg(test)]
#[path = "widget_tests.rs"]
mod tests;