use crate::runtime::{
ActionId, ContextId, Cx, DragId, FocusId, KeyInput, ScrollId, TextPosId, ViewportId,
};
use stipple_geometry::Insets;
use stipple_layout::Axis;
use stipple_render::Color;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum Align {
#[default]
Start,
Center,
End,
Stretch,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct SizeOverride {
pub width: Option<f64>,
pub height: Option<f64>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct LayoutStyle {
pub padding: Insets,
pub size: SizeOverride,
pub grow: f64,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct BoxStyle {
pub fill: Option<Color>,
pub radius: f64,
pub border: Option<(Color, f64)>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ElementKind {
Leaf,
Text {
text: String,
size: f64,
color: Color,
},
Stack {
axis: Axis,
gap: f64,
main_align: Align,
cross_align: Align,
children: Vec<Element>,
},
Viewport { id: ViewportId },
}
#[derive(Clone, Debug, PartialEq)]
pub struct Element {
pub layout: LayoutStyle,
pub decoration: BoxStyle,
pub action: Option<ActionId>,
pub focus: Option<FocusId>,
pub drag: Option<DragId>,
pub context: Option<ContextId>,
pub caret: Option<usize>,
pub selection: Option<(usize, usize)>,
pub text_pos: Option<TextPosId>,
pub wrap: bool,
pub scroll: Option<ScrollId>,
pub clip: bool,
pub kind: ElementKind,
}
impl Element {
pub fn boxed(style: BoxStyle) -> Self {
Self {
layout: LayoutStyle::default(),
decoration: style,
action: None,
focus: None,
drag: None,
context: None,
caret: None,
selection: None,
text_pos: None,
wrap: false,
scroll: None,
clip: false,
kind: ElementKind::Leaf,
}
}
pub fn text(text: impl Into<String>, size: f64, color: Color) -> Self {
Self {
layout: LayoutStyle::default(),
decoration: BoxStyle::default(),
action: None,
focus: None,
drag: None,
context: None,
caret: None,
selection: None,
text_pos: None,
wrap: false,
scroll: None,
clip: false,
kind: ElementKind::Text {
text: text.into(),
size,
color,
},
}
}
pub fn viewport(id: ViewportId) -> Self {
Self {
layout: LayoutStyle::default(),
decoration: BoxStyle::default(),
action: None,
focus: None,
drag: None,
context: None,
caret: None,
selection: None,
text_pos: None,
wrap: false,
scroll: None,
clip: false,
kind: ElementKind::Viewport { id },
}
}
pub fn stack(axis: Axis, children: Vec<Element>) -> Self {
Self {
layout: LayoutStyle::default(),
decoration: BoxStyle::default(),
action: None,
focus: None,
drag: None,
context: None,
caret: None,
selection: None,
text_pos: None,
wrap: false,
scroll: None,
clip: false,
kind: ElementKind::Stack {
axis,
gap: 0.0,
main_align: Align::Start,
cross_align: Align::Start,
children,
},
}
}
pub fn on_tap<S>(mut self, cx: &mut Cx<'_, S>, handler: impl FnMut(&mut S) + 'static) -> Self {
self.action = Some(cx.register(handler));
self
}
pub fn on_context<S>(
mut self,
cx: &mut Cx<'_, S>,
handler: impl FnMut(&mut S, stipple_geometry::Point) + 'static,
) -> Self {
self.context = Some(cx.register_context(handler));
self
}
pub fn on_key<S>(
mut self,
cx: &mut Cx<'_, S>,
handler: impl FnMut(&mut S, &KeyInput) + 'static,
) -> Self {
self.focus = Some(cx.register_key(handler));
self
}
pub fn on_drag<S>(
mut self,
cx: &mut Cx<'_, S>,
handler: impl FnMut(&mut S, f64) + 'static,
) -> Self {
self.drag = Some(cx.register_drag(handler));
self
}
pub fn on_text_pos<S>(
mut self,
cx: &mut Cx<'_, S>,
handler: impl FnMut(&mut S, usize, bool) + 'static,
) -> Self {
self.text_pos = Some(cx.register_text_pos(handler));
self
}
pub fn fill(mut self, color: Color) -> Self {
self.decoration.fill = Some(color);
self
}
pub fn radius(mut self, radius: f64) -> Self {
self.decoration.radius = radius;
self
}
pub fn border(mut self, color: Color, width: f64) -> Self {
self.decoration.border = Some((color, width));
self
}
pub fn padding(mut self, insets: Insets) -> Self {
self.layout.padding = insets;
self
}
pub fn width(mut self, w: f64) -> Self {
self.layout.size.width = Some(w);
self
}
pub fn height(mut self, h: f64) -> Self {
self.layout.size.height = Some(h);
self
}
pub fn grow(mut self, grow: f64) -> Self {
self.layout.grow = grow;
self
}
pub fn caret(mut self, byte_index: usize) -> Self {
self.caret = Some(byte_index);
self
}
pub fn selection(mut self, range: Option<(usize, usize)>) -> Self {
self.selection = range.filter(|(s, e)| e > s);
self
}
pub fn wrap(mut self) -> Self {
self.wrap = true;
self
}
pub fn clip(mut self) -> Self {
self.clip = true;
self
}
pub fn scrollable(mut self, id: ScrollId) -> Self {
self.scroll = Some(id);
self.clip = true;
self
}
pub fn gap(mut self, gap: f64) -> Self {
if let ElementKind::Stack { gap: g, .. } = &mut self.kind {
*g = gap;
}
self
}
pub fn align(mut self, main: Align, cross: Align) -> Self {
if let ElementKind::Stack {
main_align,
cross_align,
..
} = &mut self.kind
{
*main_align = main;
*cross_align = cross;
}
self
}
}