GUI now (mostly) reacts on tap (left click release) instead of left clicks

This commit is contained in:
Yousef Khadadeh 2026-03-04 13:50:15 +03:00
parent 911b7bc91d
commit 167108db7d
14 changed files with 66 additions and 37 deletions

View file

@ -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;

View file

@ -29,7 +29,7 @@ template <typename T> 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<float>(bb.min, bb.min + Vector2f{svSelectionAreaSize, svSelectionAreaSize}), io.mouse.pos);

View file

@ -1,4 +1,6 @@
#include "Element.hpp"
#include "Helpers/MathExtras.hpp"
#include <chrono>
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;

View file

@ -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 {

View file

@ -24,7 +24,7 @@ void MovableTabList::update(UpdateInputData& io, const std::vector<std::pair<std
},
.clip = {.horizontal = true, .childOffset = Clay_GetScrollOffset()}
}) {
selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld);
selection.update(Clay_Hovered(), io.mouse.leftClick, io.mouse.leftHeld, io.mouse.pos);
if(elemUpdate)
elemUpdate();
for(size_t i = 0; i < buttons.size(); i++) {

View file

@ -17,7 +17,7 @@ template <typename T> 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<T>(std::clamp<double>(std::lerp<double>(min, max, fracPosOnSlider), min, max)); // Clamp as double then cast so that unsigned types dont wrap on clamp

View file

@ -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;

View file

@ -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;

View file

@ -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();

View file

@ -49,7 +49,7 @@ void SelectableButton::update(UpdateInputData& io, DrawType drawType, const std:
.backgroundColor = convert_vec4<Clay_Color>(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);
}
}

View file

@ -29,7 +29,7 @@ template <typename T> 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;

View file

@ -334,14 +334,14 @@ bool GUIManager::radio_button(const char* id, bool val, const std::function<void
RadioButton* e = insert_element<RadioButton>();
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<void()>& elemUpdate) {
push_id(id);
CheckBox* e = insert_element<CheckBox>();
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<void(SelectionHelper&, bool)>& elemUpdate, GUIStuff::SelectableButton::DrawType drawType, bool isSelected) {
bool GUIManager::selectable_button(const char* id, const std::function<void(SelectionHelper&, bool)>& elemUpdate, GUIStuff::SelectableButton::DrawType drawType, bool isSelected, bool onClick) {
push_id(id);
SelectableButton* e = insert_element<SelectableButton>();
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<void()>& 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<void()>& 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<void()>& elemUpdate) {
bool GUIManager::text_button(const char* id, std::string_view text, bool isSelected, bool onClick, const std::function<void()>& 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<RotateWheel>();
e->update(*io, angle, elemUpdate);
toRet = e->selection.clicked;
toRet = e->selection.tapped;
}
pop_id();
return toRet;

View file

@ -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<void()>& elemUpdate = nullptr);
bool text_button(const char* id, std::string_view text, bool isSelected = false, const std::function<void()>& elemUpdate = nullptr);
bool text_button(const char* id, std::string_view text, bool isSelected = false, bool onClick = false, const std::function<void()>& elemUpdate = nullptr);
bool text_button_wide(const char* id, std::string_view text, bool isSelected = false, const std::function<void()>& 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<void()>& elemUpdate = nullptr);
void svg_icon(const char* id, const std::string& svgPath, bool isHighlighted = false, const std::function<void()>& 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<void()>& 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<void()>& 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<void()>& elemUpdate = nullptr);
bool rotate_wheel(const char* id, double* angle, float size = BIG_BUTTON_SIZE, const std::function<void()>& elemUpdate = nullptr);
@ -74,7 +74,7 @@ class GUIManager {
void left_to_right_line_layout(const std::function<void()>& elemUpdate);
void left_to_right_line_centered_layout(const std::function<void()>& elemUpdate);
bool selectable_button(const char* id, const std::function<void(SelectionHelper&, bool)>& elemUpdate, GUIStuff::SelectableButton::DrawType drawType, bool isSelected);
bool selectable_button(const char* id, const std::function<void(SelectionHelper&, bool)>& elemUpdate, GUIStuff::SelectableButton::DrawType drawType, bool isSelected, bool onClick = false);
void push_id(int64_t id);
void push_id(const char* id);

View file

@ -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<World> 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);
}