use crate::core::{EntityMap, Handle, Size};
use crate::view::any_element::AnyElement;
use crate::view::div::{Div, div};
use crate::view::element::{IntoElement, ParentElement};
use crate::view::media::Svg;
use crate::view::recipes::Recipes;
use crate::view::render::{Component, ViewContext};
use crate::view::state::{State, StateStore};
use crate::view::styled::Styled;
use crate::view::text_element::Text;
use crate::view::theme::Theme;
use crate::view::widget_store::WidgetStore;
use crate::widgets::{
Button, IconName, InputChanged, TextInput, empty_state, field, icon, page_header, section, setting_row, switch,
};
struct BoundInput {
state: State<String>,
}
pub struct Ui<'a> {
pub(crate) theme: &'a Theme,
pub(crate) viewport: Size,
pub(crate) entities: &'a mut EntityMap,
pub(crate) state: &'a mut StateStore,
pub(crate) widgets: &'a mut WidgetStore,
}
impl<'a> Ui<'a> {
pub(crate) fn new(
theme: &'a Theme,
viewport: Size,
entities: &'a mut EntityMap,
state: &'a mut StateStore,
widgets: &'a mut WidgetStore,
) -> Self {
Self { theme, viewport, entities, state, widgets }
}
pub fn theme(&self) -> &Theme {
self.theme
}
pub fn viewport(&self) -> Size {
self.viewport
}
pub fn entities(&mut self) -> &mut EntityMap {
self.entities
}
pub fn state<T: 'static>(&mut self, key: &'static str, default: T) -> State<T> {
self.state.get_or_insert(key, default)
}
pub fn component<T: 'static, R>(
&mut self,
key: &'static str,
init: impl FnOnce() -> T,
build: impl FnOnce(&mut Ui, &mut T, State<T>) -> R,
) -> R {
let state = self.state.get_or_insert_with(key, init);
state.with_mut(|value| build(self, value, state.clone()))
}
pub fn component_with<T: 'static, R>(
&mut self,
key: &'static str,
init: impl FnOnce(&mut Ui) -> T,
build: impl FnOnce(&mut Ui, &mut T, State<T>) -> R,
) -> R {
let state = match self.state.get::<T>(key) {
Some(state) => state,
None => {
let value = init(self);
self.state.insert(key, value)
}
};
state.with_mut(|value| build(self, value, state.clone()))
}
pub fn child<V: Component>(&mut self, handle: Handle<V>) -> AnyElement {
let (theme, viewport) = (self.theme, self.viewport);
self.entities
.update(handle, |view, entity| view.view(&mut ViewContext::new(theme, viewport, entity)).into_element())
.unwrap_or_else(|| div().into_element())
}
pub fn text(&self, content: impl Into<String>) -> Text {
crate::view::text_element::text(content)
}
pub fn title(&self, content: impl Into<String>) -> Div {
let content: String = content.into();
div().heading().child(content)
}
pub fn caption(&self, content: impl Into<String>) -> Div {
let content: String = content.into();
div().caption().child(content)
}
pub fn button(&self, label: impl Into<String>) -> Button {
Button::new(label)
}
pub fn icon(&self, name: IconName) -> Svg {
icon(name)
}
pub fn page(&self) -> Div {
div().page()
}
pub fn card(&self) -> Div {
div().card()
}
pub fn row(&self) -> Div {
div().flex_row().items_center()
}
pub fn column(&self) -> Div {
div().flex_col()
}
pub fn spacer(&self) -> Div {
div().flex_1()
}
pub fn section(&self, title: impl Into<String>, hint: impl Into<String>) -> Div {
section(title, hint)
}
pub fn setting_row(&self, title: impl Into<String>, hint: impl Into<String>, control: impl IntoElement) -> Div {
setting_row(title, hint, control)
}
pub fn page_header(&self, title: impl Into<String>, subtitle: impl Into<String>) -> Div {
page_header(title, subtitle)
}
pub fn field(&self, label: impl Into<String>, control: impl IntoElement) -> Div {
field(label, control)
}
pub fn empty_state(&self, title: impl Into<String>, hint: impl Into<String>) -> Div {
empty_state(title, hint)
}
pub fn text_field(&mut self, label: impl Into<String>, state: &State<String>) -> Div {
let label: String = label.into();
let key = state.key();
let input = match self.widgets.text_input(key) {
Some(input) => input,
None => {
let focus = self.entities.focus.new_handle();
let input = self.entities.insert(TextInput::new(focus).placeholder(label.clone()));
let binding = self.entities.insert(BoundInput { state: state.clone() });
self.entities.subscribe(input, binding, |binding, event: &InputChanged, _| {
binding.state.write(|text| *text = event.0.clone());
});
self.widgets.insert_text_input(key, input);
input
}
};
let text = state.get();
self.entities.update(input, |input, _| {
if input.text() != text {
input.set_text(text);
}
});
let element = self.child(input);
field(label, element)
}
pub fn switch(&self, state: &State<bool>) -> AnyElement {
switch(state.get(), state.key(), state.toggle())
}
}