use std::sync::Arc;
use fluent_core::Theme;
use fluent_layout::{modal::ModalStack, ToastStack};
use gpui::{
prelude::*, px, size, App, Application, AssetSource, Bounds, Context, Entity, IntoElement,
Render, SharedString, TitlebarOptions, Window, WindowBounds, WindowDecorations, WindowOptions,
};
use crate::{assets::FluentAssets, chrome::wrap_with_chrome, title_bar::TitleBar};
const DEFAULT_W: f32 = 1280.0;
const DEFAULT_H: f32 = 800.0;
struct ThemeRoot<V: Render + 'static> {
content: Entity<V>,
}
impl<V: Render + 'static> ThemeRoot<V> {
fn new(cx: &mut Context<Self>, content: Entity<V>) -> Self {
let themed_content = content.clone();
cx.observe_global::<Theme>(move |_, cx| {
themed_content.update(cx, |_, cx| cx.notify());
cx.notify();
})
.detach();
Self { content }
}
}
impl<V: Render + 'static> Render for ThemeRoot<V> {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
self.content.clone()
}
}
pub struct FluentApp {
title: SharedString,
window_w: f32,
window_h: f32,
dark: bool,
assets: Option<Arc<dyn AssetSource>>,
}
impl FluentApp {
pub fn new(title: impl Into<SharedString>) -> Self {
Self {
title: title.into(),
window_w: DEFAULT_W,
window_h: DEFAULT_H,
dark: true,
assets: None,
}
}
pub fn window_size(mut self, w: f32, h: f32) -> Self {
self.window_w = w;
self.window_h = h;
self
}
pub fn dark_theme(mut self) -> Self {
self.dark = true;
self
}
pub fn light_theme(mut self) -> Self {
self.dark = false;
self
}
pub fn assets(mut self, assets: impl AssetSource) -> Self {
self.assets = Some(Arc::new(assets));
self
}
pub fn run<V: Render + 'static>(self, build: impl FnOnce(&mut App) -> Entity<V> + 'static) {
let title = self.title.clone();
let w = self.window_w;
let h = self.window_h;
let dark = self.dark;
let assets = self.assets;
Application::new()
.with_assets(FluentAssets::new(assets))
.run(move |cx: &mut App| {
if dark {
Theme::init(cx);
} else {
cx.set_global(Theme::light());
}
ModalStack::init(cx);
ToastStack::init(cx);
let bounds = Bounds::centered(None, size(px(w), px(h)), cx);
cx.open_window(
WindowOptions {
window_bounds: Some(WindowBounds::Windowed(bounds)),
titlebar: Some(TitlebarOptions {
title: Some(title.clone()),
appears_transparent: true,
traffic_light_position: None,
}),
window_decorations: Some(WindowDecorations::Client),
..Default::default()
},
move |_window, cx: &mut App| {
let root = build(cx);
let chromed = wrap_with_chrome(root, cx);
cx.new(|cx| ThemeRoot::new(cx, chromed))
},
)
.unwrap();
cx.activate(true);
});
}
}
pub fn title_bar(title: impl Into<SharedString>, cx: &mut App) -> Entity<TitleBar> {
let t = title.into();
cx.new(|cx| TitleBar::new(cx, t))
}