use std::ops::{Deref, DerefMut};
use crate::{
canvas::Canvas,
context::{BuildCx, DrawCx, EventCx, LayoutCx, RebuildCx},
event::{Event, FocusTarget},
layout::{Rect, Size, Space},
};
use super::{View, ViewState};
pub struct PodState<T, V: View<T> + ?Sized> {
content: V::State,
view_state: ViewState,
prev_canvas: Canvas,
prev_visible: Rect,
}
impl<T, V: View<T> + ?Sized> Deref for PodState<T, V> {
type Target = ViewState;
fn deref(&self) -> &Self::Target {
&self.view_state
}
}
impl<T, V: View<T> + ?Sized> DerefMut for PodState<T, V> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.view_state
}
}
pub fn pod<V>(view: V) -> Pod<V> {
Pod::new(view)
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Pod<V> {
pub(crate) view: V,
}
impl<V> Pod<V> {
pub const fn new(view: V) -> Self {
Self { view }
}
pub fn event_maybe<T>(
&mut self,
handled: bool,
state: &mut PodState<T, V>,
cx: &mut EventCx,
data: &mut T,
event: &Event,
) -> bool
where
V: View<T>,
{
if !handled {
return self.event(state, cx, data, event);
}
let _ = self.event(state, cx, data, &Event::Notify);
true
}
pub(crate) fn build_with<T>(
cx: &mut BuildCx,
f: impl FnOnce(&mut BuildCx) -> T,
) -> (T, ViewState) {
let mut view_state = ViewState::default();
let mut new_cx = cx.child();
new_cx.view_state = &mut view_state;
let state = f(&mut new_cx);
cx.view_state.propagate(&mut view_state);
(state, view_state)
}
pub(crate) fn rebuild_with(
view_state: &mut ViewState,
cx: &mut RebuildCx,
f: impl FnOnce(&mut RebuildCx),
) {
view_state.prepare();
let mut new_cx = cx.child();
new_cx.view_state = view_state;
f(&mut new_cx);
cx.view_state.propagate(view_state);
}
pub(crate) fn event_with(
view_state: &mut ViewState,
cx: &mut EventCx,
event: &Event,
f: impl FnMut(&mut EventCx, &Event) -> bool,
) -> bool {
match event {
Event::Animate(_) => {
if !view_state.needs_animate() {
cx.view_state.propagate(view_state);
return false;
}
view_state.mark_animated();
Self::event_with_inner(view_state, cx, event, f)
}
Event::FocusNext | Event::FocusPrev | Event::FocusWanted if view_state.is_focused() => {
view_state.set_focused(false);
Self::event_with_inner(view_state, cx, &Event::Notify, f);
true
}
Event::FocusGiven(target) => {
let focus_given = match target {
FocusTarget::Next | FocusTarget::Prev => view_state.is_focusable(),
FocusTarget::View(id) => view_state.id() == *id && view_state.is_focusable(),
};
if !focus_given {
return Self::event_with_inner(view_state, cx, event, f);
}
view_state.set_focused(true);
Self::event_with_inner(view_state, cx, &Event::Notify, f);
true
}
Event::WindowScaled(_) | Event::WindowResized(_) | Event::ForceLayout => {
view_state.request_layout();
Self::event_with_inner(view_state, cx, event, f)
}
event => Self::event_with_inner(view_state, cx, event, f),
}
}
#[inline]
fn event_with_inner(
view_state: &mut ViewState,
cx: &mut EventCx,
event: &Event,
f: impl FnOnce(&mut EventCx, &Event) -> bool,
) -> bool {
view_state.set_hovered(cx.window().is_hovered(view_state.id()));
view_state.prepare();
let mut new_cx = cx.child();
new_cx.transform *= view_state.transform;
new_cx.view_state = view_state;
let handled = f(&mut new_cx, event);
view_state.prev_flags = view_state.flags;
cx.view_state.propagate(view_state);
handled
}
pub(crate) fn layout_with(
view_state: &mut ViewState,
cx: &mut LayoutCx,
f: impl FnOnce(&mut LayoutCx) -> Size,
) -> Size {
view_state.mark_layed_out();
let mut new_cx = cx.child();
new_cx.view_state = view_state;
view_state.size = f(&mut new_cx);
view_state.size
}
pub(crate) fn draw_with(
view_state: &mut ViewState,
cx: &mut DrawCx,
f: impl FnOnce(&mut DrawCx),
) {
view_state.mark_drawn();
let mut new_cx = cx.child();
new_cx.view_state = view_state;
new_cx.transformed(new_cx.view_state.transform, |cx| {
f(cx);
});
}
}
impl<V> From<V> for Pod<V> {
fn from(view: V) -> Self {
Self::new(view)
}
}
impl<V> Deref for Pod<V> {
type Target = V;
fn deref(&self) -> &Self::Target {
&self.view
}
}
impl<V> DerefMut for Pod<V> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.view
}
}
impl<T, V: View<T>> View<T> for Pod<V> {
type State = PodState<T, V>;
fn build(&mut self, cx: &mut BuildCx, data: &mut T) -> Self::State {
let (content, view_state) = Self::build_with(cx, |cx| self.view.build(cx, data));
PodState {
content,
view_state,
prev_canvas: Canvas::new(),
prev_visible: Rect::ZERO,
}
}
fn rebuild(&mut self, state: &mut Self::State, cx: &mut RebuildCx, data: &mut T, old: &Self) {
Self::rebuild_with(&mut state.view_state, cx, |cx| {
(self.view).rebuild(&mut state.content, cx, data, &old.view);
});
}
fn event(
&mut self,
state: &mut Self::State,
cx: &mut EventCx,
data: &mut T,
event: &Event,
) -> bool {
Self::event_with(&mut state.view_state, cx, event, |cx, event| {
(self.view).event(&mut state.content, cx, data, event)
})
}
fn layout(
&mut self,
state: &mut Self::State,
cx: &mut LayoutCx,
data: &mut T,
space: Space,
) -> Size {
Self::layout_with(&mut state.view_state, cx, |cx| {
(self.view).layout(&mut state.content, cx, data, space)
})
}
fn draw(&mut self, state: &mut Self::State, cx: &mut DrawCx, data: &mut T) {
let needs_draw = state.view_state.needs_draw();
Self::draw_with(&mut state.view_state, cx, |cx| {
if !cx.is_visible(cx.rect()) {
return;
}
if needs_draw || state.prev_visible != cx.visible {
(self.view).draw(&mut state.content, cx, data);
state.prev_canvas = cx.canvas.clone();
state.prev_visible = cx.visible;
} else {
*cx.canvas = state.prev_canvas.clone();
}
});
}
}