pub use super::render::register_input_coordinator_context_menu;
use super::render::register_context_manager_context_menu;
use super::settings::ContextMenuSettings;
use super::state::ContextMenuState;
use super::types::{ContextMenuRenderKind, ContextMenuView};
use crate::docking::panels::DockPanel;
use crate::input::core::coordinator::LayerId;
use crate::input::{Sense, WidgetKind};
use crate::layout::{CompositeKind, CompositeRegistration, ContextMenuHandle, ContextMenuNode, DismissFrame, EventBuilder, LayoutManager, LayoutNodeId, OverlayEntry, OverlayKind, WidgetNode};
use crate::render::RenderContext;
use crate::types::{Rect, WidgetId};
pub fn register_layout_manager_context_menu<P: DockPanel>(
layout: &mut LayoutManager<P>,
render: &mut dyn RenderContext,
parent: LayoutNodeId,
slot_id: &str,
handle: &ContextMenuHandle,
overlay_rect: Rect,
anchor: Option<Rect>,
view: &mut ContextMenuView<'_>,
settings: &ContextMenuSettings,
kind: &ContextMenuRenderKind<'_>,
) -> Option<ContextMenuNode> {
let id: WidgetId = handle.id.clone();
let mut state = layout.context_menus.remove(&id).unwrap_or_default();
layout.push_overlay(OverlayEntry {
id: slot_id.to_string(),
kind: OverlayKind::ContextMenu,
rect: overlay_rect,
anchor,
});
let slot_rect = overlay_rect;
let layer = LayerId::new("context_menu");
let z_order = layout.z_layers().context_menu as u32;
layout.push_dismiss_frame(DismissFrame {
z: z_order,
rect: slot_rect,
overlay_id: WidgetId(slot_id.to_owned()),
});
layout.ctx_mut().input.push_layer(layer.clone(), z_order, true);
let node_id = layout.tree_mut().add_widget(parent, WidgetNode { id: id.clone(), kind: WidgetKind::ContextMenu, rect: Rect::new(state.x, state.y, 0.0, 0.0), sense: Sense::CLICK });
layout.dispatcher_mut().on_prefix(
format!("{}:item:", id.0),
EventBuilder::ContextMenuItem { handle: handle.clone() },
);
let prefix = format!("{}:item:", id.0);
state.sync_hover_from_layout(layout, &prefix);
register_context_manager_context_menu(
layout.ctx_mut(), render, id.clone(), &mut state, view, settings, kind, &layer,
);
layout.push_composite_registration(CompositeRegistration {
kind: CompositeKind::ContextMenu,
slot_id: slot_id.to_string(),
widget_id: id.clone(),
frame_rect: overlay_rect,
});
layout.context_menus.insert(id, state);
Some(ContextMenuNode(node_id))
}
pub fn handle_context_menu_dismiss(
state: &ContextMenuState,
click_pos: (f64, f64),
menu_rect: Rect,
) -> bool {
if !state.is_open {
return false;
}
!menu_rect.contains(click_pos.0, click_pos.1)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ContextMenuKey {
ArrowDown,
ArrowUp,
Enter,
Esc,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ContextMenuKeyResult {
Close,
Activate(usize),
Hovered(usize),
None,
}
pub fn handle_context_menu_keyboard(
state: &mut ContextMenuState,
key: ContextMenuKey,
enabled_count: usize,
) -> ContextMenuKeyResult {
match key {
ContextMenuKey::Esc => {
state.close();
ContextMenuKeyResult::Close
}
ContextMenuKey::Enter => {
match state.hovered_index {
Some(idx) => ContextMenuKeyResult::Activate(idx),
None => ContextMenuKeyResult::None,
}
}
ContextMenuKey::ArrowDown => {
if enabled_count == 0 {
return ContextMenuKeyResult::None;
}
let next = match state.hovered_index {
None => 0,
Some(cur) => (cur + 1).min(enabled_count.saturating_sub(1)),
};
state.hovered_index = Some(next);
ContextMenuKeyResult::Hovered(next)
}
ContextMenuKey::ArrowUp => {
if enabled_count == 0 {
return ContextMenuKeyResult::None;
}
let next = match state.hovered_index {
None => enabled_count.saturating_sub(1),
Some(cur) => cur.saturating_sub(1),
};
state.hovered_index = Some(next);
ContextMenuKeyResult::Hovered(next)
}
}
}