use teksilo_canvas::{Canvas, Rect, SizeProposal};
use crate::accessibility::AccessNodeBuilder;
use crate::build_context::BuildContext;
use crate::widget::{LayoutContext, PaintContext, PendingChild, Widget, WidgetPlacement};
use crate::widget_builder::HandlerSet;
use crate::widget_id::WidgetId;
#[derive(Debug)]
pub enum TeksiBranch<L: Widget, R: Widget> {
L(L),
R(R),
}
impl<L: Widget, R: Widget> Widget for TeksiBranch<L, R> {
fn build(&mut self, ctx: &mut BuildContext) -> Vec<WidgetId> {
match self {
TeksiBranch::L(w) => w.build(ctx),
TeksiBranch::R(w) => w.build(ctx),
}
}
fn layout_response(
&self,
proposal: SizeProposal,
ctx: &LayoutContext,
) -> crate::widget::LayoutResponse {
match self {
TeksiBranch::L(w) => w.layout_response(proposal, ctx),
TeksiBranch::R(w) => w.layout_response(proposal, ctx),
}
}
fn place_children(
&self,
bounds: Rect,
proposal: SizeProposal,
children: &mut [WidgetPlacement],
ctx: &LayoutContext,
) {
match self {
TeksiBranch::L(w) => w.place_children(bounds, proposal, children, ctx),
TeksiBranch::R(w) => w.place_children(bounds, proposal, children, ctx),
}
}
fn paint(&self, bounds: Rect, canvas: &mut Canvas, ctx: &PaintContext) {
match self {
TeksiBranch::L(w) => w.paint(bounds, canvas, ctx),
TeksiBranch::R(w) => w.paint(bounds, canvas, ctx),
}
}
fn accessibility(&self, builder: &mut AccessNodeBuilder) {
match self {
TeksiBranch::L(w) => w.accessibility(builder),
TeksiBranch::R(w) => w.accessibility(builder),
}
}
fn accessible_title_hint(&self) -> Option<String> {
match self {
TeksiBranch::L(w) => w.accessible_title_hint(),
TeksiBranch::R(w) => w.accessible_title_hint(),
}
}
fn initial_focus_hint(&self) -> Option<WidgetId> {
match self {
TeksiBranch::L(w) => w.initial_focus_hint(),
TeksiBranch::R(w) => w.initial_focus_hint(),
}
}
fn children(&self) -> Vec<WidgetId> {
match self {
TeksiBranch::L(w) => w.children(),
TeksiBranch::R(w) => w.children(),
}
}
fn clips_children(&self) -> bool {
match self {
TeksiBranch::L(w) => w.clips_children(),
TeksiBranch::R(w) => w.clips_children(),
}
}
fn take_handler_set(&mut self) -> Option<HandlerSet> {
match self {
TeksiBranch::L(w) => w.take_handler_set(),
TeksiBranch::R(w) => w.take_handler_set(),
}
}
}
#[derive(Debug)]
pub enum TeksiBranch3<A: Widget, B: Widget, C: Widget> {
A(A),
B(B),
C(C),
}
impl<A: Widget, B: Widget, C: Widget> Widget for TeksiBranch3<A, B, C> {
fn build(&mut self, ctx: &mut BuildContext) -> Vec<WidgetId> {
match self {
TeksiBranch3::A(w) => w.build(ctx),
TeksiBranch3::B(w) => w.build(ctx),
TeksiBranch3::C(w) => w.build(ctx),
}
}
fn layout_response(
&self,
proposal: SizeProposal,
ctx: &LayoutContext,
) -> crate::widget::LayoutResponse {
match self {
TeksiBranch3::A(w) => w.layout_response(proposal, ctx),
TeksiBranch3::B(w) => w.layout_response(proposal, ctx),
TeksiBranch3::C(w) => w.layout_response(proposal, ctx),
}
}
fn place_children(
&self,
bounds: Rect,
proposal: SizeProposal,
children: &mut [WidgetPlacement],
ctx: &LayoutContext,
) {
match self {
TeksiBranch3::A(w) => w.place_children(bounds, proposal, children, ctx),
TeksiBranch3::B(w) => w.place_children(bounds, proposal, children, ctx),
TeksiBranch3::C(w) => w.place_children(bounds, proposal, children, ctx),
}
}
fn paint(&self, bounds: Rect, canvas: &mut Canvas, ctx: &PaintContext) {
match self {
TeksiBranch3::A(w) => w.paint(bounds, canvas, ctx),
TeksiBranch3::B(w) => w.paint(bounds, canvas, ctx),
TeksiBranch3::C(w) => w.paint(bounds, canvas, ctx),
}
}
fn accessibility(&self, builder: &mut AccessNodeBuilder) {
match self {
TeksiBranch3::A(w) => w.accessibility(builder),
TeksiBranch3::B(w) => w.accessibility(builder),
TeksiBranch3::C(w) => w.accessibility(builder),
}
}
fn accessible_title_hint(&self) -> Option<String> {
match self {
TeksiBranch3::A(w) => w.accessible_title_hint(),
TeksiBranch3::B(w) => w.accessible_title_hint(),
TeksiBranch3::C(w) => w.accessible_title_hint(),
}
}
fn initial_focus_hint(&self) -> Option<WidgetId> {
match self {
TeksiBranch3::A(w) => w.initial_focus_hint(),
TeksiBranch3::B(w) => w.initial_focus_hint(),
TeksiBranch3::C(w) => w.initial_focus_hint(),
}
}
fn children(&self) -> Vec<WidgetId> {
match self {
TeksiBranch3::A(w) => w.children(),
TeksiBranch3::B(w) => w.children(),
TeksiBranch3::C(w) => w.children(),
}
}
fn clips_children(&self) -> bool {
match self {
TeksiBranch3::A(w) => w.clips_children(),
TeksiBranch3::B(w) => w.clips_children(),
TeksiBranch3::C(w) => w.clips_children(),
}
}
fn take_handler_set(&mut self) -> Option<HandlerSet> {
match self {
TeksiBranch3::A(w) => w.take_handler_set(),
TeksiBranch3::B(w) => w.take_handler_set(),
TeksiBranch3::C(w) => w.take_handler_set(),
}
}
}
#[derive(Debug)]
pub enum TeksiBranch4<A: Widget, B: Widget, C: Widget, D: Widget> {
A(A),
B(B),
C(C),
D(D),
}
impl<A: Widget, B: Widget, C: Widget, D: Widget> Widget for TeksiBranch4<A, B, C, D> {
fn build(&mut self, ctx: &mut BuildContext) -> Vec<WidgetId> {
match self {
TeksiBranch4::A(w) => w.build(ctx),
TeksiBranch4::B(w) => w.build(ctx),
TeksiBranch4::C(w) => w.build(ctx),
TeksiBranch4::D(w) => w.build(ctx),
}
}
fn layout_response(
&self,
proposal: SizeProposal,
ctx: &LayoutContext,
) -> crate::widget::LayoutResponse {
match self {
TeksiBranch4::A(w) => w.layout_response(proposal, ctx),
TeksiBranch4::B(w) => w.layout_response(proposal, ctx),
TeksiBranch4::C(w) => w.layout_response(proposal, ctx),
TeksiBranch4::D(w) => w.layout_response(proposal, ctx),
}
}
fn place_children(
&self,
bounds: Rect,
proposal: SizeProposal,
children: &mut [WidgetPlacement],
ctx: &LayoutContext,
) {
match self {
TeksiBranch4::A(w) => w.place_children(bounds, proposal, children, ctx),
TeksiBranch4::B(w) => w.place_children(bounds, proposal, children, ctx),
TeksiBranch4::C(w) => w.place_children(bounds, proposal, children, ctx),
TeksiBranch4::D(w) => w.place_children(bounds, proposal, children, ctx),
}
}
fn paint(&self, bounds: Rect, canvas: &mut Canvas, ctx: &PaintContext) {
match self {
TeksiBranch4::A(w) => w.paint(bounds, canvas, ctx),
TeksiBranch4::B(w) => w.paint(bounds, canvas, ctx),
TeksiBranch4::C(w) => w.paint(bounds, canvas, ctx),
TeksiBranch4::D(w) => w.paint(bounds, canvas, ctx),
}
}
fn accessibility(&self, builder: &mut AccessNodeBuilder) {
match self {
TeksiBranch4::A(w) => w.accessibility(builder),
TeksiBranch4::B(w) => w.accessibility(builder),
TeksiBranch4::C(w) => w.accessibility(builder),
TeksiBranch4::D(w) => w.accessibility(builder),
}
}
fn accessible_title_hint(&self) -> Option<String> {
match self {
TeksiBranch4::A(w) => w.accessible_title_hint(),
TeksiBranch4::B(w) => w.accessible_title_hint(),
TeksiBranch4::C(w) => w.accessible_title_hint(),
TeksiBranch4::D(w) => w.accessible_title_hint(),
}
}
fn initial_focus_hint(&self) -> Option<WidgetId> {
match self {
TeksiBranch4::A(w) => w.initial_focus_hint(),
TeksiBranch4::B(w) => w.initial_focus_hint(),
TeksiBranch4::C(w) => w.initial_focus_hint(),
TeksiBranch4::D(w) => w.initial_focus_hint(),
}
}
fn children(&self) -> Vec<WidgetId> {
match self {
TeksiBranch4::A(w) => w.children(),
TeksiBranch4::B(w) => w.children(),
TeksiBranch4::C(w) => w.children(),
TeksiBranch4::D(w) => w.children(),
}
}
fn clips_children(&self) -> bool {
match self {
TeksiBranch4::A(w) => w.clips_children(),
TeksiBranch4::B(w) => w.clips_children(),
TeksiBranch4::C(w) => w.clips_children(),
TeksiBranch4::D(w) => w.clips_children(),
}
}
fn take_handler_set(&mut self) -> Option<HandlerSet> {
match self {
TeksiBranch4::A(w) => w.take_handler_set(),
TeksiBranch4::B(w) => w.take_handler_set(),
TeksiBranch4::C(w) => w.take_handler_set(),
TeksiBranch4::D(w) => w.take_handler_set(),
}
}
}
pub trait IntoTeksiChild {
fn into_pending(self) -> PendingChild;
}
impl<W: Widget + 'static> IntoTeksiChild for W {
fn into_pending(self) -> PendingChild {
PendingChild::Deferred(Box::new(self))
}
}
impl IntoTeksiChild for WidgetId {
fn into_pending(self) -> PendingChild {
PendingChild::Id(self)
}
}
pub trait IntoTeksiCondition {
fn teksilo_into_conditional_child<W: crate::widget::Widget + 'static>(
self,
child: W,
ctx: &mut crate::build_context::BuildContext,
) -> Option<WidgetId>;
}
impl IntoTeksiCondition for bool {
fn teksilo_into_conditional_child<W: crate::widget::Widget + 'static>(
self,
child: W,
ctx: &mut crate::build_context::BuildContext,
) -> Option<WidgetId> {
if self { Some(ctx.add(child)) } else { None }
}
}
impl IntoTeksiCondition for crate::signal::Signal<bool> {
fn teksilo_into_conditional_child<W: crate::widget::Widget + 'static>(
self,
child: W,
ctx: &mut crate::build_context::BuildContext,
) -> Option<WidgetId> {
let id = ctx.add(child);
ctx.visible_when(id, self);
Some(id)
}
}
impl IntoTeksiCondition for crate::signal::Prop<bool> {
fn teksilo_into_conditional_child<W: crate::widget::Widget + 'static>(
self,
child: W,
ctx: &mut crate::build_context::BuildContext,
) -> Option<WidgetId> {
let id = ctx.add(child);
ctx.visible_when(id, self);
Some(id)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_widgets::FillWidget;
use crate::widget_tree::WidgetTree;
use teksilo_canvas::SizeProposal;
use teksilo_tokens::Color;
#[test]
fn teksilo_branch_dispatches_to_active_variant() {
let mut tree_l = WidgetTree::new();
let id_l = tree_l.add(TeksiBranch::<FillWidget, FillWidget>::L(
FillWidget::new().background(Color::RED),
));
tree_l.layout(SizeProposal::exact(100.0, 50.0));
assert!((tree_l.bounds(id_l).width - 100.0).abs() < 0.01);
let mut tree_r = WidgetTree::new();
let id_r = tree_r.add(TeksiBranch::<FillWidget, FillWidget>::R(
FillWidget::new().background(Color::BLUE),
));
tree_r.layout(SizeProposal::exact(80.0, 40.0));
assert!((tree_r.bounds(id_r).width - 80.0).abs() < 0.01);
}
#[test]
fn teksilo_branch3_dispatches_to_active_variant() {
let mut tree = WidgetTree::new();
let id = tree.add(TeksiBranch3::<FillWidget, FillWidget, FillWidget>::B(
FillWidget::new(),
));
tree.layout(SizeProposal::exact(120.0, 60.0));
assert!((tree.bounds(id).width - 120.0).abs() < 0.01);
}
#[test]
fn into_teksilo_child_routes_widget_to_deferred() {
let pending = FillWidget::new().into_pending();
assert!(matches!(pending, PendingChild::Deferred(_)));
}
#[test]
fn into_teksilo_child_routes_widget_id_to_id() {
let mut tree = WidgetTree::new();
let leaf = tree.add(FillWidget::new());
let pending = leaf.into_pending();
match pending {
PendingChild::Id(id) => assert_eq!(id, leaf),
_ => panic!("expected PendingChild::Id"),
}
}
}