From 109fd96b67bc897e0cedccd933f725b2fe06f4a9 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Sun, 1 Mar 2026 17:28:43 +0300 Subject: [PATCH] Started work on mouse callbacks --- src/DrawingProgram/DrawingProgram.cpp | 45 ++++++------ src/DrawingProgram/DrawingProgram.hpp | 7 +- .../DrawingProgramSelection.cpp | 2 +- .../DrawingProgramSelection.hpp | 2 +- src/InputManager.cpp | 68 +++++++++++++++++-- src/InputManager.hpp | 35 +++++++++- src/MainProgram.cpp | 1 + src/Toolbar.cpp | 10 +++ src/Toolbar.hpp | 1 + src/main.cpp | 28 ++------ 10 files changed, 144 insertions(+), 55 deletions(-) diff --git a/src/DrawingProgram/DrawingProgram.cpp b/src/DrawingProgram/DrawingProgram.cpp index 12b3eae..bb984e7 100644 --- a/src/DrawingProgram/DrawingProgram.cpp +++ b/src/DrawingProgram/DrawingProgram.cpp @@ -116,11 +116,30 @@ void DrawingProgram::register_callbacks() { tempMoveToolSwitch = TemporaryMoveToolSwitch::NONE; } }); - selection.register_key_callbacks(); + mouseButtonCallback = world.main.input.mouseButtonCallbacks.register_callback([&](const InputManager::MouseButtonCallbackArgs& button) { + bool isHoveringOverCanvas = world.main.toolbar.check_if_position_isnt_obstructed(button.pos); + if(isHoveringOverCanvas) { + if(button.button == InputManager::MouseButtonCallbackArgs::Button::RIGHT) { + if(button.down) { + if(world.main.toolbar.rightClickPopupLocation) + world.main.toolbar.rightClickPopupLocation = std::nullopt; + else + world.main.toolbar.rightClickPopupLocation = world.main.input.mouse.pos / world.main.toolbar.final_gui_scale(); + } + } + else { + if(button.down) + world.main.toolbar.rightClickPopupLocation = std::nullopt; + mouseButtonOnCanvasCallbacks.run_callbacks(button); + } + } + }); + selection.register_callbacks(); } void DrawingProgram::deregister_callbacks() { tempMoveToolSwitch = TemporaryMoveToolSwitch::NONE; + world.main.input.mouseButtonCallbacks.deregister_callback(mouseButtonCallback); selection.deselect_all(); } @@ -320,21 +339,12 @@ void DrawingProgram::modify_grid(const NetworkingObjects::NetObjWeakPtrhoverObstructingAABBs) - controls.cursorHoveringOverCanvas &= !SCollision::collide(mousePosGUI, aabb); - for(auto& circle : world.main.toolbar.io->hoverObstructingCircles) - controls.cursorHoveringOverCanvas &= !SCollision::collide(mousePosGUI, circle); - } + controls.cursorHoveringOverCanvas = world.main.toolbar.check_if_position_isnt_obstructed(world.main.input.mouse.pos); controls.middleClick = controls.cursorHoveringOverCanvas && (world.main.input.mouse.middleClicks || world.main.input.pen.buttons[world.main.toolbar.tabletOptions.middleClickButton].pressed); bool middleHeld = world.main.input.mouse.middleDown || world.main.input.pen.buttons[world.main.toolbar.tabletOptions.middleClickButton].held; - if(controls.middleClick) { + if(controls.middleClick) controls.middleClickHeld = true; - world.main.toolbar.rightClickPopupLocation = std::nullopt; - } if(controls.middleClickHeld && !middleHeld) { controls.middleClick = false; controls.middleClickHeld = false; @@ -343,23 +353,14 @@ void DrawingProgram::update() { // Left click will be ignored if middle click is held (and left click held state will be gone once middle click is pressed) controls.leftClick = controls.cursorHoveringOverCanvas && world.main.input.mouse.leftClicks && !controls.middleClickHeld; - if(controls.leftClick) { + if(controls.leftClick) controls.leftClickHeld = true; - world.main.toolbar.rightClickPopupLocation = std::nullopt; - } if(controls.leftClickHeld && (!world.main.input.mouse.leftDown || controls.middleClickHeld)) { controls.leftClick = false; controls.leftClickHeld = false; controls.leftClickReleased = true; } - if(controls.cursorHoveringOverCanvas && (world.main.input.mouse.rightClicks || world.main.input.pen.buttons[world.main.toolbar.tabletOptions.rightClickButton].pressed)) { - if(world.main.toolbar.rightClickPopupLocation) - world.main.toolbar.rightClickPopupLocation = std::nullopt; - else - world.main.toolbar.rightClickPopupLocation = world.main.input.mouse.pos / world.main.toolbar.final_gui_scale(); - } - controls.previousMouseWorldPos = controls.currentMouseWorldPos; controls.currentMouseWorldPos = world.get_mouse_world_pos(); diff --git a/src/DrawingProgram/DrawingProgram.hpp b/src/DrawingProgram/DrawingProgram.hpp index bb05b45..a5ee675 100644 --- a/src/DrawingProgram/DrawingProgram.hpp +++ b/src/DrawingProgram/DrawingProgram.hpp @@ -59,7 +59,9 @@ class DrawingProgram { void register_callbacks(); void deregister_callbacks(); - + + typedef CallbackManager MouseButtonOnCanvasCallbackManager; + MouseButtonOnCanvasCallbackManager mouseButtonOnCanvasCallbacks; private: void process_transform_message(const std::vector>& transforms); @@ -68,7 +70,6 @@ class DrawingProgram { void check_updateable_components(); void update_downloading_dropped_files(); - bool selection_action_menu(Vector2f popupPos); void rebuild_cache(); @@ -96,6 +97,8 @@ class DrawingProgram { TemporaryMoveToolSwitch tempMoveToolSwitch = TemporaryMoveToolSwitch::NONE; DrawingProgramToolType toolTypeAfterTempMove; + InputManager::MouseButtonCallbackManager::Callback* mouseButtonCallback; + struct GlobalControls { std::optional lockedCameraScale; diff --git a/src/DrawingProgram/DrawingProgramSelection.cpp b/src/DrawingProgram/DrawingProgramSelection.cpp index 97434de..5a27f26 100644 --- a/src/DrawingProgram/DrawingProgramSelection.cpp +++ b/src/DrawingProgram/DrawingProgramSelection.cpp @@ -442,7 +442,7 @@ void DrawingProgramSelection::update() { } } -void DrawingProgramSelection::register_key_callbacks() { +void DrawingProgramSelection::register_callbacks() { auto& keyCallbacks = drawP.world.keyCallbacks; keyCallbacks[InputManager::KEY_DRAW_DELETE] = drawP.world.main.input.keyCallbacks[InputManager::KEY_DRAW_DELETE].register_callback([&](auto& key) { if(key.down && !key.repeat) diff --git a/src/DrawingProgram/DrawingProgramSelection.hpp b/src/DrawingProgram/DrawingProgramSelection.hpp index 00fc278..62f9b4f 100644 --- a/src/DrawingProgram/DrawingProgramSelection.hpp +++ b/src/DrawingProgram/DrawingProgramSelection.hpp @@ -28,7 +28,7 @@ class DrawingProgramSelection { void delete_all(); void selection_to_clipboard(); CanvasComponentContainer::ObjInfo* get_front_object_colliding_with_in_editing_layer(const SCollision::ColliderCollection& cC); - void register_key_callbacks(); + void register_callbacks(); private: void translate_key(unsigned keyPressed, bool pressed); bool mouse_collided_with_selection_aabb(); diff --git a/src/InputManager.cpp b/src/InputManager.cpp index 09cb093..727ee4e 100644 --- a/src/InputManager.cpp +++ b/src/InputManager.cpp @@ -23,10 +23,12 @@ extern "C" { return isAcceptingInputEmscripten; } } - #endif -InputManager::InputManager() { +#include "MainProgram.hpp" + +InputManager::InputManager(MainProgram& initMain): + main(initMain) { frame_reset({0, 0}); defaultKeyAssignments[{0, SDLK_W}] = KEY_CAMERA_ROTATE_COUNTERCLOCKWISE; @@ -92,7 +94,7 @@ void InputManager::text_input_silence_everything() { } void InputManager::set_rich_text_box_input_front(const std::shared_ptr& nTextBox, const std::shared_ptr& nCursor, bool isRichTextBox, const std::optional& nModMap) { - text.set_accepting_input(sdlWindow, true); + text.set_accepting_input(main.window.sdlWindow, true); Text::TextBoxInfo textBoxInfo; textBoxInfo.textBox = nTextBox; textBoxInfo.cursor = nCursor; @@ -102,7 +104,7 @@ void InputManager::set_rich_text_box_input_front(const std::shared_ptr& nTextBox, const std::shared_ptr& nCursor, bool isRichTextBox, const std::optional& nModMap) { - text.set_accepting_input(sdlWindow, true); + text.set_accepting_input(main.window.sdlWindow, true); Text::TextBoxInfo textBoxInfo; textBoxInfo.textBox = nTextBox; textBoxInfo.cursor = nCursor; @@ -116,7 +118,7 @@ void InputManager::remove_rich_text_box_input(const std::shared_ptr(e.button), + .down = e.down, + .clicks = e.clicks, + .pos = mousePos + }); +} + +void InputManager::backend_mouse_button_down_update(const SDL_MouseButtonEvent& e) { + Vector2f mousePos = {e.x * main.window.density, e.y * main.window.density}; + if(e.button == 1) { + mouse.leftDown = true; + mouse.leftClicks = e.clicks; + } + else if(e.button == 2) { + mouse.middleDown = true; + mouse.middleClicks = e.clicks; + } + else if(e.button == 3) { + mouse.rightDown = true; + mouse.rightClicks = e.clicks; + } + mouseButtonCallbacks.run_callbacks({ + .button = static_cast(e.button), + .down = e.down, + .clicks = e.clicks, + .pos = mousePos + }); +} + +void InputManager::backend_mouse_motion_update(const SDL_MouseMotionEvent& e) { + Vector2f mouseNewPos = {e.x * main.window.density, e.y * main.window.density}; + Vector2f mouseRel = {e.xrel * main.window.density, e.yrel * main.window.density}; + mouse.set_pos(mouseNewPos); + mouseMotionCallbacks.run_callbacks({ + .pos = mouseNewPos, + .move = mouseRel + }); +} + +void InputManager::backend_mouse_wheel_update(const SDL_MouseWheelEvent& e) { + mouse.scrollAmount.x() += e.x; + mouse.scrollAmount.y() += e.y; + mouseWheelCallbacks.run_callbacks({ + .amount = {e.x, e.y}, + }); +} + void InputManager::backend_key_down_update(const SDL_KeyboardEvent& e) { auto kPress = e.key; auto kMod = e.mod; diff --git a/src/InputManager.hpp b/src/InputManager.hpp index f3668b9..2491c09 100644 --- a/src/InputManager.hpp +++ b/src/InputManager.hpp @@ -16,9 +16,10 @@ #include using namespace Eigen; +class MainProgram; struct InputManager { - InputManager(); + InputManager(MainProgram& initMain); void frame_reset(const Vector2i& windowSize); @@ -213,6 +214,10 @@ struct InputManager { void set_key_up(const SDL_KeyboardEvent& e, KeyCode kCode); void set_key_down(const SDL_KeyboardEvent& e, KeyCode kCode); + void backend_mouse_button_up_update(const SDL_MouseButtonEvent& e); + void backend_mouse_button_down_update(const SDL_MouseButtonEvent& e); + void backend_mouse_motion_update(const SDL_MouseMotionEvent& e); + void backend_mouse_wheel_update(const SDL_MouseWheelEvent& e); void backend_key_up_update(const SDL_KeyboardEvent& e); void backend_key_down_update(const SDL_KeyboardEvent& e); @@ -243,7 +248,33 @@ struct InputManager { std::unordered_map defaultKeyAssignments; std::array keys; - SDL_Window* sdlWindow; + struct MouseButtonCallbackArgs { + enum class Button : uint8_t { + LEFT = 1, + MIDDLE, + RIGHT + } button; + bool down; + uint8_t clicks; + Vector2f pos; + }; + typedef CallbackManager MouseButtonCallbackManager; + MouseButtonCallbackManager mouseButtonCallbacks; + + struct MouseMotionCallbackArgs { + Vector2f pos; + Vector2f move; + }; + typedef CallbackManager MouseMotionCallbackManager; + MouseMotionCallbackManager mouseMotionCallbacks; + + struct MouseWheelCallbackArgs { + Vector2f amount; + }; + typedef CallbackManager MouseWheelCallbackManager; + MouseWheelCallbackManager mouseWheelCallbacks; + + MainProgram& main; }; NLOHMANN_JSON_SERIALIZE_ENUM(InputManager::KeyCodeEnum, { diff --git a/src/MainProgram.cpp b/src/MainProgram.cpp index 5a2776a..d58d630 100755 --- a/src/MainProgram.cpp +++ b/src/MainProgram.cpp @@ -36,6 +36,7 @@ #include "DrawCollision.hpp" MainProgram::MainProgram(): + input(*this), fonts(std::make_shared()), toolbar(*this) { diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index f5eb016..eb0a9af 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -2305,6 +2305,16 @@ float Toolbar::final_gui_scale() { return finalCalculatedGuiScale; } +bool Toolbar::check_if_position_isnt_obstructed(const Vector2f& pos) { + bool positionIsntObstructed = true; + Vector2f posGUI = pos / final_gui_scale(); + for(auto& aabb : io->hoverObstructingAABBs) + positionIsntObstructed &= !SCollision::collide(posGUI, aabb); + for(auto& circle : io->hoverObstructingCircles) + positionIsntObstructed &= !SCollision::collide(posGUI, circle); + return positionIsntObstructed; +} + void Toolbar::calculate_final_gui_scale() { Vector2f maxWindowSizeBeforeForcedFit = final_gui_scale_not_fit() * Vector2f{700.0f, 700.0f}; Vector2f fitRatio = {main.window.size.x() / maxWindowSizeBeforeForcedFit.x(), main.window.size.y() / maxWindowSizeBeforeForcedFit.y()}; diff --git a/src/Toolbar.hpp b/src/Toolbar.hpp index 1cbc22d..805523a 100644 --- a/src/Toolbar.hpp +++ b/src/Toolbar.hpp @@ -90,6 +90,7 @@ class Toolbar { bool flipZoomToolDirection = false; float final_gui_scale(); + bool check_if_position_isnt_obstructed(const Vector2f& pos); bool app_close_requested(); private: diff --git a/src/main.cpp b/src/main.cpp index d9dde5c..a8f15a1 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -464,7 +464,6 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { mS.m->documentsPath = std::filesystem::path(documentsPathSDL); #endif mS.m->window.sdlWindow = mS.window; - mS.m->input.sdlWindow = mS.window; mS.m->update_scale_and_density(); #ifdef __EMSCRIPTEN__ emscripten_set_beforeunload_callback((void*)mSPtr, emscripten_before_unload); @@ -737,34 +736,19 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { if(!mS.m->toolbar.tabletOptions.ignoreMouseMovementWhenPenInProximity || !mS.m->input.pen.inProximity) #endif { - mS.m->input.mouse.set_pos({event->motion.x * mS.m->window.density, event->motion.y * mS.m->window.density}); + mS.m->input.backend_mouse_motion_update(event->motion); } break; case SDL_EVENT_MOUSE_BUTTON_UP: - if(event->button.button == 1) - mS.m->input.mouse.leftDown = false; - else if(event->button.button == 2) - mS.m->input.mouse.middleDown = false; - else if(event->button.button == 3) - mS.m->input.mouse.rightDown = false; + mS.m->update_scale_and_density(); + mS.m->input.backend_mouse_button_up_update(event->button); break; case SDL_EVENT_MOUSE_BUTTON_DOWN: - if(event->button.button == 1) { - mS.m->input.mouse.leftDown = true; - mS.m->input.mouse.leftClicks = event->button.clicks; - } - else if(event->button.button == 2) { - mS.m->input.mouse.middleDown = true; - mS.m->input.mouse.middleClicks = event->button.clicks; - } - else if(event->button.button == 3) { - mS.m->input.mouse.rightDown = true; - mS.m->input.mouse.rightClicks = event->button.clicks; - } + mS.m->update_scale_and_density(); + mS.m->input.backend_mouse_button_down_update(event->button); break; case SDL_EVENT_MOUSE_WHEEL: - mS.m->input.mouse.scrollAmount.x() += event->wheel.x; - mS.m->input.mouse.scrollAmount.y() += event->wheel.y; + mS.m->input.backend_mouse_wheel_update(event->wheel); break; case SDL_EVENT_KEY_DOWN: mS.m->input.backend_key_down_update(event->key);