diff --git a/src/GUIStuff/Elements/CheckBox.cpp b/src/GUIStuff/Elements/CheckBox.cpp index 565d959..d7f303e 100644 --- a/src/GUIStuff/Elements/CheckBox.cpp +++ b/src/GUIStuff/Elements/CheckBox.cpp @@ -11,7 +11,7 @@ void CheckBox::update(UpdateInputData& io, bool newIsTicked, const std::function }, .custom = { .customData = this } }) { - selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld); + selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld, io.mouse.pos); if(isTicked != newIsTicked) { hoverAnimation2 = selection.hovered ? CHECKBOX_ANIMATION_TIME : 0.0f; isTicked = newIsTicked; diff --git a/src/GUIStuff/Elements/ColorPicker.hpp b/src/GUIStuff/Elements/ColorPicker.hpp index 5655c02..d6566bd 100644 --- a/src/GUIStuff/Elements/ColorPicker.hpp +++ b/src/GUIStuff/Elements/ColorPicker.hpp @@ -29,7 +29,7 @@ template class ColorPicker : public Element { .aspectRatio = {1.0f}, .custom = { .customData = this } }) { - selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld); + selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld, io.mouse.pos); if(selection.clicked && data) { float svSelectionAreaSize = get_sv_selection_area_size(); modifyingSv = SCollision::collide(SCollision::AABB(bb.min, bb.min + Vector2f{svSelectionAreaSize, svSelectionAreaSize}), io.mouse.pos); diff --git a/src/GUIStuff/Elements/Element.cpp b/src/GUIStuff/Elements/Element.cpp index 061e31d..d415702 100644 --- a/src/GUIStuff/Elements/Element.cpp +++ b/src/GUIStuff/Elements/Element.cpp @@ -1,4 +1,6 @@ #include "Element.hpp" +#include "Helpers/MathExtras.hpp" +#include namespace GUIStuff { SkFont get_setup_skfont() { @@ -35,24 +37,31 @@ namespace GUIStuff { return {{command->boundingBox.x, command->boundingBox.y}, {command->boundingBox.x + command->boundingBox.width, command->boundingBox.y + command->boundingBox.height}}; } - void SelectionHelper::update(bool isHovering, bool isLeftClick, bool isLeftHeld) { + void SelectionHelper::update(bool isHovering, bool isLeftClick, bool isLeftHeld, const Vector2f& cursorPos) { hovered = isHovering; clicked = false; + tapped = false; justUnselected = false; bool oldSelected = selected; if(isLeftClick) { if(hovered) { - selected = true; - clicked = true; held = true; + clicked = true; + timeClicked = std::chrono::steady_clock::now(); + clickPos = cursorPos; } else selected = false; } - else if(!isLeftHeld) + else if(!isLeftHeld) { + if(held && hovered && (std::chrono::steady_clock::now() - timeClicked) < std::chrono::milliseconds(250) && vec_distance_sqrd(clickPos, cursorPos) < (25 * 25)) { + selected = true; + tapped = true; + } held = false; + } if(!selected && oldSelected) justUnselected = true; diff --git a/src/GUIStuff/Elements/Element.hpp b/src/GUIStuff/Elements/Element.hpp index e6fc8e8..77ac80e 100644 --- a/src/GUIStuff/Elements/Element.hpp +++ b/src/GUIStuff/Elements/Element.hpp @@ -119,12 +119,15 @@ struct UpdateInputData { class SelectionHelper { public: - void update(bool isHovering, bool isLeftClick, bool isLeftHeld); + void update(bool isHovering, bool isLeftClick, bool isLeftHeld, const Vector2f& cursorPos); bool held = false; bool hovered = false; bool selected = false; bool clicked = false; bool justUnselected = false; + bool tapped = false; + Vector2f clickPos; + std::chrono::steady_clock::time_point timeClicked; }; class Element { diff --git a/src/GUIStuff/Elements/MovableTabList.cpp b/src/GUIStuff/Elements/MovableTabList.cpp index 75c7080..7305059 100644 --- a/src/GUIStuff/Elements/MovableTabList.cpp +++ b/src/GUIStuff/Elements/MovableTabList.cpp @@ -24,7 +24,7 @@ void MovableTabList::update(UpdateInputData& io, const std::vector class NumberSlider : public Element { }, .custom = { .customData = this } }) { - selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld); + selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld, io.mouse.pos); if(selection.held && data) { float fracPosOnSlider = (io.mouse.pos.x() - bb.min.x()) / bb.width(); *data = static_cast(std::clamp(std::lerp(min, max, fracPosOnSlider), min, max)); // Clamp as double then cast so that unsigned types dont wrap on clamp diff --git a/src/GUIStuff/Elements/PaintCircleMenu.cpp b/src/GUIStuff/Elements/PaintCircleMenu.cpp index 65b8d9b..1c07005 100644 --- a/src/GUIStuff/Elements/PaintCircleMenu.cpp +++ b/src/GUIStuff/Elements/PaintCircleMenu.cpp @@ -22,7 +22,7 @@ void PaintCircleMenu::update(GUIManager& gui, const Data& data, const std::funct Vector2f vecFromCenter = (io.mouse.pos - bb.center()).normalized(); float distFromCenter = vec_distance(io.mouse.pos, bb.center()); if(data.newRotationAngle) { - rotateBarSelect.update(isHoveredNoObstruction && distFromCenter > ROTATE_START && distFromCenter < CIRCLE_END, io.mouse.leftClick, io.mouse.leftHeld); + rotateBarSelect.update(isHoveredNoObstruction && distFromCenter > ROTATE_START && distFromCenter < CIRCLE_END, io.mouse.leftClick, io.mouse.leftHeld, io.mouse.pos); if(rotateBarSelect.held) { double& newRotationAngle = *data.newRotationAngle; newRotationAngle = std::atan2(vecFromCenter.y(), -vecFromCenter.x()) + std::numbers::pi; @@ -41,7 +41,7 @@ void PaintCircleMenu::update(GUIManager& gui, const Data& data, const std::funct *data.newRotationAngle = data.currentRotationAngle; } if(!data.palette.empty() && data.selectedColor) { - colorBarSelect.update(isHoveredNoObstruction && distFromCenter > PALETTE_START && distFromCenter < ROTATE_START, io.mouse.leftClick, io.mouse.leftHeld); + colorBarSelect.update(isHoveredNoObstruction && distFromCenter > PALETTE_START && distFromCenter < ROTATE_START, io.mouse.leftClick, io.mouse.leftHeld, io.mouse.pos); if(colorBarSelect.hovered) { Vector4f& selectedColor = *data.selectedColor; double selectionAngle = std::atan2(-vecFromCenter.y(), -vecFromCenter.x()) + std::numbers::pi; diff --git a/src/GUIStuff/Elements/RadioButton.cpp b/src/GUIStuff/Elements/RadioButton.cpp index b1a0262..25e79fc 100644 --- a/src/GUIStuff/Elements/RadioButton.cpp +++ b/src/GUIStuff/Elements/RadioButton.cpp @@ -9,7 +9,7 @@ void RadioButton::update(UpdateInputData& io, bool newIsTicked, const std::funct }, .custom = { .customData = this } }) { - selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld); + selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld, io.mouse.pos); if(isTicked != newIsTicked) { hoverAnimation2 = RADIOBUTTON_ANIMATION_TIME; isTicked = newIsTicked; diff --git a/src/GUIStuff/Elements/RotateWheel.cpp b/src/GUIStuff/Elements/RotateWheel.cpp index 95ea4ed..cef9138 100644 --- a/src/GUIStuff/Elements/RotateWheel.cpp +++ b/src/GUIStuff/Elements/RotateWheel.cpp @@ -18,7 +18,7 @@ void RotateWheel::update(UpdateInputData& io, double* newRotationAnglePtr, const }, .custom = { .customData = this } }) { - selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld); + selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld, io.mouse.pos); Vector2f vecFromCenter = (io.mouse.pos - bb.center()).normalized(); float distFromCenter = vec_distance(io.mouse.pos, bb.center()); wheelHovered = selection.hovered && distFromCenter > wheel_start() && distFromCenter < wheel_end(); diff --git a/src/GUIStuff/Elements/SelectableButton.cpp b/src/GUIStuff/Elements/SelectableButton.cpp index c7f4687..b29e6f0 100644 --- a/src/GUIStuff/Elements/SelectableButton.cpp +++ b/src/GUIStuff/Elements/SelectableButton.cpp @@ -49,7 +49,7 @@ void SelectableButton::update(UpdateInputData& io, DrawType drawType, const std: .backgroundColor = convert_vec4(backgroundColorHighlight), .cornerRadius = CLAY_CORNER_RADIUS(4) }) { - selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld); + selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld, io.mouse.pos); elemUpdate(selection, isSelected); } } diff --git a/src/GUIStuff/Elements/TextBox.hpp b/src/GUIStuff/Elements/TextBox.hpp index 0563868..a9d8635 100644 --- a/src/GUIStuff/Elements/TextBox.hpp +++ b/src/GUIStuff/Elements/TextBox.hpp @@ -29,7 +29,7 @@ template class TextBox : public Element { }, .custom = { .customData = this } }) { - selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld); + selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld, io.mouse.pos); if(data && selection.selected) { io.richTextBoxToEdit = textbox; io.richTextBoxToEditCursor = cur; diff --git a/src/GUIStuff/GUIManager.cpp b/src/GUIStuff/GUIManager.cpp index 790449e..b1afdbd 100644 --- a/src/GUIStuff/GUIManager.cpp +++ b/src/GUIStuff/GUIManager.cpp @@ -334,14 +334,14 @@ bool GUIManager::radio_button(const char* id, bool val, const std::function(); e->update(*io, val, elemUpdate); pop_id(); - return e->selection.clicked; + return e->selection.tapped; } void GUIManager::checkbox(const char* id, bool* val, const std::function& elemUpdate) { push_id(id); CheckBox* e = insert_element(); e->update(*io, *val, elemUpdate); - if(e->selection.clicked) + if(e->selection.tapped) *val = !(*val); pop_id(); } @@ -411,11 +411,11 @@ bool GUIManager::input_text(const char* id, std::string* val, bool updateEveryEd return toRet; } -bool GUIManager::selectable_button(const char* id, const std::function& elemUpdate, GUIStuff::SelectableButton::DrawType drawType, bool isSelected) { +bool GUIManager::selectable_button(const char* id, const std::function& elemUpdate, GUIStuff::SelectableButton::DrawType drawType, bool isSelected, bool onClick) { push_id(id); SelectableButton* e = insert_element(); e->update(*io, drawType, elemUpdate, isSelected); - bool toRet = e->selection.clicked; + bool toRet = onClick ? e->selection.clicked : e->selection.tapped; pop_id(); return toRet; } @@ -440,7 +440,7 @@ bool GUIManager::svg_icon_button(const char* id, const std::string& svgPath, boo return toRet; } -bool GUIManager::svg_icon_button_transparent(const char* id, const std::string& svgPath, bool isSelected, float size, bool hasBorder, const std::function& elemUpdate) { +bool GUIManager::svg_icon_button_transparent(const char* id, const std::string& svgPath, bool isSelected, float size, bool hasBorder, bool onClick, const std::function& elemUpdate) { bool toRet = false; push_id(id); CLAY_AUTO_ID({.layout = {.sizing = {.width = CLAY_SIZING_FIXED(size), .height = CLAY_SIZING_FIXED(size) } } }) { @@ -448,7 +448,7 @@ bool GUIManager::svg_icon_button_transparent(const char* id, const std::string& svg_icon("1", svgPath, is || s.held || s.hovered); if(elemUpdate) elemUpdate(); - }, SelectableButton::DrawType::TRANSPARENT_ALL, isSelected); + }, SelectableButton::DrawType::TRANSPARENT_ALL, isSelected, onClick); } pop_id(); return toRet; @@ -466,14 +466,14 @@ bool GUIManager::text_button_sized(const char* id, std::string_view text, Clay_S return toRet; } -bool GUIManager::text_button(const char* id, std::string_view text, bool isSelected, const std::function& elemUpdate) { +bool GUIManager::text_button(const char* id, std::string_view text, bool isSelected, bool onClick, const std::function& elemUpdate) { bool toRet = false; CLAY_AUTO_ID({.layout = {.sizing = {.width = CLAY_SIZING_FIT(0), .height = CLAY_SIZING_FIT(0) } } }) { toRet = selectable_button(id, [&](SelectionHelper& s, bool iS) { text_label(text); if(elemUpdate) elemUpdate(); - }, SelectableButton::DrawType::FILLED, isSelected); + }, SelectableButton::DrawType::FILLED, isSelected, onClick); } return toRet; } @@ -824,7 +824,7 @@ bool GUIManager::rotate_wheel(const char* id, double* angle, float size, const s CLAY_AUTO_ID({.layout = {.sizing = {.width = CLAY_SIZING_FIXED(size), .height = CLAY_SIZING_FIXED(size) } } }) { RotateWheel* e = insert_element(); e->update(*io, angle, elemUpdate); - toRet = e->selection.clicked; + toRet = e->selection.tapped; } pop_id(); return toRet; diff --git a/src/GUIStuff/GUIManager.hpp b/src/GUIStuff/GUIManager.hpp index 4beb21e..8fd82f2 100644 --- a/src/GUIStuff/GUIManager.hpp +++ b/src/GUIStuff/GUIManager.hpp @@ -58,14 +58,14 @@ class GUIManager { void text_label_light_centered(std::string_view val); void text_label_centered(std::string_view val); bool text_button_left_transparent(const char* id, std::string_view text, bool isSelected = false, const std::function& elemUpdate = nullptr); - bool text_button(const char* id, std::string_view text, bool isSelected = false, const std::function& elemUpdate = nullptr); + bool text_button(const char* id, std::string_view text, bool isSelected = false, bool onClick = false, const std::function& elemUpdate = nullptr); bool text_button_wide(const char* id, std::string_view text, bool isSelected = false, const std::function& elemUpdate = nullptr); bool text_button_sized(const char* id, std::string_view text, Clay_SizingAxis x, Clay_SizingAxis y, bool isSelected = false, const std::function& elemUpdate = nullptr); void svg_icon(const char* id, const std::string& svgPath, bool isHighlighted = false, const std::function& elemUpdate = nullptr); bool svg_icon_button(const char* id, const std::string& svgPath, bool isSelected = false, float size = BIG_BUTTON_SIZE, bool hasBorder = true, const std::function& elemUpdate = nullptr); - bool svg_icon_button_transparent(const char* id, const std::string& svgPath, bool isSelected = false, float size = BIG_BUTTON_SIZE, bool hasBorder = true, const std::function& elemUpdate = nullptr); + bool svg_icon_button_transparent(const char* id, const std::string& svgPath, bool isSelected = false, float size = BIG_BUTTON_SIZE, bool hasBorder = true, bool onClick = false, const std::function& elemUpdate = nullptr); bool rotate_wheel(const char* id, double* angle, float size = BIG_BUTTON_SIZE, const std::function& elemUpdate = nullptr); @@ -74,7 +74,7 @@ class GUIManager { void left_to_right_line_layout(const std::function& elemUpdate); void left_to_right_line_centered_layout(const std::function& elemUpdate); - bool selectable_button(const char* id, const std::function& elemUpdate, GUIStuff::SelectableButton::DrawType drawType, bool isSelected); + bool selectable_button(const char* id, const std::function& elemUpdate, GUIStuff::SelectableButton::DrawType drawType, bool isSelected, bool onClick = false); void push_id(int64_t id); void push_id(const char* id); diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index 615528e..e00974f 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -623,7 +623,7 @@ void Toolbar::top_toolbar() { bool bookmarkMenuPopUpJustOpen = false; bool gridMenuPopUpJustOpen = false; bool layerMenuPopUpJustOpen = false; - if(gui.svg_icon_button_transparent("Main Menu Button", "data/icons/menu.svg", menuPopUpOpen)) { + if(gui.svg_icon_button_transparent("Main Menu Button", "data/icons/menu.svg", menuPopUpOpen, GUIStuff::GUIManager::BIG_BUTTON_SIZE, true, true)) { if(menuPopUpOpen) menuPopUpOpen = false; else { @@ -655,7 +655,7 @@ void Toolbar::top_toolbar() { main.world->undo_with_checks(); if(gui.svg_icon_button_transparent("Menu Redo Button", "data/icons/redo.svg")) main.world->redo_with_checks(); - if(gui.svg_icon_button_transparent("Grids Button", "data/icons/grid.svg", gridMenu.popupOpen)) { + if(gui.svg_icon_button_transparent("Grids Button", "data/icons/grid.svg", gridMenu.popupOpen, GUIStuff::GUIManager::BIG_BUTTON_SIZE, true, true)) { if(gridMenu.popupOpen) stop_displaying_grid_menu(); else { @@ -663,7 +663,7 @@ void Toolbar::top_toolbar() { gridMenuPopUpJustOpen = true; } } - if(gui.svg_icon_button_transparent("Layer Menu Button", "data/icons/layer.svg", layerMenuPopupOpen)) { + if(gui.svg_icon_button_transparent("Layer Menu Button", "data/icons/layer.svg", layerMenuPopupOpen, GUIStuff::GUIManager::BIG_BUTTON_SIZE, true, true)) { if(layerMenuPopupOpen) { main.world->drawProg.layerMan.listGUI.refresh_gui_data(); layerMenuPopupOpen = false; @@ -673,7 +673,7 @@ void Toolbar::top_toolbar() { layerMenuPopUpJustOpen = true; } } - if(gui.svg_icon_button_transparent("Bookmark Menu Button", "data/icons/bookmark.svg", bookmarkMenuPopupOpen)) { + if(gui.svg_icon_button_transparent("Bookmark Menu Button", "data/icons/bookmark.svg", bookmarkMenuPopupOpen, GUIStuff::GUIManager::BIG_BUTTON_SIZE, true, true)) { if(bookmarkMenuPopupOpen) { main.world->bMan.refresh_gui_data(); bookmarkMenuPopupOpen = false; @@ -710,16 +710,25 @@ void Toolbar::top_toolbar() { main.new_tab({ .isClient = false }, true); + menuPopUpOpen = false; } - if(gui.text_button_left_transparent("open file", "Open")) + if(gui.text_button_left_transparent("open file", "Open")) { open_world_file(false, "", ""); + menuPopUpOpen = false; + } if(!main.world->clientStillConnecting) { - if(gui.text_button_left_transparent("save file", "Save")) + if(gui.text_button_left_transparent("save file", "Save")) { save_func(); - if(gui.text_button_left_transparent("save as file", "Save As")) + menuPopUpOpen = false; + } + if(gui.text_button_left_transparent("save as file", "Save As")) { save_as_func(); - if(gui.text_button_left_transparent("screenshot", "Take Screenshot")) + menuPopUpOpen = false; + } + if(gui.text_button_left_transparent("screenshot", "Take Screenshot")) { main.world->drawProg.switch_to_tool(DrawingProgramToolType::SCREENSHOT); + menuPopUpOpen = false; + } if(gui.text_button_left_transparent("add image or file to canvas", "Add Image/File to Canvas")) { #ifdef __EMSCRIPTEN__ static std::weak_ptr worldWeakPtr; @@ -742,11 +751,13 @@ void Toolbar::top_toolbar() { Logger::get().log("INFO", "Loading image to canvas that has been destroyed"); }); #endif + menuPopUpOpen = false; } if(main.world->netObjMan.is_connected()) { if(gui.text_button_left_transparent("lobby info", "Lobby Info")) { optionsMenuOpen = true; optionsMenuType = LOBBY_INFO_MENU; + menuPopUpOpen = false; } } else if(gui.text_button_left_transparent("start hosting", "Host")) { @@ -754,32 +765,38 @@ void Toolbar::top_toolbar() { serverToConnectTo = NetLibrary::get_global_id() + serverLocalID; optionsMenuOpen = true; optionsMenuType = HOST_MENU; + menuPopUpOpen = false; } if(gui.text_button_left_transparent("canvas specific settings", "Canvas Settings")) { optionsMenuOpen = true; optionsMenuType = CANVAS_SETTINGS_MENU; + menuPopUpOpen = false; } } if(gui.text_button_left_transparent("start connecting", "Connect")) { serverToConnectTo.clear(); optionsMenuOpen = true; optionsMenuType = CONNECT_MENU; + menuPopUpOpen = false; } if(gui.text_button_left_transparent("open options", "Settings")) { optionsMenuOpen = true; optionsMenuType = GENERAL_SETTINGS_MENU; + menuPopUpOpen = false; } if(gui.text_button_left_transparent("about menu button", "About")) { optionsMenuOpen = true; optionsMenuType = ABOUT_MENU; + menuPopUpOpen = false; } #ifndef __EMSCRIPTEN__ if(gui.text_button_left_transparent("quit button", "Quit")) { if(main.app_close_requested()) main.setToQuit = true; + menuPopUpOpen = false; } #endif - if(io->mouse.leftClick && !menuPopUpJustOpen) + if(io->mouse.leftClick && !Clay_Hovered() && !menuPopUpJustOpen) menuPopUpOpen = false; } } @@ -1162,7 +1179,7 @@ void Toolbar::chat_box() { gui.left_to_right_line_layout([&]() { gui.input_text("message input", &chatMessageInput); - if(gui.text_button("send button", "Send")) { + if(gui.text_button("send button", "Send", false, true)) { if(!chatMessageInput.empty()) main.world->send_chat_message(chatMessageInput); }