use bevy::{input_focus::InputFocus, prelude::*};
use bevy_enhanced_input::prelude::{Press, *};
use jackdaw_api::prelude::*;
use crate::brush::{
BrushDragState, BrushEditMode, BrushSelection, ClipState, EdgeDragState, EditMode,
VertexDragState,
};
use crate::core_extension::CoreExtensionInputContext;
use crate::draw_brush::DrawBrushState;
use crate::selection::Selection;
pub(crate) fn add_to_extension(ctx: &mut ExtensionContext) {
ctx.register_operator::<EditModeObjectOp>()
.register_operator::<EditModeVertexOp>()
.register_operator::<EditModeEdgeOp>()
.register_operator::<EditModeFaceOp>()
.register_operator::<EditModeClipOp>()
.register_operator::<BrushExitEditModeOp>();
let ext = ctx.id();
ctx.spawn((
Action::<EditModeVertexOp>::new(),
ActionOf::<CoreExtensionInputContext>::new(ext),
bindings![(KeyCode::Digit1, Press::default())],
));
ctx.spawn((
Action::<EditModeEdgeOp>::new(),
ActionOf::<CoreExtensionInputContext>::new(ext),
bindings![(KeyCode::Digit2, Press::default())],
));
ctx.spawn((
Action::<EditModeFaceOp>::new(),
ActionOf::<CoreExtensionInputContext>::new(ext),
bindings![(KeyCode::Digit3, Press::default())],
));
ctx.spawn((
Action::<EditModeClipOp>::new(),
ActionOf::<CoreExtensionInputContext>::new(ext),
bindings![(KeyCode::Digit4, Press::default())],
));
ctx.spawn((
Action::<BrushExitEditModeOp>::new(),
ActionOf::<CoreExtensionInputContext>::new(ext),
bindings![(KeyCode::Escape, Press::default())],
));
}
fn can_exit_brush_edit(
edit_mode: Res<EditMode>,
active: ActiveModalQuery,
face_drag: Res<BrushDragState>,
vertex_drag: Res<VertexDragState>,
edge_drag: Res<EdgeDragState>,
clip_state: Res<ClipState>,
) -> bool {
if active.is_modal_running() {
return false;
}
if face_drag.active || vertex_drag.active || edge_drag.active {
return false;
}
if face_drag.pending.is_some() || vertex_drag.pending.is_some() || edge_drag.pending.is_some() {
return false;
}
match *edit_mode {
EditMode::BrushEdit(BrushEditMode::Clip) if !clip_state.points.is_empty() => false,
EditMode::BrushEdit(_) => true,
_ => false,
}
}
#[operator(
id = "brush.exit_edit_mode",
label = "Exit Edit Mode",
description = "Stop editing the brush and return to selecting whole entities.",
is_available = can_exit_brush_edit,
allows_undo = false,
)]
pub(crate) fn brush_exit_edit_mode(
_: In<OperatorParameters>,
mut edit_mode: ResMut<EditMode>,
mut brush_selection: ResMut<BrushSelection>,
) -> OperatorResult {
*edit_mode = EditMode::Object;
brush_selection.clear();
OperatorResult::Finished
}
fn can_change_edit_mode(
input_focus: Res<InputFocus>,
active: ActiveModalQuery,
face_drag: Res<BrushDragState>,
vertex_drag: Res<VertexDragState>,
edge_drag: Res<EdgeDragState>,
) -> bool {
if input_focus.0.is_some() || active.is_modal_running() {
return false;
}
if face_drag.active || vertex_drag.active || edge_drag.active {
return false;
}
if face_drag.pending.is_some() || vertex_drag.pending.is_some() || edge_drag.pending.is_some() {
return false;
}
true
}
#[operator(
id = "edit_mode.object",
label = "Object Mode",
is_available = can_change_edit_mode
)]
pub fn edit_mode_object(
_: In<OperatorParameters>,
mut edit_mode: ResMut<EditMode>,
mut brush_selection: ResMut<BrushSelection>,
mut draw_state: ResMut<DrawBrushState>,
) -> OperatorResult {
*edit_mode = EditMode::Object;
brush_selection.clear();
draw_state.active = None;
OperatorResult::Finished
}
#[operator(
id = "edit_mode.vertex",
label = "Vertex Mode",
is_available = can_change_edit_mode
)]
pub(crate) fn edit_mode_vertex(
_: In<OperatorParameters>,
edit_mode: ResMut<EditMode>,
brush_selection: ResMut<BrushSelection>,
draw_state: ResMut<DrawBrushState>,
selection: Res<Selection>,
brushes: Query<(), With<jackdaw_jsn::Brush>>,
) -> OperatorResult {
switch_brush_edit_mode(
BrushEditMode::Vertex,
edit_mode,
brush_selection,
draw_state,
selection,
brushes,
)
}
#[operator(
id = "edit_mode.edge",
label = "Edge Mode",
is_available = can_change_edit_mode
)]
pub(crate) fn edit_mode_edge(
_: In<OperatorParameters>,
edit_mode: ResMut<EditMode>,
brush_selection: ResMut<BrushSelection>,
draw_state: ResMut<DrawBrushState>,
selection: Res<Selection>,
brushes: Query<(), With<jackdaw_jsn::Brush>>,
) -> OperatorResult {
switch_brush_edit_mode(
BrushEditMode::Edge,
edit_mode,
brush_selection,
draw_state,
selection,
brushes,
)
}
#[operator(
id = "edit_mode.face",
label = "Face Mode",
is_available = can_change_edit_mode
)]
pub(crate) fn edit_mode_face(
_: In<OperatorParameters>,
edit_mode: ResMut<EditMode>,
brush_selection: ResMut<BrushSelection>,
draw_state: ResMut<DrawBrushState>,
selection: Res<Selection>,
brushes: Query<(), With<jackdaw_jsn::Brush>>,
) -> OperatorResult {
switch_brush_edit_mode(
BrushEditMode::Face,
edit_mode,
brush_selection,
draw_state,
selection,
brushes,
)
}
#[operator(
id = "edit_mode.clip",
label = "Clip Mode",
is_available = can_change_edit_mode
)]
pub(crate) fn edit_mode_clip(
_: In<OperatorParameters>,
edit_mode: ResMut<EditMode>,
brush_selection: ResMut<BrushSelection>,
draw_state: ResMut<DrawBrushState>,
selection: Res<Selection>,
brushes: Query<(), With<jackdaw_jsn::Brush>>,
) -> OperatorResult {
switch_brush_edit_mode(
BrushEditMode::Clip,
edit_mode,
brush_selection,
draw_state,
selection,
brushes,
)
}
fn switch_brush_edit_mode(
target: BrushEditMode,
mut edit_mode: ResMut<EditMode>,
mut brush_selection: ResMut<BrushSelection>,
mut draw_state: ResMut<DrawBrushState>,
selection: Res<Selection>,
brushes: Query<(), With<jackdaw_jsn::Brush>>,
) -> OperatorResult {
draw_state.active = None;
match *edit_mode {
EditMode::BrushEdit(current) if current == target => {
*edit_mode = EditMode::Object;
brush_selection.clear();
}
EditMode::BrushEdit(_) => {
*edit_mode = EditMode::BrushEdit(target);
brush_selection.faces.clear();
brush_selection.vertices.clear();
brush_selection.edges.clear();
}
_ => {
let Some(entity) = selection.primary().filter(|&e| brushes.contains(e)) else {
return OperatorResult::Cancelled;
};
*edit_mode = EditMode::BrushEdit(target);
brush_selection.entity = Some(entity);
brush_selection.faces.clear();
brush_selection.vertices.clear();
brush_selection.edges.clear();
}
}
OperatorResult::Finished
}