Skip to content

Commit

Permalink
added more constraints check to node creator
Browse files Browse the repository at this point in the history
- cannot use names of any registered nodes
- component labels inside a node have to be unique

added quick actions
- copy, cut, delete

fixed more ui things
removed unused ui methods
added tooltip
  • Loading branch information
gk646 committed May 19, 2024
1 parent 4b7b0b5 commit 6859db5
Show file tree
Hide file tree
Showing 15 changed files with 226 additions and 139 deletions.
26 changes: 15 additions & 11 deletions src/external/cxstructs/include/cxutil/cxstring.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,37 +190,41 @@ inline double str_parse_double(const char* str) {

return negative ? -result : result;
}
// Returns the levenshtein distance for the two given strings - stops when either string ends
// This doesnt penalize long strings
// Returns the Levenshtein distance for the two given strings
// This penalizes differences in length
// Failure: returns -1
template <int MAX_LEN>
int str_sort_levenshtein_prefix(const char* s1, const char* s2) {
const size_t len1 = str_len(s1), len2 = str_len(s2);
const size_t limit = len1 < len2 ? len1 : len2;
int str_sort_levenshtein(const char* s1, const char* s2) {
if (s1 == nullptr || s2 == nullptr) {
return -1; // Error: string length exceeds maximum allowed length
}

const size_t len1 = strlen(s1);
const size_t len2 = strlen(s2);

if (len1 > MAX_LEN || len2 > MAX_LEN) {
return -1; // Error: string length exceeds maximum allowed length
}

unsigned int d[MAX_LEN + 1][MAX_LEN + 1] = {0};

for (unsigned int i = 1; i <= limit; ++i)
for (unsigned int i = 0; i <= len1; ++i)
d[i][0] = i;
for (unsigned int i = 1; i <= len2; ++i)
d[0][i] = i;
for (unsigned int j = 0; j <= len2; ++j)
d[0][j] = j;

for (unsigned int i = 1; i <= limit; ++i) {
for (unsigned int i = 1; i <= len1; ++i) {
for (unsigned int j = 1; j <= len2; ++j) {
unsigned int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1;
unsigned int above = d[i - 1][j] + 1;
unsigned int left = d[i][j - 1] + 1;
unsigned int diag = d[i - 1][j - 1] + cost;

d[i][j] = above < left ? (above < diag ? above : diag) : (left < diag ? left : diag);
d[i][j] = above < left ? above : left < diag ? left : diag;
}
}

return d[limit][len2];
return d[len1][len2];
}
// string hash function
constexpr auto str_hash_fnv1a_32(char const* s) noexcept -> uint32_t {
Expand Down
3 changes: 2 additions & 1 deletion src/raynodes/application/NodeEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include "shared/rayutils.h"
#include "ui/Window.h"
#include "ui/elements/ToolTip.h"

#include "application/elements/Action.h"
#include "application/editor/EditorControls.h"
Expand Down Expand Up @@ -69,6 +70,7 @@ void DrawBackGround(EditorContext& ec) {
Editor::DrawSideBar(ec);
Editor::DrawWindows(ec);
Editor::DrawUnsavedChanges(ec);
ToolTip::Draw(ec);
}
EndTextureMode();
Editor::UpdateTick(ec); // Updates all nodes
Expand Down Expand Up @@ -99,7 +101,6 @@ void DrawForeGround(EditorContext& ec) {

int NodeEditor::run() {
const auto& camera = context.display.camera;

// Double loop to catch the window close event from raylib
// Would require native handling and overriding the window function otherwise
while (!context.core.closeApplication) {
Expand Down
23 changes: 23 additions & 0 deletions src/raynodes/application/context/impl/ContextLogic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,27 @@ void Logic::registerNodeContextActions(EditorContext& ec) {
else ec.core.addEditorAction(ec, action);
},
175);

ec.ui.nodeContextMenu.registerQickAction(
"Cut (Ctrl+X)",
[](EditorContext& ec, Node& node) {
ec.core.selectedNodes.insert({node.uID, &node});
ec.core.cut(ec);
},
17);
ec.ui.nodeContextMenu.registerQickAction(
"Copy (Ctrl+C)",
[](EditorContext& ec, Node& node) {
ec.core.selectedNodes.insert({node.uID, &node});
ec.core.copy(ec);
},
18);

ec.ui.nodeContextMenu.registerQickAction(
"Delete (Delete)",
[](EditorContext& ec, Node& node) {
ec.core.selectedNodes.insert({node.uID, &node});
ec.core.erase(ec);
},
143);
}
6 changes: 4 additions & 2 deletions src/raynodes/application/editor/EditorControls.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,10 @@ inline void PollControls(EditorContext& ec) {
if (ec.input.isMouseButtonReleased(MOUSE_BUTTON_RIGHT)) {
if (DistEuclidean(contextMenuPos, mouse) <= 5.0F) {
if (ec.logic.isAnyPinHovered) ec.logic.showPinContextMenu = true;
else if (ec.logic.isAnyNodeHovered) ec.logic.showNodeContextMenu = true;
else ec.logic.showCanvasContextMenu = true;
else if (ec.logic.isAnyNodeHovered) {
ec.logic.showNodeContextMenu = true;
ec.core.selectedNodes.insert({ec.logic.hoveredNode->uID, ec.logic.hoveredNode});
} else ec.logic.showCanvasContextMenu = true;
ec.logic.isDraggingScreen = false;
ec.logic.isSelecting = false;
return;
Expand Down
4 changes: 2 additions & 2 deletions src/raynodes/application/editor/EditorUI.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,8 @@ inline void DrawUnsavedChanges(EditorContext& ec) {
if (UI::DrawButton(ec, windowRect, "#072#Cancel")) { ec.ui.showUnsavedChanges = false; }
}
inline void DrawSideBar(EditorContext& ec) {
Rectangle button = {5, 250, 50, 25};
if (GuiButton(ec.display.getFullyScaled(button), "Create custom Node")) {
Rectangle button = {5, 250, 100, 25};
if (GuiButton(ec.display.getFullyScaled(button), "Custom Nodes")) {
ec.ui.getWindow(NODE_CREATOR)->toggleWindow(ec);
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/raynodes/application/editor/EditorUpdate.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ inline void StartUpdateTick(EditorContext& ec) {
const auto scaleY = ec.display.screenSize.y / 1080.0F;
const auto fontSize = fmaxf(13.0F, std::round(13.0F * scaleY));
GuiSetStyle(DEFAULT, TEXT_SIZE, static_cast<int>(fontSize));

ToolTip::Set(nullptr);
}

} // namespace Editor
Expand Down
50 changes: 48 additions & 2 deletions src/raynodes/shared/uiutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,56 @@

#include "shared/fwd.h"

#include <string>

#include <cxstructs/StackVector.h>
#include <cxutil/cxstring.h>
// Current limit of 150 -> stacked based so really doesnt matter
using SortVector = cxstructs::StackVector<const char*, 150, uint16_t>;
using SortVector = cxstructs::StackVector<const char*, 150>;

// Filters the strings based on prefix and then sorts after levenshtein distance
template <typename T>
void StringFilterMap(auto& map, const std::string& s, cxstructs::StackVector<T*, 150>& vec) {
vec.clear();

#endif //UIUTIL_H
if constexpr (std::is_same_v<const char, T>) { volatile int s = 5; }

const char* searchCStr = s.c_str();
const int searchSize = s.size();

for (auto& pair : map) {
const auto* name = pair.first; // Sort after keys
if (searchSize == 0 || strncmp(name, searchCStr, searchSize) == 0) {
if constexpr (std::is_same_v<NodeInfo, T>) {
vec.push_back(&pair.second);
} else if constexpr (std::is_same_v<const char, T>) {
vec.push_back(pair.first);
}
}
}

size_t size = vec.size();
if (size == 0) return;

for (uint8_t i = 0; i < size - 1; ++i) {
uint8_t minIndex = i;
for (uint8_t j = i + 1; j < size; ++j) {
const char* str1 = nullptr;
const char* str2 = nullptr;
if constexpr (std::is_same_v<NodeInfo, T>) {
str1 = vec[j]->nTemplate.label;
str2 = vec[minIndex]->nTemplate.label;
} else if constexpr (std::is_same_v<const char, T>) {
str1 = vec[j];
str2 = vec[minIndex];
}
if (cxstructs::str_sort_levenshtein<PLG_MAX_NAME_LEN>(str1, searchCStr)
< cxstructs::str_sort_levenshtein<PLG_MAX_NAME_LEN>(str2, searchCStr)) {
minIndex = j;
}
}
if (minIndex != i) { std::swap(vec[i], vec[minIndex]); }
}
}

#endif //UIUTIL_H
60 changes: 0 additions & 60 deletions src/raynodes/ui/elements/ListSearchMenu.cpp

This file was deleted.

39 changes: 33 additions & 6 deletions src/raynodes/ui/elements/NodeContextMenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#include <raygui.h>

#include "NodeContextMenu.h"

#include "ToolTip.h"
#include "application/EditorContext.h"
#include "shared/rayutils.h"

Expand All @@ -32,24 +34,49 @@ void NodeContextMenu::draw(EditorContext& ec, const Vector2& pos) {
const auto [paddingX, paddingY, w, h] = Rectangle{22, 4, 200, fs + 4};
const auto entryHeight = fs + paddingY;

const auto pressed = ec.input.isMouseButtonPressed(MOUSE_BUTTON_LEFT);
const auto pressed =
ec.input.isMouseButtonDown(MOUSE_BUTTON_LEFT) || ec.input.isMouseButtonPressed(MOUSE_BUTTON_LEFT);
const auto released = ec.input.isMouseButtonReleased(MOUSE_BUTTON_LEFT);
if (pressed) ec.input.consumeMouse();

const auto background = ColorAlpha(UI::COLORS[UI_DARK], 0.97F);
const auto highlight = ColorAlpha(UI::COLORS[UI_MEDIUM], 0.97F);

Rectangle entry = {pos.x, pos.y, w, h * 2};
// Draw First Row - Quick actions
DrawRectangleRec(entry, background);
DrawRectangleRec({pos.x, pos.y, w, 32}, background);

Rectangle entry = {pos.x, pos.y, 32, 32};
entry.x += 2;
GuiSetIconScale(2);
for (const auto& [action, name, icon] : quickActions) {
const auto hovered = CheckCollisionPointRec(mouse, entry);
const auto back = pressed ? UI::Darken(UI::COLORS[UI_MEDIUM], 25) : UI::COLORS[UI_MEDIUM];
if (hovered) {
BeginBlendMode(BLEND_ADDITIVE);
DrawRectangleRounded(entry, 0.3F, 30, ColorAlpha(back, 0.4F));
EndBlendMode();
ToolTip::Set(name);
}
GuiDrawIcon(icon, entry.x, entry.y, 2, GRAY);
if (hovered && released && ec.logic.hoveredNode) {
action(ec, *ec.logic.hoveredNode);
ec.logic.showNodeContextMenu = false;
}
entry.x += 34;
}

GuiSetIconScale(1);

entry.x = pos.x;
entry.y += entry.height;
entry.width = w;
entry.height = h;
entry.y += h * 2;

int i = 0;
for (const auto& [action, name, icon] : actions) {
if (CheckCollisionPointRec(mouse, entry)) {
DrawRectangleRounded(entry, 0.1F, 30, highlight);
if (released && ec.logic.hoveredNode) {
if (released && ec.logic.hoveredNode != nullptr) {
action(ec, *ec.logic.hoveredNode);
ec.logic.showNodeContextMenu = false;
}
Expand All @@ -64,7 +91,7 @@ void NodeContextMenu::draw(EditorContext& ec, const Vector2& pos) {
i++;
}

const auto totalHeight = static_cast<float>(actions.size()) * entryHeight + h * 2;
const auto totalHeight = static_cast<float>(actions.size()) * entryHeight + h * 1.5F;
if (!CheckExtendedRec(mouse, {entry.x, entry.y - totalHeight, entry.width, totalHeight},
UI::CONTEXT_MENU_THRESHOLD)) {
ec.logic.showNodeContextMenu = false;
Expand Down
15 changes: 14 additions & 1 deletion src/raynodes/ui/elements/NodeContextMenu.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ struct ContextActionInfo {
};

struct NodeContextMenu {
static constexpr int SIZE = 1;
static constexpr int SIZE = 4;
cxstructs::StackVector<ContextActionInfo, SIZE> actions;
cxstructs::StackVector<ContextActionInfo, SIZE> quickActions;

void draw(EditorContext& ec, const Vector2& pos);
void registerAction(const char* name, NodeContextAction action, uint8_t iconID) {
Expand All @@ -44,6 +45,18 @@ struct NodeContextMenu {
}
actions.push_back({action, name, iconID});
}
void registerQickAction(const char* name, NodeContextAction action, uint8_t iconID) {
for (const auto& n : quickActions) {
if (strcmp(n.name, name) == 0) {
fprintf(stderr, "QuickAction with this name already exists");
return;
}
}
quickActions.push_back({action, name, iconID});
}

private:
void drawQuickActions();
};

#endif //ACTIONMENU_H

0 comments on commit 6859db5

Please sign in to comment.