use keyboard_types::Code as KeyCode;
use crate::io::{NodeGraphInteractionState, NodeGraphViewState};
use crate::rules::{DeletePlan, plan_delete_elements_with_policy};
use jellyflow_core::core::Graph;
use jellyflow_core::ops::{GraphOp, GraphTransaction};
use super::types::{
DELETE_SELECTION_TRANSACTION_LABEL, DeleteElements, DeleteSelectionError, PreDeleteRequest,
};
pub fn plan_delete_selection(
graph: &Graph,
view_state: &NodeGraphViewState,
interaction: &NodeGraphInteractionState,
) -> DeletePlan {
let elements = delete_selection_elements(view_state);
plan_delete_elements(graph, interaction, &elements)
}
pub fn plan_delete_selection_for_key(
graph: &Graph,
view_state: &NodeGraphViewState,
interaction: &NodeGraphInteractionState,
key: KeyCode,
) -> Option<DeletePlan> {
let keyboard = interaction.keyboard_interaction();
if keyboard.disable_keyboard_a11y || !keyboard.delete_key.matches(key) {
return None;
}
Some(plan_delete_selection(graph, view_state, interaction))
}
pub fn delete_selection_elements(view_state: &NodeGraphViewState) -> DeleteElements {
DeleteElements::new(
view_state.selected_nodes.iter().copied(),
view_state.selected_edges.iter().copied(),
)
}
pub fn plan_delete_elements(
graph: &Graph,
interaction: &NodeGraphInteractionState,
elements: &DeleteElements,
) -> DeletePlan {
plan_delete_elements_with_policy(
graph,
elements.nodes().iter().copied(),
elements.edges().iter().copied(),
interaction,
)
}
pub fn prepare_delete_selection(
graph: &Graph,
view_state: &NodeGraphViewState,
interaction: &NodeGraphInteractionState,
) -> Result<Option<PreDeleteRequest>, DeleteSelectionError> {
let requested = delete_selection_elements(view_state);
prepare_delete_elements(graph, interaction, requested)
}
pub fn prepare_delete_selection_for_key(
graph: &Graph,
view_state: &NodeGraphViewState,
interaction: &NodeGraphInteractionState,
key: KeyCode,
) -> Result<Option<PreDeleteRequest>, DeleteSelectionError> {
let keyboard = interaction.keyboard_interaction();
if keyboard.disable_keyboard_a11y || !keyboard.delete_key.matches(key) {
return Ok(None);
}
prepare_delete_selection(graph, view_state, interaction)
}
pub fn prepare_delete_elements(
graph: &Graph,
interaction: &NodeGraphInteractionState,
requested: DeleteElements,
) -> Result<Option<PreDeleteRequest>, DeleteSelectionError> {
let plan = plan_delete_elements(graph, interaction, &requested);
if plan.is_reject() {
return Err(DeleteSelectionError::Rejected {
diagnostics: plan.diagnostics,
});
}
let planned = delete_elements_from_plan(&plan);
if planned.is_empty() {
return Ok(None);
}
Ok(Some(PreDeleteRequest::new(requested, planned)))
}
pub fn delete_elements_from_plan(plan: &DeletePlan) -> DeleteElements {
let mut nodes = Vec::new();
let mut edges = Vec::new();
for op in plan.ops() {
match op {
GraphOp::RemoveNode {
id,
edges: removed_edges,
..
} => {
nodes.push(*id);
edges.extend(removed_edges.iter().map(|(edge_id, _)| *edge_id));
}
GraphOp::RemovePort {
edges: removed_edges,
..
} => {
edges.extend(removed_edges.iter().map(|(edge_id, _)| *edge_id));
}
GraphOp::RemoveEdge { id, .. } => edges.push(*id),
_ => {}
}
}
DeleteElements::new(nodes, edges)
}
pub fn delete_selection_transaction(plan: &DeletePlan) -> Option<GraphTransaction> {
if !plan.is_accept() || plan.ops().is_empty() {
return None;
}
Some(
GraphTransaction::from_ops(plan.ops().iter().cloned())
.with_label(DELETE_SELECTION_TRANSACTION_LABEL),
)
}
pub fn delete_selection_transaction_from_plan(plan: DeletePlan) -> Option<GraphTransaction> {
if !plan.is_accept() || plan.ops().is_empty() {
return None;
}
Some(GraphTransaction::from_ops(plan.into_ops()).with_label(DELETE_SELECTION_TRANSACTION_LABEL))
}