use std::any::Any; use async_channel::Sender; use galaxyui::elements::Point; use galaxyui::event::DispatchedEvent; use galaxyui::geometry::rect::RectF; use galaxyui::geometry::vector::Vector2F; use galaxyui::{ AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint, }; use super::view::TerminalAction; pub struct TerminalSizeElement { child: Box, resize_tx: Sender, } impl TerminalSizeElement { pub fn new(resize_tx: Sender, child: Box) -> Self { TerminalSizeElement { child, resize_tx } } } impl Element for TerminalSizeElement { fn layout( &mut self, constraint: SizeConstraint, ctx: &mut LayoutContext, app: &AppContext, ) -> Vector2F { self.child.layout(constraint, ctx, app) } fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &AppContext) { self.child.after_layout(ctx, app); let terminal_size = self.size().expect("Size should be present"); // It's possible that the underlying shell session has been terminated // but we're showing a read-only terminal view, in which case, the // channel will be closed. If we're unable to send a resize through // the channel, that's fine, just ignore the error. let _ = self.resize_tx.try_send(terminal_size); } fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) { self.child.paint(origin, ctx, app) } fn size(&self) -> Option { self.child.size() } fn origin(&self) -> Option { self.child.origin() } fn bounds(&self) -> Option { self.child.bounds() } fn parent_data(&self) -> Option<&dyn Any> { self.child.parent_data() } fn dispatch_event( &mut self, event: &DispatchedEvent, ctx: &mut EventContext, app: &AppContext, ) -> bool { let handled_by_child = self.child.dispatch_event(event, ctx, app); let Some(z_index) = self.z_index() else { return false; }; if !handled_by_child { if let Some(event_at_z_index) = event.at_z_index(z_index, ctx) { match event_at_z_index { Event::DragFiles { location } => { if self.mouse_position_is_in_bounds(*location) { ctx.dispatch_typed_action(TerminalAction::StartFileDropTarget); } else { ctx.dispatch_typed_action(TerminalAction::StopFileDropTarget); } return true; } Event::DragFileExit => { ctx.dispatch_typed_action(TerminalAction::StopFileDropTarget); return true; } Event::DragAndDropFiles { paths, location } => { if self.mouse_position_is_in_bounds(*location) && !paths.is_empty() { let paths = paths.iter().map(ToOwned::to_owned).collect(); ctx.dispatch_typed_action(TerminalAction::DragAndDropFiles(paths)); } return true; } _ => {} }; } } handled_by_child } } impl TerminalSizeElement { fn mouse_position_is_in_bounds(&self, position: Vector2F) -> bool { let Some(bounds) = self.bounds() else { return false; }; bounds.contains_point(position) } }