use std::{
cell::RefCell,
rc::{Rc, Weak},
};
use futures::Future;
use crate::{util::LateRefCell, Error, Result};
use super::{
platform::{
app_delegate::ApplicationDelegateManager, drag_data::DragDataAdapter,
engine::PlatformPlugin, init::init_platform,
},
screen_manager::ScreenManager,
status_item_manager::StatusItemManager,
EngineManager, HotKeyManager, JoinHandle, KeyboardMapManager, MenuManager, MessageManager,
RegisteredMethodCallHandler, RunLoop, WindowManager, WindowMethodChannel,
};
pub struct ContextOptions {
pub app_namespace: String,
pub flutter_plugins: Vec<PlatformPlugin>,
pub on_last_engine_removed: Box<dyn Fn(&ContextRef)>,
pub custom_drag_data_adapters: Vec<Box<dyn DragDataAdapter>>,
}
impl Default for ContextOptions {
fn default() -> Self {
Self {
app_namespace: Default::default(),
flutter_plugins: Vec::new(),
on_last_engine_removed: Box::new(|context| context.run_loop.borrow().stop()),
custom_drag_data_adapters: Vec::new(),
}
}
}
pub struct ContextImpl {
pub options: ContextOptions,
pub run_loop: LateRefCell<RunLoop>,
pub engine_manager: LateRefCell<EngineManager>,
pub message_manager: LateRefCell<MessageManager>,
pub window_method_channel: LateRefCell<WindowMethodChannel>,
pub window_manager: LateRefCell<WindowManager>,
pub application_delegate_manager: LateRefCell<ApplicationDelegateManager>,
pub(crate) menu_manager: LateRefCell<RegisteredMethodCallHandler<MenuManager>>,
pub(crate) keyboard_map_manager: LateRefCell<RegisteredMethodCallHandler<KeyboardMapManager>>,
pub(crate) hot_key_manager: LateRefCell<RegisteredMethodCallHandler<HotKeyManager>>,
pub(crate) screen_manager: LateRefCell<RegisteredMethodCallHandler<ScreenManager>>,
pub(crate) status_item_manager: LateRefCell<RegisteredMethodCallHandler<StatusItemManager>>,
}
impl ContextImpl {
fn create(options: ContextOptions) -> Result<ContextRef> {
let res = Rc::new(Self {
options,
run_loop: LateRefCell::new(),
engine_manager: LateRefCell::new(),
message_manager: LateRefCell::new(),
window_method_channel: LateRefCell::new(),
window_manager: LateRefCell::new(),
application_delegate_manager: LateRefCell::new(),
menu_manager: LateRefCell::new(),
keyboard_map_manager: LateRefCell::new(),
hot_key_manager: LateRefCell::new(),
screen_manager: LateRefCell::new(),
status_item_manager: LateRefCell::new(),
});
let res = ContextRef { context: res };
res.initialize(&res)?;
Ok(res)
}
fn initialize(&self, context: &ContextRef) -> Result<()> {
init_platform().map_err(Error::from)?;
self.run_loop.set(RunLoop::new(context));
self.engine_manager.set(EngineManager::new(context));
self.message_manager.set(MessageManager::new(context));
self.window_method_channel
.set(WindowMethodChannel::new(context));
self.window_manager.set(WindowManager::new(context));
self.application_delegate_manager
.set(ApplicationDelegateManager::new(context));
self.menu_manager.set(MenuManager::new(context.weak()));
self.keyboard_map_manager
.set(KeyboardMapManager::new(context.weak()));
self.hot_key_manager.set(HotKeyManager::new(context.weak()));
self.screen_manager.set(ScreenManager::new(context.weak()));
self.status_item_manager
.set(StatusItemManager::new(context.weak()));
#[cfg(debug_assertions)]
{
self.sponsor_prompt();
}
Ok(())
}
#[cfg(debug_assertions)]
fn sponsor_prompt(&self) {
if std::env::var("NATIVESHELL_SPONSOR").ok().is_none() {
println!();
println!("** Help me make NativeShell and Flutter on desktop better!");
println!("** We have a long way to go: https://nativeshell.dev/roadmap");
println!();
}
}
}
impl Drop for ContextImpl {
fn drop(&mut self) {
self.engine_manager.borrow_mut().shut_down().ok();
}
}
#[derive(Clone)]
pub struct Context {
context: Weak<ContextImpl>,
}
impl Context {
#[allow(clippy::new_ret_no_self)]
pub fn new(options: ContextOptions) -> Result<ContextRef> {
ContextImpl::create(options)
}
pub fn get(&self) -> Option<ContextRef> {
self.context.upgrade().map(|c| ContextRef { context: c })
}
pub fn current() -> Option<ContextRef> {
CURRENT_CONTEXT.with(|c| c.borrow().as_ref().and_then(|c| c.get()))
}
}
thread_local! {
static CURRENT_CONTEXT: RefCell<Option<Context>> = RefCell::new(None);
}
pub struct CurrentContextHandle {
previous: Option<Context>,
}
impl Drop for CurrentContextHandle {
fn drop(&mut self) {
CURRENT_CONTEXT.with(|c| *c.borrow_mut() = self.previous.take());
}
}
pub struct ContextRef {
context: Rc<ContextImpl>,
}
impl ContextRef {
pub fn weak(&self) -> Context {
Context {
context: Rc::downgrade(&self.context),
}
}
pub fn set_as_current(&self) -> CurrentContextHandle {
CurrentContextHandle {
previous: CURRENT_CONTEXT.with(|c| c.borrow_mut().replace(self.weak())),
}
}
}
pub fn spawn<T: 'static>(future: impl Future<Output = T> + 'static) -> JoinHandle<T> {
let context = Context::current().unwrap();
let run_loop = context.run_loop.borrow();
run_loop.spawn(future)
}
impl std::ops::Deref for ContextRef {
type Target = ContextImpl;
fn deref(&self) -> &ContextImpl {
self.context.deref()
}
}