use std::any::Any;
use std::clone::Clone;
use std::fmt::{self, Debug};
use std::ops::{ControlFlow, Deref, DerefMut};
use std::sync::atomic::{self, AtomicU64};
use std::sync::Arc;
use std::{slice, vec};
use alot::LotId;
use figures::units::{Px, UPx};
use figures::{IntoSigned, IntoUnsigned, Point, Rect, Size, Zero};
use intentional::Assert;
use kludgine::app::winit::event::{Ime, MouseButton, MouseScrollDelta, TouchPhase};
use kludgine::app::winit::window::CursorIcon;
use kludgine::Color;
use parking_lot::{Mutex, MutexGuard};
use crate::app::{Application, Open, PendingApp, Run};
use crate::context::sealed::Trackable as _;
use crate::context::{
AsEventContext, EventContext, GraphicsContext, LayoutContext, ManageWidget, WidgetContext,
};
use crate::styles::components::{
FontFamily, FontStyle, FontWeight, Heading1FontFamily, Heading1Style, Heading1Weight,
Heading2FontFamily, Heading2Style, Heading2Weight, Heading3FontFamily, Heading3Style,
Heading3Weight, Heading4FontFamily, Heading4Style, Heading4Weight, Heading5FontFamily,
Heading5Style, Heading5Weight, Heading6FontFamily, Heading6Style, Heading6Weight, LineHeight,
LineHeight1, LineHeight2, LineHeight3, LineHeight4, LineHeight5, LineHeight6, LineHeight7,
LineHeight8, TextSize, TextSize1, TextSize2, TextSize3, TextSize4, TextSize5, TextSize6,
TextSize7, TextSize8,
};
use crate::styles::{
ComponentDefinition, ContainerLevel, Dimension, DimensionRange, Edges, IntoComponentValue,
IntoDynamicComponentValue, Styles, ThemePair, VisualOrder,
};
use crate::tree::{Tree, WeakTree};
use crate::value::{Dynamic, Generation, IntoDynamic, IntoValue, Validation, Value};
use crate::widgets::checkbox::{Checkable, CheckboxState};
use crate::widgets::layers::{OverlayLayer, Tooltipped};
use crate::widgets::list::List;
use crate::widgets::{
Align, Button, Checkbox, Collapse, Container, Disclose, Expand, Layers, Resize, Scroll, Space,
Stack, Style, Themed, ThemedMode, Validated, Wrap,
};
use crate::window::sealed::WindowCommand;
use crate::window::{
DeviceId, KeyEvent, Rgb8, RunningWindow, StandaloneWindowBuilder, ThemeMode,
VirtualRecorderBuilder, Window, WindowBehavior, WindowHandle, WindowLocal,
};
use crate::ConstraintLimit;
pub trait Widget: Send + Debug + 'static {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>);
fn summarize(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(self, f)
}
fn full_control_redraw(&self) -> bool {
false
}
#[allow(unused_variables)]
fn layout(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> {
available_space.map(ConstraintLimit::min)
}
#[allow(unused_variables)]
fn mounted(&mut self, context: &mut EventContext<'_>) {}
#[allow(unused_variables)]
fn unmounted(&mut self, context: &mut EventContext<'_>) {}
#[allow(unused_variables)]
fn hit_test(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> bool {
false
}
#[allow(unused_variables)]
fn hover(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> Option<CursorIcon> {
None
}
#[allow(unused_variables)]
fn unhover(&mut self, context: &mut EventContext<'_>) {}
#[allow(unused_variables)]
fn accept_focus(&mut self, context: &mut EventContext<'_>) -> bool {
false
}
#[allow(unused_variables)]
fn focus(&mut self, context: &mut EventContext<'_>) {}
#[allow(unused_variables)]
fn advance_focus(
&mut self,
direction: VisualOrder,
context: &mut EventContext<'_>,
) -> EventHandling {
IGNORED
}
#[allow(unused_variables)]
fn allow_blur(&mut self, context: &mut EventContext<'_>) -> bool {
true
}
#[allow(unused_variables)]
fn blur(&mut self, context: &mut EventContext<'_>) {}
#[allow(unused_variables)]
fn activate(&mut self, context: &mut EventContext<'_>) {}
#[allow(unused_variables)]
fn deactivate(&mut self, context: &mut EventContext<'_>) {}
#[allow(unused_variables)]
fn mouse_down(
&mut self,
location: Point<Px>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_>,
) -> EventHandling {
IGNORED
}
#[allow(unused_variables)]
fn mouse_drag(
&mut self,
location: Point<Px>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_>,
) {
}
#[allow(unused_variables)]
fn mouse_up(
&mut self,
location: Option<Point<Px>>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_>,
) {
}
#[allow(unused_variables)]
fn keyboard_input(
&mut self,
device_id: DeviceId,
input: KeyEvent,
is_synthetic: bool,
context: &mut EventContext<'_>,
) -> EventHandling {
IGNORED
}
#[allow(unused_variables)]
fn ime(&mut self, ime: Ime, context: &mut EventContext<'_>) -> EventHandling {
IGNORED
}
#[allow(unused_variables)]
fn mouse_wheel(
&mut self,
device_id: DeviceId,
delta: MouseScrollDelta,
phase: TouchPhase,
context: &mut EventContext<'_>,
) -> EventHandling {
IGNORED
}
#[must_use]
#[allow(unused_variables)]
fn root_behavior(
&mut self,
context: &mut EventContext<'_>,
) -> Option<(RootBehavior, WidgetInstance)> {
None
}
}
impl<T> Run for T
where
T: MakeWidget,
{
fn run(self) -> crate::Result {
Window::for_widget(self).run()
}
}
impl<T> Open for T
where
T: MakeWidget,
{
fn open<App>(self, app: &mut App) -> crate::Result<Option<crate::window::WindowHandle>>
where
App: Application + ?Sized,
{
Window::<WidgetInstance>::new(self.make_widget()).open(app)
}
fn run_in(self, mut app: PendingApp) -> crate::Result {
Window::<WidgetInstance>::new(self.make_widget()).open(&mut app)?;
app.run()
}
}
#[derive(Debug, Clone, Copy)]
pub enum RootBehavior {
PassThrough,
Expand,
Align,
Pad(Edges<Dimension>),
Resize(Size<DimensionRange>),
}
#[derive(Clone, Copy, Debug)]
pub struct WrappedLayout {
pub child: Rect<Px>,
pub size: Size<UPx>,
}
impl From<Size<Px>> for WrappedLayout {
fn from(size: Size<Px>) -> Self {
WrappedLayout {
child: size.into(),
size: size.into_unsigned(),
}
}
}
impl From<Size<UPx>> for WrappedLayout {
fn from(size: Size<UPx>) -> Self {
WrappedLayout {
child: size.into_signed().into(),
size,
}
}
}
pub trait WrapperWidget: Debug + Send + 'static {
fn child_mut(&mut self) -> &mut WidgetRef;
fn summarize(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(self, f)
}
#[allow(unused_variables)]
fn root_behavior(&mut self, context: &mut EventContext<'_>) -> Option<RootBehavior> {
None
}
#[allow(unused_variables)]
fn redraw_background(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {}
#[allow(unused_variables)]
fn redraw_foreground(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {}
#[allow(unused_variables)]
fn layout_child(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout {
let adjusted_space = self.adjust_child_constraints(available_space, context);
let child = self.child_mut().mounted(&mut context.as_event_context());
let size = context
.for_other(&child)
.layout(adjusted_space)
.into_signed();
self.position_child(size, available_space, context)
}
#[allow(unused_variables)]
#[must_use]
fn adjust_child_constraints(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<ConstraintLimit> {
available_space
}
#[allow(unused_variables)]
#[must_use]
fn position_child(
&mut self,
size: Size<Px>,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout {
Size::new(
available_space
.width
.fit_measured(size.width, context.gfx.scale()),
available_space
.height
.fit_measured(size.height, context.gfx.scale()),
)
.into()
}
#[allow(unused_variables)]
#[must_use]
fn background_color(&mut self, context: &WidgetContext<'_>) -> Option<Color> {
None
}
#[allow(unused_variables)]
fn mounted(&mut self, context: &mut EventContext<'_>) {}
#[allow(unused_variables)]
fn unmounted(&mut self, context: &mut EventContext<'_>) {
self.child_mut().unmount_in(context);
}
#[allow(unused_variables)]
fn hit_test(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> bool {
false
}
#[allow(unused_variables)]
fn hover(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> Option<CursorIcon> {
None
}
#[allow(unused_variables)]
fn unhover(&mut self, context: &mut EventContext<'_>) {}
#[allow(unused_variables)]
fn accept_focus(&mut self, context: &mut EventContext<'_>) -> bool {
false
}
#[allow(unused_variables)]
fn advance_focus(
&mut self,
direction: VisualOrder,
context: &mut EventContext<'_>,
) -> EventHandling {
IGNORED
}
#[allow(unused_variables)]
fn focus(&mut self, context: &mut EventContext<'_>) {}
#[allow(unused_variables)]
fn allow_blur(&mut self, context: &mut EventContext<'_>) -> bool {
true
}
#[allow(unused_variables)]
fn blur(&mut self, context: &mut EventContext<'_>) {}
#[allow(unused_variables)]
fn activate(&mut self, context: &mut EventContext<'_>) {}
#[allow(unused_variables)]
fn deactivate(&mut self, context: &mut EventContext<'_>) {}
#[allow(unused_variables)]
fn mouse_down(
&mut self,
location: Point<Px>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_>,
) -> EventHandling {
IGNORED
}
#[allow(unused_variables)]
fn mouse_drag(
&mut self,
location: Point<Px>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_>,
) {
}
#[allow(unused_variables)]
fn mouse_up(
&mut self,
location: Option<Point<Px>>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_>,
) {
}
#[allow(unused_variables)]
fn keyboard_input(
&mut self,
device_id: DeviceId,
input: KeyEvent,
is_synthetic: bool,
context: &mut EventContext<'_>,
) -> EventHandling {
IGNORED
}
#[allow(unused_variables)]
fn ime(&mut self, ime: Ime, context: &mut EventContext<'_>) -> EventHandling {
IGNORED
}
#[allow(unused_variables)]
fn mouse_wheel(
&mut self,
device_id: DeviceId,
delta: MouseScrollDelta,
phase: TouchPhase,
context: &mut EventContext<'_>,
) -> EventHandling {
IGNORED
}
}
impl<T> Widget for T
where
T: WrapperWidget,
{
fn root_behavior(
&mut self,
context: &mut EventContext<'_>,
) -> Option<(RootBehavior, WidgetInstance)> {
T::root_behavior(self, context)
.map(|behavior| (behavior, T::child_mut(self).widget().clone()))
}
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
let background_color = self.background_color(context);
if let Some(color) = background_color {
context.fill(color);
}
self.redraw_background(context);
let child = self.child_mut().mounted(&mut context.as_event_context());
context.for_other(&child).redraw();
self.redraw_foreground(context);
}
fn layout(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> {
let layout = self.layout_child(available_space, context);
let child = self.child_mut().mounted(&mut context.as_event_context());
context.set_child_layout(&child, layout.child);
layout.size
}
fn mounted(&mut self, context: &mut EventContext<'_>) {
T::mounted(self, context);
}
fn unmounted(&mut self, context: &mut EventContext<'_>) {
T::unmounted(self, context);
}
fn hit_test(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> bool {
T::hit_test(self, location, context)
}
fn hover(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> Option<CursorIcon> {
T::hover(self, location, context)
}
fn unhover(&mut self, context: &mut EventContext<'_>) {
T::unhover(self, context);
}
fn accept_focus(&mut self, context: &mut EventContext<'_>) -> bool {
T::accept_focus(self, context)
}
fn focus(&mut self, context: &mut EventContext<'_>) {
T::focus(self, context);
}
fn blur(&mut self, context: &mut EventContext<'_>) {
T::blur(self, context);
}
fn activate(&mut self, context: &mut EventContext<'_>) {
T::activate(self, context);
}
fn deactivate(&mut self, context: &mut EventContext<'_>) {
T::deactivate(self, context);
}
fn mouse_down(
&mut self,
location: Point<Px>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_>,
) -> EventHandling {
T::mouse_down(self, location, device_id, button, context)
}
fn mouse_drag(
&mut self,
location: Point<Px>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_>,
) {
T::mouse_drag(self, location, device_id, button, context);
}
fn mouse_up(
&mut self,
location: Option<Point<Px>>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_>,
) {
T::mouse_up(self, location, device_id, button, context);
}
fn keyboard_input(
&mut self,
device_id: DeviceId,
input: KeyEvent,
is_synthetic: bool,
context: &mut EventContext<'_>,
) -> EventHandling {
T::keyboard_input(self, device_id, input, is_synthetic, context)
}
fn ime(&mut self, ime: Ime, context: &mut EventContext<'_>) -> EventHandling {
T::ime(self, ime, context)
}
fn mouse_wheel(
&mut self,
device_id: DeviceId,
delta: MouseScrollDelta,
phase: TouchPhase,
context: &mut EventContext<'_>,
) -> EventHandling {
T::mouse_wheel(self, device_id, delta, phase, context)
}
fn advance_focus(
&mut self,
direction: VisualOrder,
context: &mut EventContext<'_>,
) -> EventHandling {
T::advance_focus(self, direction, context)
}
fn allow_blur(&mut self, context: &mut EventContext<'_>) -> bool {
T::allow_blur(self, context)
}
fn summarize(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
T::summarize(self, fmt)
}
}
pub trait MakeWidget: Sized {
fn make_widget(self) -> WidgetInstance;
fn into_window(self) -> Window<WidgetInstance> {
Window::new(self.make_widget())
}
fn build_standalone_window(self) -> StandaloneWindowBuilder {
StandaloneWindowBuilder::new(self)
}
fn build_recorder(self) -> VirtualRecorderBuilder<Rgb8> {
VirtualRecorderBuilder::new(self)
}
fn with_styles(self, styles: impl IntoValue<Styles>) -> Style
where
Self: Sized,
{
Style::new(styles, self)
}
fn with<C: ComponentDefinition>(
self,
name: &C,
component: impl IntoValue<C::ComponentType>,
) -> Style
where
Value<C::ComponentType>: IntoComponentValue,
{
Style::new(Styles::new().with(name, component), self)
}
fn with_dynamic<C: ComponentDefinition>(
self,
name: &C,
dynamic: impl IntoDynamicComponentValue,
) -> Style
where
Value<C::ComponentType>: IntoComponentValue,
{
Style::new(Styles::new().with_dynamic(name, dynamic), self)
}
fn h1(self) -> Style {
self.xxxx_large()
.with_dynamic(&FontStyle, Heading1Style)
.with_dynamic(&FontFamily, Heading1FontFamily)
.with_dynamic(&FontWeight, Heading1Weight)
}
fn h2(self) -> Style {
self.xxx_large()
.with_dynamic(&FontStyle, Heading2Style)
.with_dynamic(&FontFamily, Heading2FontFamily)
.with_dynamic(&FontWeight, Heading2Weight)
}
fn h3(self) -> Style {
self.xx_large()
.with_dynamic(&FontStyle, Heading3Style)
.with_dynamic(&FontFamily, Heading3FontFamily)
.with_dynamic(&FontWeight, Heading3Weight)
}
fn h4(self) -> Style {
self.x_large()
.with_dynamic(&FontStyle, Heading4Style)
.with_dynamic(&FontFamily, Heading4FontFamily)
.with_dynamic(&FontWeight, Heading4Weight)
}
fn h5(self) -> Style {
self.large()
.with_dynamic(&FontStyle, Heading5Style)
.with_dynamic(&FontFamily, Heading5FontFamily)
.with_dynamic(&FontWeight, Heading5Weight)
}
fn h6(self) -> Style {
self.default_size()
.with_dynamic(&FontStyle, Heading6Style)
.with_dynamic(&FontFamily, Heading6FontFamily)
.with_dynamic(&FontWeight, Heading6Weight)
}
#[must_use]
fn xxxx_large(self) -> Style {
self.with_dynamic(&TextSize, TextSize8)
.with_dynamic(&LineHeight, LineHeight8)
}
#[must_use]
fn xxx_large(self) -> Style {
self.with_dynamic(&TextSize, TextSize7)
.with_dynamic(&LineHeight, LineHeight7)
}
#[must_use]
fn xx_large(self) -> Style {
self.with_dynamic(&TextSize, TextSize6)
.with_dynamic(&LineHeight, LineHeight6)
}
#[must_use]
fn x_large(self) -> Style {
self.with_dynamic(&TextSize, TextSize5)
.with_dynamic(&LineHeight, LineHeight5)
}
#[must_use]
fn large(self) -> Style {
self.with_dynamic(&TextSize, TextSize4)
.with_dynamic(&LineHeight, LineHeight4)
}
#[must_use]
fn default_size(self) -> Style {
self.with_dynamic(&TextSize, TextSize3)
.with_dynamic(&LineHeight, LineHeight3)
}
#[must_use]
fn small(self) -> Style {
self.with_dynamic(&TextSize, TextSize2)
.with_dynamic(&LineHeight, LineHeight2)
}
#[must_use]
fn x_small(self) -> Style {
self.with_dynamic(&TextSize, TextSize1)
.with_dynamic(&LineHeight, LineHeight1)
}
fn with_next_focus(self, next_focus: impl IntoValue<Option<WidgetId>>) -> WidgetInstance {
self.make_widget().with_next_focus(next_focus)
}
fn with_enabled(self, enabled: impl IntoValue<bool>) -> WidgetInstance {
self.make_widget().with_enabled(enabled)
}
#[must_use]
fn into_default(self) -> WidgetInstance {
self.make_widget().into_default()
}
#[must_use]
fn into_escape(self) -> WidgetInstance {
self.make_widget().into_escape()
}
fn and(self, other: impl MakeWidget) -> WidgetList {
let mut children = WidgetList::new();
children.push(self);
children.push(other);
children
}
fn chain<W: MakeWidget>(self, others: impl IntoIterator<Item = W>) -> WidgetList {
let others = others.into_iter();
let mut widgets = WidgetList::with_capacity(others.size_hint().0 + 1);
widgets.push(self);
widgets.extend(others);
widgets
}
#[must_use]
fn expand(self) -> Expand {
Expand::new(self)
}
#[must_use]
fn expand_weighted(self, weight: u8) -> Expand {
Expand::weighted(weight, self)
}
#[must_use]
fn expand_horizontally(self) -> Expand {
Expand::horizontal(self)
}
#[must_use]
fn expand_vertically(self) -> Expand {
Expand::vertical(self)
}
#[must_use]
fn size<T>(self, size: Size<T>) -> Resize
where
T: Into<DimensionRange>,
{
Resize::to(size, self)
}
#[must_use]
fn width(self, width: impl Into<DimensionRange>) -> Resize {
Resize::from_width(width, self)
}
#[must_use]
fn height(self, height: impl Into<DimensionRange>) -> Resize {
Resize::from_height(height, self)
}
fn into_button(self) -> Button {
Button::new(self)
}
fn into_checkbox(self, value: impl IntoDynamic<CheckboxState>) -> Checkbox {
value.into_checkbox(self)
}
#[must_use]
fn centered(self) -> Align {
Align::centered(self)
}
fn align_left(self) -> Align {
self.centered().align_left()
}
fn align_right(self) -> Align {
self.centered().align_right()
}
fn align_top(self) -> Align {
self.centered().align_top()
}
fn align_bottom(self) -> Align {
self.centered().align_bottom()
}
fn fit_horizontally(self) -> Align {
self.centered().fit_horizontally()
}
fn fit_vertically(self) -> Align {
self.centered().fit_vertically()
}
#[must_use]
fn scroll(self) -> Scroll {
Scroll::new(self)
}
#[must_use]
fn vertical_scroll(self) -> Scroll {
Scroll::vertical(self)
}
#[must_use]
fn horizontal_scroll(self) -> Scroll {
Scroll::horizontal(self)
}
#[must_use]
fn widget_ref(self) -> WidgetRef {
WidgetRef::new(self)
}
fn contain(self) -> Container {
Container::new(self)
}
fn contain_level(self, level: impl IntoValue<ContainerLevel>) -> Container {
self.contain().contain_level(level)
}
fn background_color(self, color: impl IntoValue<Color>) -> Container {
self.contain().pad_by(Px::ZERO).background_color(color)
}
fn pad(self) -> Container {
self.contain().transparent()
}
fn pad_by(self, padding: impl IntoValue<Edges<Dimension>>) -> Container {
self.contain().transparent().pad_by(padding)
}
fn themed(self, theme: impl IntoValue<ThemePair>) -> Themed {
Themed::new(theme, self)
}
fn themed_mode(self, mode: impl IntoValue<ThemeMode>) -> ThemedMode {
ThemedMode::new(mode, self)
}
fn collapse_horizontally(self, collapse_when: impl IntoDynamic<bool>) -> Collapse {
Collapse::horizontal(collapse_when, self)
}
fn collapse_vertically(self, collapse_when: impl IntoDynamic<bool>) -> Collapse {
Collapse::vertical(collapse_when, self)
}
fn disclose(self) -> Disclose {
Disclose::new(self)
}
fn validation(self, validation: impl IntoDynamic<Validation>) -> Validated {
Validated::new(validation, self)
}
fn tooltip(self, layer: &OverlayLayer, tip: impl MakeWidget) -> Tooltipped {
layer.new_tooltip(tip, self)
}
}
pub trait MakeWidgetWithTag: Sized {
fn make_with_tag(self, tag: WidgetTag) -> WidgetInstance;
}
impl<T> MakeWidgetWithTag for T
where
T: Widget,
{
fn make_with_tag(self, id: WidgetTag) -> WidgetInstance {
WidgetInstance::with_id(self, id)
}
}
impl<T> MakeWidget for T
where
T: MakeWidgetWithTag,
{
fn make_widget(self) -> WidgetInstance {
self.make_with_tag(WidgetTag::unique())
}
}
impl MakeWidget for WidgetInstance {
fn make_widget(self) -> WidgetInstance {
self
}
}
impl MakeWidgetWithTag for Color {
fn make_with_tag(self, id: WidgetTag) -> WidgetInstance {
Space::colored(self).make_with_tag(id)
}
}
pub type EventHandling = ControlFlow<EventHandled, EventIgnored>;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct EventHandled;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct EventIgnored;
pub const HANDLED: EventHandling = EventHandling::Break(EventHandled);
pub const IGNORED: EventHandling = EventHandling::Continue(EventIgnored);
pub(crate) trait AnyWidget: Widget {
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
impl<T> AnyWidget for T
where
T: Widget,
{
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[derive(Clone)]
pub struct WidgetInstance {
data: Arc<WidgetInstanceData>,
}
impl Debug for WidgetInstance {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.data.widget.try_lock() {
Some(widget) => widget.summarize(f),
None => f.debug_struct("WidgetInstance").finish_non_exhaustive(),
}
}
}
#[derive(Debug)]
struct WidgetInstanceData {
id: WidgetId,
default: bool,
cancel: bool,
next_focus: Value<Option<WidgetId>>,
enabled: Value<bool>,
widget: Box<Mutex<dyn AnyWidget>>,
}
impl WidgetInstance {
pub fn with_id<W>(widget: W, id: WidgetTag) -> Self
where
W: Widget,
{
Self {
data: Arc::new(WidgetInstanceData {
id: id.into(),
next_focus: Value::default(),
default: false,
cancel: false,
widget: Box::new(Mutex::new(widget)),
enabled: Value::Constant(true),
}),
}
}
pub fn new<W>(widget: W) -> Self
where
W: Widget,
{
Self::with_id(widget, WidgetTag::unique())
}
#[must_use]
pub fn id(&self) -> WidgetId {
self.data.id
}
#[must_use]
pub fn with_next_focus(
mut self,
next_focus: impl IntoValue<Option<WidgetId>>,
) -> WidgetInstance {
let data = Arc::get_mut(&mut self.data)
.expect("with_next_focus can only be called on newly created widget instances");
data.next_focus = next_focus.into_value();
self
}
#[must_use]
pub fn with_enabled(mut self, enabled: impl IntoValue<bool>) -> WidgetInstance {
let data = Arc::get_mut(&mut self.data)
.expect("with_enabled can only be called on newly created widget instances");
data.enabled = enabled.into_value();
self
}
#[must_use]
pub fn into_default(mut self) -> WidgetInstance {
let data = Arc::get_mut(&mut self.data)
.expect("with_next_focus can only be called on newly created widget instances");
data.default = true;
self
}
#[must_use]
pub fn into_escape(mut self) -> WidgetInstance {
let data = Arc::get_mut(&mut self.data)
.expect("with_next_focus can only be called on newly created widget instances");
data.cancel = true;
self
}
#[must_use]
pub fn lock(&self) -> WidgetGuard<'_> {
WidgetGuard(self.data.widget.lock())
}
#[must_use]
pub fn next_focus(&self) -> Option<WidgetId> {
self.data.next_focus.get()
}
#[must_use]
pub fn is_default(&self) -> bool {
self.data.default
}
#[must_use]
pub fn is_escape(&self) -> bool {
self.data.cancel
}
pub(crate) fn enabled(&self, context: &WindowHandle) -> bool {
if let Value::Dynamic(dynamic) = &self.data.enabled {
dynamic.inner_redraw_when_changed(context.clone());
}
self.data.enabled.get()
}
}
impl AsRef<WidgetId> for WidgetInstance {
fn as_ref(&self) -> &WidgetId {
&self.data.id
}
}
impl Eq for WidgetInstance {}
impl PartialEq for WidgetInstance {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.data, &other.data)
}
}
impl WindowBehavior for WidgetInstance {
type Context = Self;
fn initialize(
_window: &mut RunningWindow<kludgine::app::Window<'_, WindowCommand>>,
context: Self::Context,
) -> Self {
context
}
fn make_root(&mut self) -> WidgetInstance {
self.clone()
}
}
pub struct Callback<T = (), R = ()>(Box<dyn CallbackFunction<T, R>>);
impl<T, R> Debug for Callback<T, R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Callback")
.field(&std::ptr::from_ref::<Self>(self))
.finish()
}
}
impl<T, R> Eq for Callback<T, R> {}
impl<T, R> PartialEq for Callback<T, R> {
fn eq(&self, _other: &Self) -> bool {
false
}
}
impl<T, R> Callback<T, R> {
pub fn new<F>(function: F) -> Self
where
F: FnMut(T) -> R + Send + 'static,
{
Self(Box::new(function))
}
pub fn invoke(&mut self, value: T) -> R {
self.0.invoke(value)
}
}
trait CallbackFunction<T, R>: Send {
fn invoke(&mut self, value: T) -> R;
}
impl<T, R, F> CallbackFunction<T, R> for F
where
F: FnMut(T) -> R + Send,
{
fn invoke(&mut self, value: T) -> R {
self(value)
}
}
pub struct OnceCallback<T = (), R = ()>(Box<dyn OnceCallbackFunction<T, R>>);
impl<T, R> Debug for OnceCallback<T, R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("OnceCallback")
.field(&std::ptr::from_ref::<Self>(self))
.finish()
}
}
impl<T, R> Eq for OnceCallback<T, R> {}
impl<T, R> PartialEq for OnceCallback<T, R> {
fn eq(&self, _other: &Self) -> bool {
false
}
}
impl<T, R> OnceCallback<T, R> {
pub fn new<F>(function: F) -> Self
where
F: FnOnce(T) -> R + Send + 'static,
{
Self(Box::new(Some(function)))
}
pub fn invoke(mut self, value: T) -> R {
self.0.invoke(value)
}
}
trait OnceCallbackFunction<T, R>: Send {
fn invoke(&mut self, value: T) -> R;
}
impl<T, R, F> OnceCallbackFunction<T, R> for Option<F>
where
F: FnOnce(T) -> R + Send,
{
fn invoke(&mut self, value: T) -> R {
(self.take().assert("invoked once"))(value)
}
}
#[derive(Clone)]
pub struct MountedWidget {
pub(crate) node_id: LotId,
pub(crate) widget: WidgetInstance,
pub(crate) tree: WeakTree,
}
impl Debug for MountedWidget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self.widget, f)
}
}
impl MountedWidget {
pub(crate) fn tree(&self) -> Tree {
self.tree.upgrade().expect("tree missing")
}
pub fn remount_if_needed(&mut self, context: &mut EventContext<'_>) {
if !self.is_mounted() {
*self = context.push_child(self.widget.clone());
}
}
#[must_use]
pub fn is_mounted(&self) -> bool {
let Some(tree) = self.tree.upgrade() else {
return false;
};
tree.widget_is_valid(self.node_id)
}
#[must_use]
pub fn lock(&self) -> WidgetGuard<'_> {
self.widget.lock()
}
pub fn invalidate(&self) {
let Some(tree) = self.tree.upgrade() else {
return;
};
tree.invalidate(self.node_id, false);
}
pub(crate) fn set_layout(&self, rect: Rect<Px>) {
self.tree().set_layout(self.node_id, rect);
}
#[must_use]
pub fn id(&self) -> WidgetId {
self.widget.id()
}
#[must_use]
pub const fn instance(&self) -> &WidgetInstance {
&self.widget
}
#[must_use]
pub fn next_focus(&self) -> Option<MountedWidget> {
self.widget
.next_focus()
.and_then(|next_focus| self.tree.upgrade()?.widget(next_focus))
}
#[must_use]
pub fn previous_focus(&self) -> Option<MountedWidget> {
self.tree.upgrade()?.previous_focus(self.id())
}
#[must_use]
pub fn explicit_focus_target(&self, advance: bool) -> Option<MountedWidget> {
if advance {
self.next_focus()
} else {
self.previous_focus()
}
}
#[must_use]
pub fn last_layout(&self) -> Option<Rect<Px>> {
self.tree.upgrade()?.layout(self.node_id)
}
#[must_use]
pub fn effective_styles(&self) -> Styles {
self.tree().effective_styles(self.node_id)
}
#[must_use]
pub fn active(&self) -> bool {
self.tree().active_widget() == Some(self.node_id)
}
pub(crate) fn enabled(&self, handle: &WindowHandle) -> bool {
self.tree().is_enabled(self.node_id, handle)
}
#[must_use]
pub fn hovered(&self) -> bool {
self.tree().is_hovered(self.node_id)
}
#[must_use]
pub fn primary_hover(&self) -> bool {
self.tree().hovered_widget() == Some(self.node_id)
}
#[must_use]
pub fn focused(&self) -> bool {
self.tree().focused_widget() == Some(self.node_id)
}
#[must_use]
pub fn parent(&self) -> Option<MountedWidget> {
let tree = self.tree.upgrade()?;
tree.parent(self.node_id)
.and_then(|id| tree.widget_from_node(id))
}
#[must_use]
pub fn has_parent(&self) -> bool {
let Some(tree) = self.tree.upgrade() else {
return false;
};
tree.parent(self.node_id).is_some()
}
pub(crate) fn attach_styles(&self, styles: Value<Styles>) {
self.tree().attach_styles(self.node_id, styles);
}
pub(crate) fn attach_theme(&self, theme: Value<ThemePair>) {
self.tree().attach_theme(self.node_id, theme);
}
pub(crate) fn attach_theme_mode(&self, theme: Value<ThemeMode>) {
self.tree().attach_theme_mode(self.node_id, theme);
}
pub(crate) fn overidden_theme(
&self,
) -> (Styles, Option<Value<ThemePair>>, Option<Value<ThemeMode>>) {
self.tree().overriden_theme(self.node_id)
}
pub(crate) fn begin_layout(&self, constraints: Size<ConstraintLimit>) -> Option<Size<UPx>> {
self.tree().begin_layout(self.node_id, constraints)
}
pub(crate) fn persist_layout(&self, constraints: Size<ConstraintLimit>, size: Size<UPx>) {
self.tree().persist_layout(self.node_id, constraints, size);
}
pub(crate) fn visually_ordered_children(&self, order: VisualOrder) -> Vec<MountedWidget> {
self.tree().visually_ordered_children(self.node_id, order)
}
}
impl AsRef<WidgetId> for MountedWidget {
fn as_ref(&self) -> &WidgetId {
self.widget.as_ref()
}
}
impl PartialEq for MountedWidget {
fn eq(&self, other: &Self) -> bool {
self.widget == other.widget
}
}
impl PartialEq<WidgetInstance> for MountedWidget {
fn eq(&self, other: &WidgetInstance) -> bool {
&self.widget == other
}
}
pub struct WidgetGuard<'a>(MutexGuard<'a, dyn AnyWidget>);
impl WidgetGuard<'_> {
pub(crate) fn as_widget(&mut self) -> &mut dyn AnyWidget {
&mut *self.0
}
#[must_use]
pub fn downcast_ref<T>(&self) -> Option<&T>
where
T: 'static,
{
self.0.as_any().downcast_ref()
}
#[must_use]
pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
where
T: 'static,
{
self.0.as_any_mut().downcast_mut()
}
}
#[derive(Default, Eq, PartialEq)]
#[must_use]
pub struct WidgetList {
ordered: Vec<WidgetInstance>,
}
impl WidgetList {
pub const fn new() -> Self {
Self {
ordered: Vec::new(),
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
ordered: Vec::with_capacity(capacity),
}
}
pub fn push<W>(&mut self, widget: W)
where
W: MakeWidget,
{
self.ordered.push(widget.make_widget());
}
pub fn insert<W>(&mut self, index: usize, widget: W)
where
W: MakeWidget,
{
self.ordered.insert(index, widget.make_widget());
}
pub fn extend<T, Iter>(&mut self, iter: Iter)
where
Iter: IntoIterator<Item = T>,
T: MakeWidget,
{
self.ordered.extend(iter.into_iter().map(T::make_widget));
}
pub fn and<W>(mut self, widget: W) -> Self
where
W: MakeWidget,
{
self.push(widget);
self
}
pub fn chain<T, Iter>(mut self, iter: Iter) -> Self
where
Iter: IntoIterator<Item = T>,
T: MakeWidget,
{
self.extend(iter);
self
}
#[must_use]
pub fn len(&self) -> usize {
self.ordered.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.ordered.is_empty()
}
pub fn truncate(&mut self, length: usize) {
self.ordered.truncate(length);
}
#[must_use]
pub fn into_rows(self) -> Stack {
Stack::rows(self)
}
#[must_use]
pub fn into_columns(self) -> Stack {
Stack::columns(self)
}
#[must_use]
pub fn into_layers(self) -> Layers {
Layers::new(self)
}
#[must_use]
pub fn into_wrap(self) -> Wrap {
Wrap::new(self)
}
#[must_use]
pub fn into_list(self) -> List {
List::new(self)
}
pub fn synchronize_with<Collection>(
&self,
collection: &mut Collection,
get_index: impl Fn(&Collection, usize) -> Option<&WidgetInstance>,
mut change_fn: impl FnMut(&mut Collection, ChildrenSyncChange),
) {
for (index, widget) in self.iter().enumerate() {
if get_index(collection, index).map_or(true, |child| child != widget) {
if let Some(Some(swap_index)) = (index + 1..usize::MAX).find_map(|index| {
if let Some(child) = get_index(collection, index) {
if widget == child {
Some(Some(index))
} else {
None
}
} else {
Some(None)
}
}) {
change_fn(collection, ChildrenSyncChange::Swap(index, swap_index));
} else {
change_fn(
collection,
ChildrenSyncChange::Insert(index, widget.clone()),
);
}
}
}
change_fn(collection, ChildrenSyncChange::Truncate(self.len()));
}
}
impl Debug for WidgetList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.ordered, f)
}
}
impl Dynamic<WidgetList> {
#[must_use]
pub fn into_rows(self) -> Stack {
Stack::rows(self)
}
#[must_use]
pub fn to_rows(&self) -> Stack {
self.clone().into_rows()
}
#[must_use]
pub fn into_columns(self) -> Stack {
Stack::columns(self)
}
#[must_use]
pub fn to_columns(&self) -> Stack {
self.clone().into_columns()
}
#[must_use]
pub fn into_layers(self) -> Layers {
Layers::new(self)
}
#[must_use]
pub fn to_layers(&self) -> Layers {
self.clone().into_layers()
}
#[must_use]
pub fn into_list(self) -> List {
List::new(self)
}
#[must_use]
pub fn to_list(self) -> List {
self.clone().into_list()
}
#[must_use]
pub fn into_wrap(self) -> Wrap {
Wrap::new(self)
}
#[must_use]
pub fn to_wrap(&self) -> Wrap {
self.clone().into_wrap()
}
}
impl<W> FromIterator<W> for WidgetList
where
W: MakeWidget,
{
fn from_iter<T: IntoIterator<Item = W>>(iter: T) -> Self {
Self {
ordered: iter.into_iter().map(MakeWidget::make_widget).collect(),
}
}
}
impl Deref for WidgetList {
type Target = [WidgetInstance];
fn deref(&self) -> &Self::Target {
&self.ordered
}
}
impl DerefMut for WidgetList {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.ordered
}
}
impl<'a> IntoIterator for &'a WidgetList {
type IntoIter = slice::Iter<'a, WidgetInstance>;
type Item = &'a WidgetInstance;
fn into_iter(self) -> Self::IntoIter {
self.ordered.iter()
}
}
impl<I: IntoIterator> MakeWidgetList for I
where
I::Item: MakeWidget,
{
fn make_widget_list(self) -> WidgetList {
self.into_iter().collect()
}
}
pub trait MakeWidgetList: Sized {
fn make_widget_list(self) -> WidgetList;
fn and<W>(self, widget: W) -> WidgetList
where
W: MakeWidget,
{
let mut list = self.make_widget_list();
list.push(widget);
list
}
#[must_use]
fn into_rows(self) -> Stack {
Stack::rows(self.make_widget_list())
}
#[must_use]
fn into_columns(self) -> Stack {
Stack::columns(self.make_widget_list())
}
#[must_use]
fn into_layers(self) -> Layers {
Layers::new(self.make_widget_list())
}
#[must_use]
fn into_wrap(self) -> Wrap {
Wrap::new(self.make_widget_list())
}
#[must_use]
fn into_list(self) -> List {
List::new(self.make_widget_list())
}
}
pub enum ChildrenSyncChange {
Insert(usize, WidgetInstance),
Swap(usize, usize),
Truncate(usize),
}
#[derive(Debug)]
pub struct MountedChildren<T = MountedWidget> {
generation: Option<Generation>,
children: Vec<T>,
}
impl<T> MountedChildren<T>
where
T: MountableChild,
{
pub fn synchronize_with(
&mut self,
children: &Value<WidgetList>,
context: &mut EventContext<'_>,
) {
let current_generation = children.generation();
if current_generation.map_or_else(
|| children.map(WidgetList::len) != self.children.len(),
|gen| Some(gen) != self.generation,
) {
self.generation = current_generation;
children.map(|children| {
children.synchronize_with(
self,
|this, index| {
this.children
.get(index)
.map(|mounted| mounted.widget().instance())
},
|this, change| match change {
ChildrenSyncChange::Insert(index, widget) => {
this.children
.insert(index, T::mount(context.push_child(widget), this, index));
}
ChildrenSyncChange::Swap(a, b) => {
this.children.swap(a, b);
}
ChildrenSyncChange::Truncate(length) => {
for removed in this.children.drain(length..) {
context.remove_child(&removed.unmount());
}
}
},
);
});
}
}
pub fn drain(&mut self) -> vec::Drain<'_, T> {
self.generation = None;
self.children.drain(..)
}
#[must_use]
pub fn children(&self) -> &[T] {
&self.children
}
}
impl<T> Default for MountedChildren<T> {
fn default() -> Self {
Self {
generation: None,
children: Vec::default(),
}
}
}
pub trait MountableChild: Sized {
fn mount(widget: MountedWidget, into: &MountedChildren<Self>, index: usize) -> Self;
fn unmount(self) -> MountedWidget;
fn widget(&self) -> &MountedWidget;
}
impl MountableChild for MountedWidget {
fn mount(widget: MountedWidget, _into: &MountedChildren<Self>, _index: usize) -> Self {
widget
}
fn widget(&self) -> &MountedWidget {
self
}
fn unmount(self) -> MountedWidget {
self
}
}
#[derive(Clone)]
pub struct WidgetRef {
instance: WidgetInstance,
mounted: WindowLocal<MountedWidget>,
}
impl WidgetRef {
pub fn new(widget: impl MakeWidget) -> Self {
Self {
instance: widget.make_widget(),
mounted: WindowLocal::default(),
}
}
fn mounted_for_context(&mut self, context: &mut impl AsEventContext) -> &MountedWidget {
let mut context = context.as_event_context();
self.mounted
.entry(&context)
.and_modify(|w| {
w.remount_if_needed(&mut context.as_event_context());
})
.or_insert_with(|| context.push_child(self.instance.clone()))
}
pub fn mount_if_needed(&mut self, context: &mut impl AsEventContext) {
self.mounted_for_context(context);
}
pub fn mounted(&mut self, context: &mut impl AsEventContext) -> MountedWidget {
self.mounted_for_context(context).clone()
}
#[must_use]
pub fn as_mounted(&self, context: &WidgetContext<'_>) -> Option<&MountedWidget> {
self.mounted.get(context)
}
#[must_use]
pub const fn widget(&self) -> &WidgetInstance {
&self.instance
}
pub fn unmount_in(&mut self, context: &mut impl AsEventContext) {
let mut context = context.as_event_context();
if let Some(mounted) = self.mounted.clear_for(&context) {
context.remove_child(&mounted);
}
}
}
impl AsRef<WidgetId> for WidgetRef {
fn as_ref(&self) -> &WidgetId {
self.instance.as_ref()
}
}
impl Debug for WidgetRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.instance, f)
}
}
impl Eq for WidgetRef {}
impl PartialEq for WidgetRef {
fn eq(&self, other: &Self) -> bool {
self.instance == other.instance
}
}
impl ManageWidget for WidgetRef {
type Managed = Option<MountedWidget>;
fn manage(&self, context: &WidgetContext<'_>) -> Self::Managed {
self.mounted
.get(context)
.cloned()
.or_else(|| context.tree.widget(self.instance.id()))
}
}
#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash, Ord, PartialOrd)]
pub struct WidgetId(u64);
impl WidgetId {
fn unique() -> Self {
static COUNTER: AtomicU64 = AtomicU64::new(0);
Self(COUNTER.fetch_add(1, atomic::Ordering::Acquire))
}
#[must_use]
pub fn find_in(self, context: &WidgetContext<'_>) -> Option<MountedWidget> {
context.tree.widget(self)
}
}
#[derive(Eq, PartialEq, Debug)]
pub struct WidgetTag(WidgetId);
impl WidgetTag {
#[must_use]
pub fn new() -> (Self, WidgetId) {
let tag = Self::unique();
let id = *tag;
(tag, id)
}
#[must_use]
pub fn unique() -> Self {
Self(WidgetId::unique())
}
#[must_use]
pub const fn id(&self) -> WidgetId {
self.0
}
}
impl From<WidgetTag> for WidgetId {
fn from(value: WidgetTag) -> Self {
value.0
}
}
impl Deref for WidgetTag {
type Target = WidgetId;
fn deref(&self) -> &Self::Target {
&self.0
}
}