use nami::{Computed, signal::IntoComputed};
use waterui_core::{Environment, handler::ViewBuilder};
use waterui_str::Str;
use crate::{
component::menu::{Menu, MenuBarView},
window::Window,
};
#[derive(Debug)]
pub struct App {
main_window: Window,
windows: Vec<Window>,
pub menu_bar: Computed<Vec<Menu>>,
pub env: Environment,
}
#[must_use]
pub fn application_name() -> Str {
std::env::var("WATERUI_APP_NAME").map_or_else(|_| Str::default(), Str::from)
}
impl App {
pub fn new(content: impl ViewBuilder, env: Environment) -> Self {
let state = nami::binding(crate::window::WindowState::Normal);
Self::new_with_windows([Window::new("", state, content)], env)
}
pub fn new_with_windows(windows: impl Into<Vec<Window>>, mut env: Environment) -> Self {
let mut iter = windows.into().into_iter();
let main_window = iter
.next()
.expect("App::new_with_windows requires at least one window");
if env
.get::<Computed<waterui_core::layout::LayoutDirection>>()
.is_none()
&& env
.get::<nami::Binding<waterui_core::layout::LayoutDirection>>()
.is_none()
&& env.get::<waterui_core::layout::LayoutDirection>().is_none()
{
env.insert(waterui_core::layout::AutomaticLayoutDirection(
waterui_locale::layout_direction_computed(&env),
));
}
crate::realization::install(&mut env);
Self {
main_window,
windows: iter.collect(),
menu_bar: Computed::constant(Vec::new()),
env,
}
}
#[must_use]
pub const fn main_window(&self) -> &Window {
&self.main_window
}
#[must_use]
pub const fn main_window_mut(&mut self) -> &mut Window {
&mut self.main_window
}
#[must_use = "iterators are lazy; dropping this one visits no windows"]
pub fn windows(&self) -> impl DoubleEndedIterator<Item = &Window> {
std::iter::once(&self.main_window).chain(self.windows.iter())
}
pub fn windows_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut Window> {
std::iter::once(&mut self.main_window).chain(self.windows.iter_mut())
}
#[must_use]
pub fn window(mut self, window: Window) -> Self {
self.windows.push(window);
self
}
#[must_use]
pub fn menu_bar(mut self, menus: impl MenuBarView) -> Self {
self.menu_bar = menus.into_menus();
self
}
#[must_use]
pub fn into_windows(self) -> Vec<Window> {
self.into_parts().0
}
#[must_use]
pub fn into_parts(self) -> (Vec<Window>, Computed<Vec<Menu>>, Environment) {
let mut windows = Vec::with_capacity(1 + self.windows.len());
windows.push(self.main_window);
windows.extend(self.windows);
(windows, self.menu_bar, self.env)
}
#[must_use]
pub fn title(mut self, title: impl IntoComputed<Str>) -> Self {
self.main_window.title = title.into_computed();
self
}
}
#[cfg(test)]
mod tests {
use nami::{Binding, Signal};
use waterui_core::layout::{LayoutDirection, layout_direction};
use waterui_locale::locales;
use super::*;
#[test]
fn application_direction_tracks_locale_binding() {
let locale = Binding::container(locales::AR);
let mut env = Environment::new();
env.insert(locale.clone());
let app = App::new(|| (), env);
let direction = layout_direction(&app.env);
assert_eq!(direction.get(), LayoutDirection::RightToLeft);
locale.set(locales::EN);
assert_eq!(direction.get(), LayoutDirection::LeftToRight);
}
#[test]
fn explicit_application_direction_overrides_locale() {
let mut env = Environment::new();
env.insert(locales::AR);
env.insert(LayoutDirection::LeftToRight);
let app = App::new(|| (), env);
assert_eq!(
layout_direction(&app.env).get(),
LayoutDirection::LeftToRight
);
}
}