use crate::app::{PendingWindow, WindowConfig};
use crate::commands::{SUB_WINDOW_HOST_TO_PARENT, SUB_WINDOW_PARENT_TO_HOST};
use crate::lens::Unit;
use crate::widget::prelude::*;
use crate::win_handler::AppState;
use crate::{Data, Point, Widget, WidgetExt, WidgetId, WidgetPod, WindowHandle, WindowId};
use druid_shell::Error;
use std::any::Any;
use std::ops::Deref;
use tracing::{instrument, warn};
pub(crate) struct SubWindowDesc {
pub(crate) host_id: WidgetId,
pub(crate) sub_window_root: Box<dyn Widget<()>>,
pub(crate) window_config: WindowConfig,
pub window_id: WindowId,
}
pub(crate) struct SubWindowUpdate {
pub(crate) data: Option<Box<dyn Any>>,
pub(crate) env: Option<Env>,
}
impl SubWindowDesc {
pub fn new<U, W: Widget<U>>(
parent_id: WidgetId,
window_config: WindowConfig,
widget: W,
data: U,
env: Env,
) -> SubWindowDesc
where
W: 'static,
U: Data,
{
let host_id = WidgetId::next();
let sub_window_host = SubWindowHost::new(host_id, parent_id, widget, data, env).boxed();
SubWindowDesc {
host_id,
sub_window_root: sub_window_host,
window_config,
window_id: WindowId::next(),
}
}
pub(crate) fn make_sub_window<T: Data>(
self,
app_state: &mut AppState<T>,
) -> Result<WindowHandle, Error> {
let sub_window_root = self.sub_window_root;
let pending = PendingWindow::new(sub_window_root.lens(Unit::default()));
app_state.build_native_window(self.window_id, pending, self.window_config)
}
}
struct SubWindowHost<U, W: Widget<U>> {
id: WidgetId,
parent_id: WidgetId,
child: WidgetPod<U, W>,
data: U,
env: Env,
}
impl<U, W: Widget<U>> SubWindowHost<U, W> {
pub(crate) fn new(id: WidgetId, parent_id: WidgetId, widget: W, data: U, env: Env) -> Self {
SubWindowHost {
id,
parent_id,
data,
env,
child: WidgetPod::new(widget),
}
}
}
impl<U: Data, W: Widget<U>> Widget<()> for SubWindowHost<U, W> {
#[instrument(
name = "SubWindowHost",
level = "trace",
skip(self, ctx, event, _data, _env)
)]
fn event(&mut self, ctx: &mut EventCtx, event: &Event, _data: &mut (), _env: &Env) {
match event {
Event::Command(cmd) if cmd.is(SUB_WINDOW_PARENT_TO_HOST) => {
let update = cmd.get_unchecked(SUB_WINDOW_PARENT_TO_HOST);
if let Some(data_update) = &update.data {
if let Some(dc) = data_update.downcast_ref::<U>() {
self.data = dc.deref().clone();
ctx.request_update();
} else {
warn!("Received a sub window parent to host command that could not be unwrapped. \
This could mean that the sub window you requested and the enclosing widget pod that you opened it from do not share a common data type. \
Make sure you have a widget pod between your requesting widget and any lenses." )
}
}
if let Some(env_update) = &update.env {
self.env = env_update.clone()
}
ctx.set_handled();
}
_ => {
let old = self.data.clone(); self.child.event(ctx, event, &mut self.data, &self.env);
if !old.same(&self.data) {
ctx.submit_command(
SUB_WINDOW_HOST_TO_PARENT
.with(Box::new(self.data.clone()))
.to(self.parent_id),
)
}
}
}
}
#[instrument(
name = "SubWindowHost",
level = "trace",
skip(self, ctx, event, _data, _env)
)]
fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, _data: &(), _env: &Env) {
self.child.lifecycle(ctx, event, &self.data, &self.env)
}
#[instrument(
name = "SubWindowHost",
level = "trace",
skip(self, ctx, _old_data, _data, _env)
)]
fn update(&mut self, ctx: &mut UpdateCtx, _old_data: &(), _data: &(), _env: &Env) {
if ctx.has_requested_update() {
self.child.update(ctx, &self.data, &self.env);
}
}
#[instrument(
name = "SubWindowHost",
level = "trace",
skip(self, ctx, bc, _data, _env)
)]
fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, _data: &(), _env: &Env) -> Size {
let size = self.child.layout(ctx, bc, &self.data, &self.env);
self.child.set_origin(ctx, Point::ORIGIN);
size
}
#[instrument(name = "SubWindowHost", level = "trace", skip(self, ctx, _data, _env))]
fn paint(&mut self, ctx: &mut PaintCtx, _data: &(), _env: &Env) {
self.child.paint_raw(ctx, &self.data, &self.env);
}
fn id(&self) -> Option<WidgetId> {
Some(self.id)
}
}