use std::rc::Rc;
use teksilo_canvas::{Rect, Size, SizeProposal};
use teksilo_core::PlatformTitleBarHost;
use teksilo_core::accessibility::AccessNodeBuilder;
use teksilo_core::event::{EventResponse, PointerButton, WidgetEvent};
use teksilo_core::gesture::DragPhase;
use teksilo_core::widget::{LayoutContext, PaintContext, PendingChild, Widget, WidgetPlacement};
use teksilo_core::widget_builder::HandlerSet;
use teksilo_core::widget_id::WidgetId;
pub struct DragRegion {
host: Rc<dyn PlatformTitleBarHost>,
pending_child: Option<PendingChild>,
child_id: Option<WidgetId>,
close_action: Option<Rc<dyn Fn(&mut teksilo_core::widget::EventContext)>>,
}
impl std::fmt::Debug for DragRegion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DragRegion")
.field("has_child", &self.pending_child.is_some())
.finish_non_exhaustive()
}
}
impl DragRegion {
pub fn new(host: Rc<dyn PlatformTitleBarHost>) -> Self {
Self {
host,
pending_child: None,
child_id: None,
close_action: None,
}
}
pub fn with_child(host: Rc<dyn PlatformTitleBarHost>, child: Box<dyn Widget>) -> Self {
Self {
host,
pending_child: Some(PendingChild::Deferred(child)),
child_id: None,
close_action: None,
}
}
pub fn with_child_id(host: Rc<dyn PlatformTitleBarHost>, id: WidgetId) -> Self {
Self {
host,
pending_child: Some(PendingChild::Id(id)),
child_id: None,
close_action: None,
}
}
pub fn close_action(
mut self,
action: Option<Rc<dyn Fn(&mut teksilo_core::widget::EventContext)>>,
) -> Self {
self.close_action = action;
self
}
}
impl Widget for DragRegion {
fn build(&mut self, ctx: &mut teksilo_core::build_context::BuildContext) -> Vec<WidgetId> {
if let Some(pending) = self.pending_child.take() {
self.child_id = Some(match pending {
PendingChild::Id(id) => id,
PendingChild::Deferred(w) => ctx.add_boxed(w),
});
}
let host_drag = self.host.clone();
let host_pointer = self.host.clone();
let has_os_window_menu = self.host.has_window_menu();
let close_action = self.close_action.clone();
let mut handlers = HandlerSet::new()
.on_drag(move |phase, _ctx| {
if let DragPhase::Started {
button: PointerButton::Primary,
..
} = phase
{
let _ = host_drag.begin_drag();
}
})
.on_double_tap(move |_pos, ctx| {
if let Some(w) = ctx.window() {
let next = if w.placement().get().is_maximized() {
teksilo_core::WindowPlacement::Floating
} else {
teksilo_core::WindowPlacement::Maximized
};
w.placement().set(next);
}
})
.on_pointer_event(move |evt, _ctx| {
if !has_os_window_menu {
return EventResponse::Ignored;
}
if let WidgetEvent::PointerDown {
button: PointerButton::Secondary,
position,
..
} = evt
{
let _ = host_pointer.show_window_menu(*position);
return EventResponse::Handled;
}
EventResponse::Ignored
});
if !has_os_window_menu {
handlers = handlers.context_menu(move |_at, ctx| {
super::window_menu::build_window_menu(ctx, close_action.clone())
});
}
ctx.apply_self_handlers(handlers);
self.child_id.into_iter().collect()
}
fn layout_response(
&self,
proposal: SizeProposal,
_ctx: &LayoutContext,
) -> teksilo_core::widget::LayoutResponse {
teksilo_core::widget::LayoutResponse::flexible(
Size::new(0.0, proposal.height.unwrap_or(0.0)),
1.0,
)
}
fn place_children(
&self,
bounds: Rect,
_proposal: SizeProposal,
children: &mut [WidgetPlacement],
_ctx: &LayoutContext,
) {
for child in children.iter_mut() {
child.origin = bounds.origin();
child.size = bounds.size();
}
}
fn paint(&self, _bounds: Rect, _canvas: &mut teksilo_canvas::Canvas, _ctx: &PaintContext) {
}
fn accessibility(&self, builder: &mut AccessNodeBuilder) {
builder.set_hidden();
}
fn children(&self) -> Vec<WidgetId> {
self.child_id.into_iter().collect()
}
}