use std::any::{Any, TypeId};
use std::cell::RefCell;
use std::collections::{HashMap, VecDeque};
use std::rc::Rc;
use crate::kurbo::Size;
use crate::piet::Piet;
use crate::shell::{
text::InputHandler, Application, FileDialogToken, FileInfo, IdleToken, MouseEvent, Region,
Scale, TextFieldToken, WinHandler, WindowHandle,
};
use crate::app_delegate::{AppDelegate, DelegateCtx};
use crate::core::CommandQueue;
use crate::ext_event::{ExtEventHost, ExtEventSink};
use crate::menu::{ContextMenu, MenuItemId, MenuManager};
use crate::window::{ImeUpdateFn, Window};
use crate::{
Command, Data, Env, Event, Handled, InternalEvent, KeyEvent, PlatformError, Selector, Target,
TimerToken, WidgetId, WindowDesc, WindowId,
};
use crate::app::{PendingWindow, WindowConfig};
use crate::command::sys as sys_cmd;
use druid_shell::WindowBuilder;
pub(crate) const RUN_COMMANDS_TOKEN: IdleToken = IdleToken::new(1);
pub(crate) const EXT_EVENT_IDLE_TOKEN: IdleToken = IdleToken::new(2);
pub struct DruidHandler<T> {
pub(crate) app_state: AppState<T>,
window_id: WindowId,
}
pub(crate) struct AppHandler<T> {
app_state: AppState<T>,
}
#[derive(Clone)]
pub(crate) struct AppState<T> {
inner: Rc<RefCell<InnerAppState<T>>>,
}
struct DialogInfo {
id: WindowId,
accept_cmd: Selector<FileInfo>,
accept_multiple_cmd: Selector<Vec<FileInfo>>,
cancel_cmd: Selector<()>,
}
struct InnerAppState<T> {
app: Application,
delegate: Option<Box<dyn AppDelegate<T>>>,
command_queue: CommandQueue,
file_dialogs: HashMap<FileDialogToken, DialogInfo>,
ext_event_host: ExtEventHost,
windows: Windows<T>,
root_menu: Option<MenuManager<T>>,
#[allow(unused)]
menu_window: Option<WindowId>,
pub(crate) env: Env,
pub(crate) data: T,
ime_focus_change: Option<Box<dyn Fn()>>,
}
struct Windows<T> {
pending: HashMap<WindowId, PendingWindow<T>>,
windows: HashMap<WindowId, Window<T>>,
}
impl<T> Windows<T> {
fn connect(&mut self, id: WindowId, handle: WindowHandle, ext_handle: ExtEventSink) {
if let Some(pending) = self.pending.remove(&id) {
let win = Window::new(id, handle, pending, ext_handle);
assert!(self.windows.insert(id, win).is_none(), "duplicate window");
} else {
tracing::error!("no window for connecting handle {:?}", id);
}
}
fn add(&mut self, id: WindowId, win: PendingWindow<T>) {
assert!(self.pending.insert(id, win).is_none(), "duplicate pending");
}
fn remove(&mut self, id: WindowId) -> Option<Window<T>> {
self.windows.remove(&id)
}
fn iter_mut(&mut self) -> impl Iterator<Item = &'_ mut Window<T>> {
self.windows.values_mut()
}
fn get(&self, id: WindowId) -> Option<&Window<T>> {
self.windows.get(&id)
}
fn get_mut(&mut self, id: WindowId) -> Option<&mut Window<T>> {
self.windows.get_mut(&id)
}
fn count(&self) -> usize {
self.windows.len() + self.pending.len()
}
}
impl<T> AppHandler<T> {
pub(crate) fn new(app_state: AppState<T>) -> Self {
Self { app_state }
}
}
impl<T> AppState<T> {
pub(crate) fn new(
app: Application,
data: T,
env: Env,
delegate: Option<Box<dyn AppDelegate<T>>>,
ext_event_host: ExtEventHost,
) -> Self {
let inner = Rc::new(RefCell::new(InnerAppState {
app,
delegate,
command_queue: VecDeque::new(),
file_dialogs: HashMap::new(),
root_menu: None,
menu_window: None,
ext_event_host,
data,
env,
windows: Windows::default(),
ime_focus_change: None,
}));
AppState { inner }
}
pub(crate) fn app(&self) -> Application {
self.inner.borrow().app.clone()
}
}
impl<T: Data> InnerAppState<T> {
fn handle_menu_cmd(&mut self, cmd_id: MenuItemId, window_id: Option<WindowId>) {
let queue = &mut self.command_queue;
let data = &mut self.data;
let env = &self.env;
match window_id {
Some(id) => self
.windows
.get_mut(id)
.map(|w| w.menu_cmd(queue, cmd_id, data, env)),
None => self
.root_menu
.as_mut()
.map(|m| m.event(queue, None, cmd_id, data, env)),
};
}
fn append_command(&mut self, cmd: Command) {
self.command_queue.push_back(cmd);
}
fn with_delegate<R, F>(&mut self, f: F) -> Option<R>
where
F: FnOnce(&mut dyn AppDelegate<T>, &mut T, &Env, &mut DelegateCtx) -> R,
{
let InnerAppState {
ref mut delegate,
ref mut command_queue,
ref mut data,
ref ext_event_host,
ref env,
..
} = self;
let mut ctx = DelegateCtx {
command_queue,
app_data_type: TypeId::of::<T>(),
ext_event_host,
};
delegate
.as_deref_mut()
.map(|delegate| f(delegate, data, env, &mut ctx))
}
fn delegate_event(&mut self, id: WindowId, event: Event) -> Option<Event> {
if self.delegate.is_some() {
self.with_delegate(|del, data, env, ctx| del.event(ctx, id, event, data, env))
.unwrap()
} else {
Some(event)
}
}
fn delegate_cmd(&mut self, cmd: &Command) -> Handled {
self.with_delegate(|del, data, env, ctx| del.command(ctx, cmd.target(), cmd, data, env))
.unwrap_or(Handled::No)
}
fn connect(&mut self, id: WindowId, handle: WindowHandle) {
self.windows
.connect(id, handle.clone(), self.ext_event_host.make_sink());
if self.ext_event_host.handle_window_id.is_none() {
self.set_ext_event_idle_handler(id);
}
self.with_delegate(|del, data, env, ctx| del.window_added(id, handle, data, env, ctx));
}
fn remove_window(&mut self, window_id: WindowId) {
self.with_delegate(|del, data, env, ctx| del.window_removed(window_id, data, env, ctx));
if let Some(mut win) = self.windows.remove(window_id) {
if self.windows.windows.is_empty() {
self.root_menu = win.menu.take();
if self.windows.count() == 0 {
#[cfg(any(target_os = "windows", feature = "x11"))]
self.app.quit();
}
}
}
if self.ext_event_host.handle_window_id == Some(window_id) {
self.ext_event_host.handle_window_id = None;
let win_id = self.windows.windows.keys().find(|k| *k != &window_id);
if let Some(any_other_window) = win_id.cloned() {
self.set_ext_event_idle_handler(any_other_window);
}
}
}
fn set_ext_event_idle_handler(&mut self, id: WindowId) {
if let Some(mut idle) = self
.windows
.get_mut(id)
.and_then(|win| win.handle.get_idle_handle())
{
if self.ext_event_host.has_pending_items() {
idle.schedule_idle(EXT_EVENT_IDLE_TOKEN);
}
self.ext_event_host.set_idle(idle, id);
}
}
fn request_close_window(&mut self, window_id: WindowId) {
if let Some(win) = self.windows.get_mut(window_id) {
win.handle.close();
}
}
fn request_close_all_windows(&mut self) {
for win in self.windows.iter_mut() {
win.handle.close();
}
}
fn show_window(&mut self, id: WindowId) {
if let Some(win) = self.windows.get_mut(id) {
win.handle.bring_to_front_and_focus();
}
}
fn hide_window(&mut self, id: WindowId) {
if let Some(win) = self.windows.get_mut(id) {
win.handle.hide();
}
}
fn configure_window(&mut self, config: &WindowConfig, id: WindowId) {
if let Some(win) = self.windows.get_mut(id) {
config.apply_to_handle(&mut win.handle);
}
}
fn prepare_paint(&mut self, window_id: WindowId) {
if let Some(win) = self.windows.get_mut(window_id) {
win.prepare_paint(&mut self.command_queue, &mut self.data, &self.env);
}
self.do_update();
}
fn paint(&mut self, window_id: WindowId, piet: &mut Piet, invalid: &Region) {
if let Some(win) = self.windows.get_mut(window_id) {
win.do_paint(
piet,
invalid,
&mut self.command_queue,
&self.data,
&self.env,
);
}
}
fn dispatch_cmd(&mut self, cmd: Command) -> Handled {
let handled = self.delegate_cmd(&cmd);
self.do_update();
if handled.is_handled() {
return handled;
}
match cmd.target() {
Target::Window(id) => {
if cmd.is(sys_cmd::SHOW_CONTEXT_MENU) {
self.show_context_menu(id, &cmd);
return Handled::Yes;
}
if let Some(w) = self.windows.get_mut(id) {
return if cmd.is(sys_cmd::CLOSE_WINDOW) {
let handled = w.event(
&mut self.command_queue,
Event::WindowCloseRequested,
&mut self.data,
&self.env,
);
if !handled.is_handled() {
w.event(
&mut self.command_queue,
Event::WindowDisconnected,
&mut self.data,
&self.env,
);
}
handled
} else {
w.event(
&mut self.command_queue,
Event::Command(cmd),
&mut self.data,
&self.env,
)
};
}
}
Target::Widget(id) => {
for w in self.windows.iter_mut().filter(|w| w.may_contain_widget(id)) {
let event = Event::Internal(InternalEvent::TargetedCommand(cmd.clone()));
if w.event(&mut self.command_queue, event, &mut self.data, &self.env)
.is_handled()
{
return Handled::Yes;
}
}
}
Target::Global => {
for w in self.windows.iter_mut() {
let event = Event::Command(cmd.clone());
if w.event(&mut self.command_queue, event, &mut self.data, &self.env)
.is_handled()
{
return Handled::Yes;
}
}
}
Target::Auto => {
tracing::error!("{:?} reached window handler with `Target::Auto`", cmd);
}
}
Handled::No
}
fn do_window_event(&mut self, source_id: WindowId, event: Event) -> Handled {
match event {
Event::Command(..) | Event::Internal(InternalEvent::TargetedCommand(..)) => {
panic!("commands should be dispatched via dispatch_cmd");
}
_ => (),
}
let event = match self.delegate_event(source_id, event) {
Some(event) => event,
None => return Handled::Yes,
};
if let Some(win) = self.windows.get_mut(source_id) {
win.event(&mut self.command_queue, event, &mut self.data, &self.env)
} else {
Handled::No
}
}
fn show_context_menu(&mut self, window_id: WindowId, cmd: &Command) {
if let Some(win) = self.windows.get_mut(window_id) {
match cmd
.get_unchecked(sys_cmd::SHOW_CONTEXT_MENU)
.take()
.and_then(|b| b.downcast::<ContextMenu<T>>().ok())
{
Some(menu) => {
win.show_context_menu(menu.menu, menu.location, &self.data, &self.env)
}
None => panic!(
"{} command must carry a ContextMenu<application state>.",
sys_cmd::SHOW_CONTEXT_MENU
),
}
}
}
fn do_update(&mut self) {
for window in self.windows.iter_mut() {
window.update(&mut self.command_queue, &self.data, &self.env);
if let Some(focus_change) = window.ime_focus_change.take() {
let handle = window.handle.clone();
let f = Box::new(move || handle.set_focused_text_field(focus_change));
self.ime_focus_change = Some(f);
}
#[cfg(not(target_os = "macos"))]
window.update_menu(&self.data, &self.env);
}
#[cfg(target_os = "macos")]
{
use druid_shell::platform::mac::ApplicationExt as _;
let windows = &mut self.windows;
let window = self.menu_window.and_then(|w| windows.get_mut(w));
if let Some(window) = window {
window.update_menu(&self.data, &self.env);
} else if let Some(root_menu) = &mut self.root_menu {
if let Some(new_menu) = root_menu.update(None, &self.data, &self.env) {
self.app.set_menu(new_menu);
}
}
}
self.invalidate_and_finalize();
}
fn invalidate_and_finalize(&mut self) {
for win in self.windows.iter_mut() {
win.invalidate_and_finalize();
}
}
fn ime_update_fn(&self, window_id: WindowId, widget_id: WidgetId) -> Option<Box<ImeUpdateFn>> {
self.windows
.get(window_id)
.and_then(|window| window.ime_invalidation_fn(widget_id))
}
fn get_ime_lock(
&mut self,
window_id: WindowId,
token: TextFieldToken,
mutable: bool,
) -> Box<dyn InputHandler> {
self.windows
.get_mut(window_id)
.unwrap()
.get_ime_handler(token, mutable)
}
fn release_ime_lock(&mut self, window_id: WindowId, token: TextFieldToken) -> Option<WidgetId> {
self.windows
.get_mut(window_id)
.unwrap()
.release_ime_lock(token)
}
fn window_got_focus(&mut self, window_id: WindowId) {
if let Some(win) = self.windows.get_mut(window_id) {
if win.menu.is_some() {
self.menu_window = Some(window_id);
}
#[cfg(target_os = "macos")]
win.macos_update_app_menu(&self.data, &self.env)
}
}
}
impl<T: Data> DruidHandler<T> {
pub(crate) fn new_shared(app_state: AppState<T>, window_id: WindowId) -> DruidHandler<T> {
DruidHandler {
app_state,
window_id,
}
}
}
impl<T: Data> AppState<T> {
pub(crate) fn data(&self) -> T {
self.inner.borrow().data.clone()
}
pub(crate) fn env(&self) -> Env {
self.inner.borrow().env.clone()
}
pub(crate) fn add_window(&self, id: WindowId, window: PendingWindow<T>) {
self.inner.borrow_mut().windows.add(id, window);
}
fn connect_window(&mut self, window_id: WindowId, handle: WindowHandle) {
self.inner.borrow_mut().connect(window_id, handle)
}
fn remove_window(&mut self, window_id: WindowId) {
self.inner.borrow_mut().remove_window(window_id)
}
fn window_got_focus(&mut self, window_id: WindowId) {
self.inner.borrow_mut().window_got_focus(window_id)
}
fn do_window_event(&mut self, event: Event, window_id: WindowId) -> Handled {
let result = self.inner.borrow_mut().do_window_event(window_id, event);
self.process_commands();
self.inner.borrow_mut().do_update();
let ime_change = self.inner.borrow_mut().ime_focus_change.take();
if let Some(ime_change) = ime_change {
(ime_change)()
}
result
}
fn prepare_paint_window(&mut self, window_id: WindowId) {
self.inner.borrow_mut().prepare_paint(window_id);
}
fn paint_window(&mut self, window_id: WindowId, piet: &mut Piet, invalid: &Region) {
self.inner.borrow_mut().paint(window_id, piet, invalid);
}
fn idle(&mut self, token: IdleToken) {
match token {
RUN_COMMANDS_TOKEN => {
self.process_commands();
self.inner.borrow_mut().do_update();
}
EXT_EVENT_IDLE_TOKEN => {
self.process_ext_events();
self.process_commands();
self.inner.borrow_mut().do_update();
}
other => tracing::warn!("unexpected idle token {:?}", other),
}
}
pub(crate) fn handle_idle_callback(&mut self, cb: impl FnOnce(&mut T)) {
let mut inner = self.inner.borrow_mut();
cb(&mut inner.data);
inner.do_update();
}
fn process_commands(&mut self) {
loop {
let next_cmd = self.inner.borrow_mut().command_queue.pop_front();
match next_cmd {
Some(cmd) => self.handle_cmd(cmd),
None => break,
}
}
}
fn process_ext_events(&mut self) {
loop {
let ext_cmd = self.inner.borrow_mut().ext_event_host.recv();
match ext_cmd {
Some(cmd) => self.handle_cmd(cmd),
None => break,
}
}
}
fn handle_system_cmd(&mut self, cmd_id: u32, window_id: Option<WindowId>) {
self.inner
.borrow_mut()
.handle_menu_cmd(MenuItemId::new(cmd_id), window_id);
self.process_commands();
self.inner.borrow_mut().do_update();
}
fn handle_cmd(&mut self, cmd: Command) {
use Target as T;
match cmd.target() {
_ if cmd.is(sys_cmd::QUIT_APP) => self.quit(),
#[cfg(target_os = "macos")]
_ if cmd.is(sys_cmd::HIDE_APPLICATION) => self.hide_app(),
#[cfg(target_os = "macos")]
_ if cmd.is(sys_cmd::HIDE_OTHERS) => self.hide_others(),
_ if cmd.is(sys_cmd::NEW_WINDOW) => {
if let Err(e) = self.new_window(cmd) {
tracing::error!("failed to create window: '{}'", e);
}
}
_ if cmd.is(sys_cmd::NEW_SUB_WINDOW) => {
if let Err(e) = self.new_sub_window(cmd) {
tracing::error!("failed to create sub window: '{}'", e);
}
}
_ if cmd.is(sys_cmd::CLOSE_ALL_WINDOWS) => self.request_close_all_windows(),
T::Window(id) if cmd.is(sys_cmd::INVALIDATE_IME) => self.invalidate_ime(cmd, id),
T::Window(id) if cmd.is(sys_cmd::SHOW_OPEN_PANEL) => self.show_open_panel(cmd, id),
T::Window(id) if cmd.is(sys_cmd::SHOW_SAVE_PANEL) => self.show_save_panel(cmd, id),
T::Window(id) if cmd.is(sys_cmd::CONFIGURE_WINDOW) => self.configure_window(cmd, id),
T::Window(id) if cmd.is(sys_cmd::CLOSE_WINDOW) => {
if !self.inner.borrow_mut().dispatch_cmd(cmd).is_handled() {
self.request_close_window(id);
}
}
T::Window(id) if cmd.is(sys_cmd::SHOW_WINDOW) => self.show_window(id),
T::Window(id) if cmd.is(sys_cmd::HIDE_WINDOW) => self.hide_window(id),
T::Window(id) if cmd.is(sys_cmd::PASTE) => self.do_paste(id),
_ if cmd.is(sys_cmd::CLOSE_WINDOW) => {
tracing::warn!("CLOSE_WINDOW command must target a window.")
}
_ if cmd.is(sys_cmd::SHOW_WINDOW) => {
tracing::warn!("SHOW_WINDOW command must target a window.")
}
_ if cmd.is(sys_cmd::HIDE_WINDOW) => {
tracing::warn!("HIDE_WINDOW command must target a window.")
}
_ if cmd.is(sys_cmd::SHOW_OPEN_PANEL) => {
tracing::warn!("SHOW_OPEN_PANEL command must target a window.")
}
_ => {
self.inner.borrow_mut().dispatch_cmd(cmd);
}
}
}
fn show_open_panel(&mut self, cmd: Command, window_id: WindowId) {
let options = cmd.get_unchecked(sys_cmd::SHOW_OPEN_PANEL).to_owned();
let handle = self
.inner
.borrow_mut()
.windows
.get_mut(window_id)
.map(|w| w.handle.clone());
let accept_cmd = options.accept_cmd.unwrap_or(crate::commands::OPEN_FILE);
let accept_multiple_cmd = options
.accept_multiple_cmd
.unwrap_or(crate::commands::OPEN_FILES);
let cancel_cmd = options
.cancel_cmd
.unwrap_or(crate::commands::OPEN_PANEL_CANCELLED);
let token = handle.and_then(|mut handle| handle.open_file(options.opt));
if let Some(token) = token {
self.inner.borrow_mut().file_dialogs.insert(
token,
DialogInfo {
id: window_id,
accept_cmd,
accept_multiple_cmd,
cancel_cmd,
},
);
}
}
fn show_save_panel(&mut self, cmd: Command, window_id: WindowId) {
let options = cmd.get_unchecked(sys_cmd::SHOW_SAVE_PANEL).to_owned();
let handle = self
.inner
.borrow_mut()
.windows
.get_mut(window_id)
.map(|w| w.handle.clone());
let accept_cmd = options.accept_cmd.unwrap_or(crate::commands::SAVE_FILE_AS);
let accept_multiple_cmd = options
.accept_multiple_cmd
.unwrap_or(crate::commands::OPEN_FILES);
let cancel_cmd = options
.cancel_cmd
.unwrap_or(crate::commands::SAVE_PANEL_CANCELLED);
let token = handle.and_then(|mut handle| handle.save_as(options.opt));
if let Some(token) = token {
self.inner.borrow_mut().file_dialogs.insert(
token,
DialogInfo {
id: window_id,
accept_cmd,
accept_multiple_cmd,
cancel_cmd,
},
);
}
}
fn handle_dialog_multiple_response(
&mut self,
token: FileDialogToken,
file_info: Vec<FileInfo>,
) {
let mut inner = self.inner.borrow_mut();
if let Some(dialog_info) = inner.file_dialogs.remove(&token) {
let cmd = if !file_info.is_empty() {
dialog_info
.accept_multiple_cmd
.with(file_info)
.to(dialog_info.id)
} else {
dialog_info.cancel_cmd.to(dialog_info.id)
};
inner.append_command(cmd);
} else {
tracing::error!("unknown dialog token");
}
std::mem::drop(inner);
self.process_commands();
self.inner.borrow_mut().do_update();
}
fn handle_dialog_response(&mut self, token: FileDialogToken, file_info: Option<FileInfo>) {
let mut inner = self.inner.borrow_mut();
if let Some(dialog_info) = inner.file_dialogs.remove(&token) {
let cmd = if let Some(info) = file_info {
dialog_info.accept_cmd.with(info).to(dialog_info.id)
} else {
dialog_info.cancel_cmd.to(dialog_info.id)
};
inner.append_command(cmd);
} else {
tracing::error!("unknown dialog token");
}
std::mem::drop(inner);
self.process_commands();
self.inner.borrow_mut().do_update();
}
fn new_window(&mut self, cmd: Command) -> Result<(), Box<dyn std::error::Error>> {
let desc = cmd.get_unchecked(sys_cmd::NEW_WINDOW);
let desc = desc.take().unwrap().downcast::<WindowDesc<T>>().unwrap();
let window = desc.build_native(self)?;
window.show();
Ok(())
}
fn new_sub_window(&mut self, cmd: Command) -> Result<(), Box<dyn std::error::Error>> {
if let Some(transfer) = cmd.get(sys_cmd::NEW_SUB_WINDOW) {
if let Some(sub_window_desc) = transfer.take() {
let window = sub_window_desc.make_sub_window(self)?;
window.show();
Ok(())
} else {
panic!(
"{} command must carry a SubWindowDesc internally",
sys_cmd::NEW_SUB_WINDOW
)
}
} else {
panic!(
"{} command must carry a SingleUse<SubWindowDesc>",
sys_cmd::NEW_SUB_WINDOW
)
}
}
fn request_close_window(&mut self, id: WindowId) {
self.inner.borrow_mut().request_close_window(id);
}
fn request_close_all_windows(&mut self) {
self.inner.borrow_mut().request_close_all_windows();
}
fn show_window(&mut self, id: WindowId) {
self.inner.borrow_mut().show_window(id);
}
fn hide_window(&mut self, id: WindowId) {
self.inner.borrow_mut().hide_window(id);
}
fn configure_window(&mut self, cmd: Command, id: WindowId) {
if let Some(config) = cmd.get(sys_cmd::CONFIGURE_WINDOW) {
self.inner.borrow_mut().configure_window(config, id);
}
}
fn do_paste(&mut self, window_id: WindowId) {
let event = Event::Paste(self.inner.borrow().app.clipboard());
self.inner.borrow_mut().do_window_event(window_id, event);
}
fn invalidate_ime(&mut self, cmd: Command, id: WindowId) {
let params = cmd.get_unchecked(sys_cmd::INVALIDATE_IME);
let update_fn = self.inner.borrow().ime_update_fn(id, params.widget);
if let Some(func) = update_fn {
func(params.event);
}
}
fn release_ime_lock(&mut self, window_id: WindowId, token: TextFieldToken) {
let needs_update = self.inner.borrow_mut().release_ime_lock(window_id, token);
if let Some(widget) = needs_update {
let event = Event::Internal(InternalEvent::RouteImeStateChange(widget));
self.do_window_event(event, window_id);
}
}
fn quit(&self) {
self.inner.borrow().app.quit()
}
#[cfg(target_os = "macos")]
fn hide_app(&self) {
use druid_shell::platform::mac::ApplicationExt as _;
self.inner.borrow().app.hide()
}
#[cfg(target_os = "macos")]
fn hide_others(&mut self) {
use druid_shell::platform::mac::ApplicationExt as _;
self.inner.borrow().app.hide_others();
}
pub(crate) fn build_native_window(
&mut self,
id: WindowId,
mut pending: PendingWindow<T>,
config: WindowConfig,
) -> Result<WindowHandle, PlatformError> {
let mut builder = WindowBuilder::new(self.app());
config.apply_to_builder(&mut builder);
let data = self.data();
let env = self.env();
pending.size_policy = config.size_policy;
pending.title.resolve(&data, &env);
builder.set_title(pending.title.display_text().to_string());
let platform_menu = pending
.menu
.as_mut()
.map(|m| m.initialize(Some(id), &data, &env));
if let Some(menu) = platform_menu {
builder.set_menu(menu);
}
let handler = DruidHandler::new_shared((*self).clone(), id);
builder.set_handler(Box::new(handler));
self.add_window(id, pending);
builder.build()
}
}
impl<T: Data> crate::shell::AppHandler for AppHandler<T> {
fn command(&mut self, id: u32) {
self.app_state.handle_system_cmd(id, None)
}
}
impl<T: Data> WinHandler for DruidHandler<T> {
fn connect(&mut self, handle: &WindowHandle) {
self.app_state
.connect_window(self.window_id, handle.clone());
let event = Event::WindowConnected;
self.app_state.do_window_event(event, self.window_id);
}
fn prepare_paint(&mut self) {
self.app_state.prepare_paint_window(self.window_id);
}
fn paint(&mut self, piet: &mut Piet, region: &Region) {
self.app_state.paint_window(self.window_id, piet, region);
}
fn size(&mut self, size: Size) {
let event = Event::WindowSize(size);
self.app_state.do_window_event(event, self.window_id);
}
fn scale(&mut self, scale: Scale) {
let event = Event::WindowScale(scale);
self.app_state.do_window_event(event, self.window_id);
}
fn command(&mut self, id: u32) {
self.app_state.handle_system_cmd(id, Some(self.window_id));
}
fn save_as(&mut self, token: FileDialogToken, file_info: Option<FileInfo>) {
self.app_state.handle_dialog_response(token, file_info);
}
fn open_file(&mut self, token: FileDialogToken, file_info: Option<FileInfo>) {
self.app_state.handle_dialog_response(token, file_info);
}
fn open_files(&mut self, token: FileDialogToken, file_info: Vec<FileInfo>) {
self.app_state
.handle_dialog_multiple_response(token, file_info);
}
fn mouse_down(&mut self, event: &MouseEvent) {
let event = Event::MouseDown(event.clone().into());
self.app_state.do_window_event(event, self.window_id);
}
fn mouse_up(&mut self, event: &MouseEvent) {
let event = Event::MouseUp(event.clone().into());
self.app_state.do_window_event(event, self.window_id);
}
fn mouse_move(&mut self, event: &MouseEvent) {
let event = Event::MouseMove(event.clone().into());
self.app_state.do_window_event(event, self.window_id);
}
fn mouse_leave(&mut self) {
self.app_state
.do_window_event(Event::Internal(InternalEvent::MouseLeave), self.window_id);
}
fn key_down(&mut self, event: KeyEvent) -> bool {
self.app_state
.do_window_event(Event::KeyDown(event), self.window_id)
.is_handled()
}
fn key_up(&mut self, event: KeyEvent) {
self.app_state
.do_window_event(Event::KeyUp(event), self.window_id);
}
fn wheel(&mut self, event: &MouseEvent) {
self.app_state
.do_window_event(Event::Wheel(event.clone().into()), self.window_id);
}
fn zoom(&mut self, delta: f64) {
let event = Event::Zoom(delta);
self.app_state.do_window_event(event, self.window_id);
}
fn got_focus(&mut self) {
self.app_state.window_got_focus(self.window_id);
}
fn timer(&mut self, token: TimerToken) {
self.app_state
.do_window_event(Event::Timer(token), self.window_id);
}
fn idle(&mut self, token: IdleToken) {
self.app_state.idle(token);
}
fn as_any(&mut self) -> &mut dyn Any {
self
}
fn acquire_input_lock(
&mut self,
token: TextFieldToken,
mutable: bool,
) -> Box<dyn InputHandler> {
self.app_state
.inner
.borrow_mut()
.get_ime_lock(self.window_id, token, mutable)
}
fn release_input_lock(&mut self, token: TextFieldToken) {
self.app_state.release_ime_lock(self.window_id, token);
}
fn request_close(&mut self) {
self.app_state
.handle_cmd(sys_cmd::CLOSE_WINDOW.to(self.window_id));
self.app_state.process_commands();
self.app_state.inner.borrow_mut().do_update();
}
fn destroy(&mut self) {
self.app_state.remove_window(self.window_id);
}
}
impl<T> Default for Windows<T> {
fn default() -> Self {
Windows {
windows: HashMap::new(),
pending: HashMap::new(),
}
}
}