diff --git a/crates/jackdaw_panels/src/tabs.rs b/crates/jackdaw_panels/src/tabs.rs index 9d9929a0..36eedf1b 100644 --- a/crates/jackdaw_panels/src/tabs.rs +++ b/crates/jackdaw_panels/src/tabs.rs @@ -21,8 +21,7 @@ pub struct DockTabPlugin; impl Plugin for DockTabPlugin { fn build(&self, app: &mut App) { - app.add_systems(Update, (handle_dock_tab_clicks, show_close_on_hover)) - .add_observer(on_close_button_click); + app.add_systems(Update, (handle_dock_tab_clicks, show_close_on_hover)); } } @@ -318,15 +317,3 @@ fn show_close_on_hover( } } } - -fn on_close_button_click( - trigger: On>, - close_buttons: Query<&crate::area::DockTabCloseButton>, - mut tree: ResMut, -) { - let entity = trigger.event_target(); - let Ok(close_btn) = close_buttons.get(entity) else { - return; - }; - tree.remove_tab(close_btn.tab_id); -} diff --git a/src/core_extension.rs b/src/core_extension.rs index 0cfad9cf..16a71a9e 100644 --- a/src/core_extension.rs +++ b/src/core_extension.rs @@ -243,6 +243,7 @@ impl JackdawExtension for JackdawCoreExtension { crate::viewport::add_to_extension(ctx); crate::command_palette::add_to_extension(ctx); crate::document_ops::add_to_extension(ctx); + crate::dock_ops::add_to_extension(ctx); } fn register_input_context(&self, app: &mut App) { diff --git a/src/dock_ops.rs b/src/dock_ops.rs new file mode 100644 index 00000000..bf2e1c6c --- /dev/null +++ b/src/dock_ops.rs @@ -0,0 +1,66 @@ +//! Operators for the panel docking system. + +use bevy::picking::pointer::PointerButton; +use bevy::prelude::*; +use jackdaw_api::prelude::*; +use jackdaw_panels::area::{DockTab, DockTabCloseButton}; +use jackdaw_panels::tree::{DockTree, TabId}; + +pub struct DockOpsPlugin; + +impl Plugin for DockOpsPlugin { + fn build(&self, app: &mut App) { + app.add_observer(on_close_button_click) + .add_observer(on_tab_middle_click); + } +} + +pub(crate) fn add_to_extension(ctx: &mut ExtensionContext) { + ctx.register_operator::(); +} + +#[operator( + id = "dock.close_tab", + label = "Close Tab", + description = "Close the specified docked tab.", + allows_undo = false, + params(tab_id(i64, doc = "TabId of the tab to close.")) +)] +pub(crate) fn dock_close_tab( + In(params): In, + mut tree: ResMut, +) -> OperatorResult { + let Some(tab_id) = params.as_int("tab_id") else { + warn!("dock.close_tab: missing 'tab_id' parameter"); + return OperatorResult::Cancelled; + }; + tree.remove_tab(TabId(tab_id as u64)); + OperatorResult::Finished +} + +fn on_close_button_click( + trigger: On>, + close_buttons: Query<&DockTabCloseButton>, + mut commands: Commands, +) { + let Ok(close_btn) = close_buttons.get(trigger.event_target()) else { + return; + }; + commands + .operator(DockCloseTabOp::ID) + .param("tab_id", close_btn.tab_id.0 as i64) + .call(); +} + +fn on_tab_middle_click(trigger: On>, tabs: Query<&DockTab>, mut commands: Commands) { + if trigger.event().button != PointerButton::Middle { + return; + } + let Ok(tab) = tabs.get(trigger.event_target()) else { + return; + }; + commands + .operator(DockCloseTabOp::ID) + .param("tab_id", tab.tab_id.0 as i64) + .call(); +} diff --git a/src/lib.rs b/src/lib.rs index 884a8317..c5c926cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,6 +34,7 @@ use std::{collections::BTreeMap, marker::PhantomData}; pub use inspector::{EditorCategory, EditorDescription, EditorHidden, SkipSerialization}; pub mod core_extension; +pub mod dock_ops; pub mod document_ops; pub mod ext_build; mod extension_lifecycle; @@ -313,6 +314,7 @@ impl Plugin for EditorCorePlugin { .add_plugins(extensions_dialog::ExtensionsDialogPlugin) .add_plugins(hot_reload::HotReloadPlugin) .add_plugins(pie::PiePlugin) + .add_plugins(dock_ops::DockOpsPlugin) // Force-exit on `AppExit`: bypass wgpu device cleanup // and AsyncComputeTaskPool shutdown that otherwise hang // the process after window close. Hosted here so every