Can now add and remove from selection using shift and alt

This commit is contained in:
Yousef Khadadeh 2025-08-05 19:04:47 +03:00
parent 137b1603e5
commit 1a5815a994
12 changed files with 103 additions and 61 deletions

View file

@ -114,7 +114,7 @@ void DrawCamera::update_main(World& w) {
}
else {
bool middleClickDown = w.main.input.mouse.middleDown || w.main.input.pen.buttons[w.main.toolbar.tabletOptions.middleClickButton].held;
bool newIsMiddleClickZooming = middleClickDown && w.main.input.key(InputManager::KEY_ZOOM_CTRL).held;
bool newIsMiddleClickZooming = middleClickDown && w.main.input.key(InputManager::KEY_GENERIC_LCTRL).held;
if(newIsMiddleClickZooming && !isMiddleClickZooming) {
startZoomMousePos = w.get_mouse_world_pos();
startZoomVal = c.inverseScale;

View file

@ -285,6 +285,7 @@ void DrawingProgram::update() {
else if(world.main.input.key(InputManager::KEY_DRAW_TOOL_EDIT).pressed)
switch_to_tool(DrawingProgramToolType::EDIT);
selection.update();
drawTool->tool_update();
if(controls.leftClickReleased)
@ -293,8 +294,6 @@ void DrawingProgram::update() {
check_delayed_update_timers();
check_updateable_components();
selection.update();
if(drawTool->get_type() == DrawingProgramToolType::ERASER) {
EraserTool* eraserTool = static_cast<EraserTool*>(drawTool.get());
compCache.test_rebuild_dont_include_set_dont_include_nodes(components.client_list(), eraserTool->erasedComponents, eraserTool->erasedBVHNodes);
@ -312,7 +311,7 @@ void DrawingProgram::switch_to_tool_ptr(std::unique_ptr<DrawingProgramToolBase>
void DrawingProgram::switch_to_tool(DrawingProgramToolType newToolType) {
if(newToolType != drawTool->get_type()) {
drawTool->switch_tool(drawTool->get_type());
drawTool->switch_tool(newToolType);
drawTool = DrawingProgramToolBase::allocate_tool_type(*this, newToolType);
}
}

View file

@ -35,11 +35,7 @@ void DrawingProgramSelection::erase_component(const CollabListType::ObjectInfoPt
}
}
void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC) {
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
std::unordered_set<CollabListType::ObjectInfoPtr> selectedComponents;
std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&, std::vector<CollabListType::ObjectInfoPtr>&)> DrawingProgramSelection::erase_select_objects_in_bvh_func(std::unordered_set<CollabListType::ObjectInfoPtr>& selectedComponents, const SCollision::ColliderCollection<float>& cC, const SCollision::ColliderCollection<WorldScalar>& cCWorld) {
std::function<void(const std::shared_ptr<DrawingProgramCacheBVHNode>&)> fullyCollidedFunc = [&](const std::shared_ptr<DrawingProgramCacheBVHNode>& bvhNode) {
if(bvhNode) {
for(auto& c : bvhNode->components)
@ -48,7 +44,8 @@ void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCo
fullyCollidedFunc(p);
}
};
drawP.compCache.traverse_bvh_erase_function(cCWorld.bounds, [&](const auto& bvhNode, auto& comps) {
auto toRet = [&](const auto& bvhNode, auto& comps) {
if(bvhNode && (bvhNode->coords.inverseScale << 9) < drawP.world.drawData.cam.c.inverseScale &&
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.min)) &&
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.max)) &&
@ -67,9 +64,43 @@ void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCo
return false;
});
return false;
});
};
set_to_selection(selectedComponents);
return toRet;
}
void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC) {
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
std::unordered_set<CollabListType::ObjectInfoPtr> selectedComponents;
drawP.compCache.traverse_bvh_erase_function(cCWorld.bounds, erase_select_objects_in_bvh_func(selectedComponents, cC, cCWorld));
add_to_selection(selectedComponents);
}
void DrawingProgramSelection::remove_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC) {
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
std::unordered_set<CollabListType::ObjectInfoPtr> selectedComponents;
cache.traverse_bvh_erase_function(cCWorld.bounds, erase_select_objects_in_bvh_func(selectedComponents, cC, cCWorld));
std::erase_if(selectedSet, [&](auto& c) {
return selectedComponents.contains(c);
});
for(auto& obj : selectedComponents)
drawP.compCache.add_component(obj);
if(!is_something_selected()) {
reset_selection_data();
reset_transform_data();
}
else {
calculate_aabb();
calculate_initial_rotate_center_location();
}
}
void DrawingProgramSelection::add_to_selection(const std::unordered_set<CollabListType::ObjectInfoPtr>& newSelection) {
selectedSet.insert(newSelection.begin(), newSelection.end());
for(auto& obj : newSelection)
cache.add_component(obj);
calculate_aabb();
calculate_initial_rotate_center_location();
}
void DrawingProgramSelection::set_to_selection(const std::unordered_set<CollabListType::ObjectInfoPtr>& newSelection) {
@ -235,7 +266,7 @@ void DrawingProgramSelection::update() {
switch(transformOpHappening) {
case TransformOperation::NONE:
if(drawP.controls.leftClick) {
if(drawP.controls.leftClick && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held && !drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held) {
if(mouse_collided_with_scale_point()) {
scaleData.currentPos = scaleData.startPos = selectionTransformCoords.from_space_world(initialSelectionAABB.max);
scaleData.centerPos = selectionTransformCoords.from_space_world(initialSelectionAABB.center());
@ -251,8 +282,6 @@ void DrawingProgramSelection::update() {
translateData.startPos = drawP.world.get_mouse_world_pos();
transformOpHappening = TransformOperation::TRANSLATE;
}
else
deselect_all();
}
break;
case TransformOperation::TRANSLATE:
@ -325,6 +354,10 @@ void DrawingProgramSelection::update() {
paste_clipboard();
}
bool DrawingProgramSelection::is_being_transformed() {
return transformOpHappening != TransformOperation::NONE;
}
void DrawingProgramSelection::paste_clipboard() {
auto& clipboard = drawP.world.main.clipboard;

View file

@ -8,6 +8,8 @@ class DrawingProgramSelection {
public:
DrawingProgramSelection(DrawingProgram& initDrawP);
void add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC);
void remove_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC);
void deselect_all();
void update();
void draw_components(SkCanvas* canvas, const DrawData& drawData);
@ -18,9 +20,13 @@ class DrawingProgramSelection {
bool is_selected(const CollabListType::ObjectInfoPtr& objToCheck);
const std::unordered_set<CollabListType::ObjectInfoPtr>& get_selected_set();
bool is_being_transformed();
void invalidate_cache_at_optional_aabb_before_pos(const std::optional<SCollision::AABB<WorldScalar>>& aabb, uint64_t placementToInvalidateAt);
void preupdate_component(const CollabListType::ObjectInfoPtr& objToCheck);
private:
std::function<bool(const std::shared_ptr<DrawingProgramCacheBVHNode>&, std::vector<CollabListType::ObjectInfoPtr>&)> erase_select_objects_in_bvh_func(std::unordered_set<CollabListType::ObjectInfoPtr>& selectedComponents, const SCollision::ColliderCollection<float>& cC, const SCollision::ColliderCollection<WorldScalar>& cCWorld);
void delete_all();
bool mouse_collided_with_selection_aabb();
bool mouse_collided_with_scale_point();
@ -28,7 +34,10 @@ class DrawingProgramSelection {
bool mouse_collided_with_rotate_handle_point();
void selection_to_clipboard();
void paste_clipboard();
void set_to_selection(const std::unordered_set<CollabListType::ObjectInfoPtr>& newSelection);
void add_to_selection(const std::unordered_set<CollabListType::ObjectInfoPtr>& newSelection);
void add_erased_objects_back_to_cache(const std::unordered_set<CollabListType::ObjectInfoPtr>& erasedObjects);
void commit_transform_selection();

View file

@ -62,7 +62,7 @@ void EllipseDrawTool::tool_update() {
if(drawP.controls.leftClickHeld) {
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
Vector2f newPos = controls.intermediateItem->coords.get_mouse_pos(drawP.world);
if(drawP.world.main.input.key(InputManager::KEY_EQUAL_DIMENSIONS).held) {
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) {
float height = std::fabs(controls.startAt.y() - newPos.y());
newPos.x() = controls.startAt.x() + (((newPos.x() - controls.startAt.x()) < 0.0f ? -1.0f : 1.0f) * height);
}

View file

@ -42,25 +42,18 @@ void LassoSelectTool::gui_toolbox() {
}
void LassoSelectTool::switch_tool(DrawingProgramToolType newTool) {
drawP.selection.deselect_all();
controls.lassoPoints.clear();
controls = LassoSelectControls();
if(newTool != DrawingProgramToolType::RECTSELECT && newTool != DrawingProgramToolType::LASSOSELECT)
drawP.selection.deselect_all();
}
void LassoSelectTool::tool_update() {
switch(controls.selectionMode) {
case 0: {
if(drawP.controls.leftClick) {
if(drawP.selection.is_something_selected()) {
if(!drawP.selection.mouse_collided_with_selection())
switch_tool(get_type());
}
else {
switch_tool(get_type());
controls.coords = drawP.world.drawData.cam.c;
controls.lassoPoints.emplace_back(controls.coords.get_mouse_pos(drawP.world));
controls.selectionMode = 1;
}
if(drawP.controls.leftClick && !drawP.selection.is_being_transformed()) {
controls = LassoSelectControls();
controls.coords = drawP.world.drawData.cam.c;
controls.lassoPoints.emplace_back(controls.coords.get_mouse_pos(drawP.world));
controls.selectionMode = 1;
}
break;
}
@ -91,12 +84,16 @@ void LassoSelectTool::tool_update() {
cC.recalculate_bounds();
drawP.selection.add_from_cam_coord_collider_to_selection(cC);
controls.selectionMode = 0;
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held)
drawP.selection.add_from_cam_coord_collider_to_selection(cC);
else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held)
drawP.selection.remove_from_cam_coord_collider_to_selection(cC);
else {
drawP.selection.deselect_all();
drawP.selection.add_from_cam_coord_collider_to_selection(cC);
}
}
else
switch_tool(get_type());
controls.selectionMode = 0;
}
break;
}

View file

@ -59,7 +59,7 @@ void RectDrawTool::tool_update() {
if(drawP.controls.leftClickHeld) {
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
Vector2f newPos = controls.intermediateItem->coords.get_mouse_pos(drawP.world);
if(drawP.world.main.input.key(InputManager::KEY_EQUAL_DIMENSIONS).held) {
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) {
float height = std::fabs(controls.startAt.y() - newPos.y());
newPos.x() = controls.startAt.x() + (((newPos.x() - controls.startAt.x()) < 0.0f ? -1.0f : 1.0f) * height);
}

View file

@ -21,24 +21,18 @@ void RectSelectTool::gui_toolbox() {
}
void RectSelectTool::switch_tool(DrawingProgramToolType newTool) {
drawP.selection.deselect_all();
controls = RectSelectControls();
if(newTool != DrawingProgramToolType::RECTSELECT && newTool != DrawingProgramToolType::LASSOSELECT)
drawP.selection.deselect_all();
}
void RectSelectTool::tool_update() {
switch(controls.selectionMode) {
case 0: {
if(drawP.controls.leftClick) {
if(drawP.selection.is_something_selected()) {
if(!drawP.selection.mouse_collided_with_selection())
switch_tool(get_type());
}
else {
switch_tool(get_type());
controls.coords = drawP.world.drawData.cam.c;
controls.selectStartAt = controls.coords.get_mouse_pos(drawP.world);
controls.selectionMode = 1;
}
if(drawP.controls.leftClick && !drawP.selection.is_being_transformed()) {
controls = RectSelectControls();
controls.coords = drawP.world.drawData.cam.c;
controls.selectStartAt = controls.coords.get_mouse_pos(drawP.world);
controls.selectionMode = 1;
}
break;
}
@ -54,7 +48,14 @@ void RectSelectTool::tool_update() {
cC.triangle.emplace_back(controls.newT[2], controls.newT[3], controls.newT[0]);
cC.recalculate_bounds();
drawP.selection.add_from_cam_coord_collider_to_selection(cC);
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held)
drawP.selection.add_from_cam_coord_collider_to_selection(cC);
else if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LALT).held)
drawP.selection.remove_from_cam_coord_collider_to_selection(cC);
else {
drawP.selection.deselect_all();
drawP.selection.add_from_cam_coord_collider_to_selection(cC);
}
controls.selectionMode = 0;
}

View file

@ -15,7 +15,6 @@ class RectSelectTool : public DrawingProgramToolBase {
virtual void draw(SkCanvas* canvas, const DrawData& drawData) override;
virtual void switch_tool(DrawingProgramToolType newTool) override;
virtual bool prevent_undo_or_redo() override;
private:
struct RectSelectControls {
int selectionMode = 0;

View file

@ -54,7 +54,7 @@ void TextBoxTool::tool_update() {
}
case 1: {
Vector2f newPos = controls.intermediateItem->coords.get_mouse_pos(drawP.world);
if(drawP.world.main.input.key(InputManager::KEY_EQUAL_DIMENSIONS).held) {
if(drawP.world.main.input.key(InputManager::KEY_GENERIC_LSHIFT).held) {
float height = std::fabs(controls.startAt.y() - newPos.y());
newPos.x() = controls.startAt.x() + (((newPos.x() - controls.startAt.x()) < 0.0f ? -1.0f : 1.0f) * height);
}

View file

@ -227,13 +227,15 @@ void InputManager::backend_key_down_update(const SDL_KeyboardEvent& e) {
set_key_down(e, KEY_TEXT_HOME);
break;
case SDLK_LSHIFT:
set_key_down(e, KEY_ZOOM_SHIFT);
set_key_down(e, KEY_TEXT_SHIFT);
set_key_down(e, KEY_EQUAL_DIMENSIONS);
set_key_down(e, KEY_GENERIC_LSHIFT);
break;
case SDLK_LALT:
set_key_down(e, KEY_GENERIC_LALT);
break;
case SDLK_LCTRL:
set_key_down(e, KEY_TEXT_CTRL);
set_key_down(e, KEY_ZOOM_CTRL);
set_key_down(e, KEY_GENERIC_LCTRL);
break;
case SDLK_C:
// Use either Ctrl or Meta (command for Mac) keys. We do either instead of checking, since checking can get complicated on Emscripten
@ -297,13 +299,15 @@ void InputManager::backend_key_up_update(const SDL_KeyboardEvent& e) {
set_key_up(e, KEY_TEXT_HOME);
break;
case SDLK_LSHIFT:
set_key_up(e, KEY_ZOOM_SHIFT);
set_key_up(e, KEY_TEXT_SHIFT);
set_key_up(e, KEY_EQUAL_DIMENSIONS);
set_key_up(e, KEY_GENERIC_LSHIFT);
break;
case SDLK_LALT:
set_key_up(e, KEY_GENERIC_LALT);
break;
case SDLK_LCTRL:
set_key_up(e, KEY_TEXT_CTRL);
set_key_up(e, KEY_ZOOM_CTRL);
set_key_up(e, KEY_GENERIC_LCTRL);
break;
case SDLK_C:
set_key_up(e, KEY_TEXT_COPY);

View file

@ -154,9 +154,9 @@ struct InputManager {
KEY_TEXT_CTRL,
KEY_TEXT_SELECTALL,
KEY_TEXT_ESCAPE,
KEY_ZOOM_SHIFT,
KEY_ZOOM_CTRL,
KEY_EQUAL_DIMENSIONS,
KEY_GENERIC_LSHIFT,
KEY_GENERIC_LALT,
KEY_GENERIC_LCTRL,
KEY_COUNT
};