summaryrefslogtreecommitdiff
path: root/3rdparty/imgui-node-editor
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/imgui-node-editor')
-rw-r--r--3rdparty/imgui-node-editor/crude_json.cpp80
-rw-r--r--3rdparty/imgui-node-editor/crude_json.h29
-rw-r--r--3rdparty/imgui-node-editor/imgui_bezier_math.h2
-rw-r--r--3rdparty/imgui-node-editor/imgui_bezier_math.inl5
-rw-r--r--3rdparty/imgui-node-editor/imgui_canvas.cpp47
-rw-r--r--3rdparty/imgui-node-editor/imgui_canvas.h16
-rw-r--r--3rdparty/imgui-node-editor/imgui_extra_math.h2
-rw-r--r--3rdparty/imgui-node-editor/imgui_extra_math.inl2
-rw-r--r--3rdparty/imgui-node-editor/imgui_node_editor.cpp709
-rw-r--r--3rdparty/imgui-node-editor/imgui_node_editor.h47
-rw-r--r--3rdparty/imgui-node-editor/imgui_node_editor_api.cpp115
-rw-r--r--3rdparty/imgui-node-editor/imgui_node_editor_internal.h92
-rw-r--r--3rdparty/imgui-node-editor/imgui_node_editor_internal.inl2
-rw-r--r--3rdparty/imgui-node-editor/thedmd-imgui-node-editor.stamp2
14 files changed, 911 insertions, 239 deletions
diff --git a/3rdparty/imgui-node-editor/crude_json.cpp b/3rdparty/imgui-node-editor/crude_json.cpp
index 34f117f..a9f7c70 100644
--- a/3rdparty/imgui-node-editor/crude_json.cpp
+++ b/3rdparty/imgui-node-editor/crude_json.cpp
@@ -1,4 +1,6 @@
-// Crude implementation of JSON value object and parser.
+// Crude implementation of JSON value object and parser.
+//
+// VERSION 0.1
//
// LICENSE
// This software is dual-licensed to the public domain and under the following
@@ -14,7 +16,10 @@
# include <clocale>
# include <cmath>
# include <cstring>
-
+# if CRUDE_JSON_IO
+# include <stdio.h>
+# include <memory>
+# endif
namespace crude_json {
@@ -147,6 +152,22 @@ void value::push_back(value&& value)
}
}
+size_t value::erase(const string& key)
+{
+ if (!is_object())
+ return 0;
+
+ auto& o = *object_ptr(m_Storage);
+ auto it = o.find(key);
+
+ if (it == o.end())
+ return 0;
+
+ o.erase(it);
+
+ return 1;
+}
+
void value::swap(value& other)
{
using std::swap;
@@ -811,4 +832,59 @@ value value::parse(const string& data)
return v;
}
+# if CRUDE_JSON_IO
+std::pair<value, bool> value::load(const string& path)
+{
+ // Modern C++, so beautiful...
+ std::unique_ptr<FILE, void(*)(FILE*)> file{nullptr, [](FILE* file) { if (file) fclose(file); }};
+# if defined(_MSC_VER) || (defined(__STDC_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__)
+ FILE* handle = nullptr;
+ if (fopen_s(&handle, path.c_str(), "rb") != 0)
+ return {value{}, false};
+ file.reset(handle);
+# else
+ file.reset(fopen(path.c_str(), "rb"));
+# endif
+
+ if (!file)
+ return {value{}, false};
+
+ fseek(file.get(), 0, SEEK_END);
+ auto size = static_cast<size_t>(ftell(file.get()));
+ fseek(file.get(), 0, SEEK_SET);
+
+ string data;
+ data.resize(size);
+ if (fread(const_cast<char*>(data.data()), size, 1, file.get()) != 1)
+ return {value{}, false};
+
+ return {parse(data), true};
+}
+
+bool value::save(const string& path, const int indent, const char indent_char) const
+{
+ // Modern C++, so beautiful...
+ std::unique_ptr<FILE, void(*)(FILE*)> file{nullptr, [](FILE* file) { if (file) fclose(file); }};
+# if defined(_MSC_VER) || (defined(__STDC_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__)
+ FILE* handle = nullptr;
+ if (fopen_s(&handle, path.c_str(), "wb") != 0)
+ return false;
+ file.reset(handle);
+# else
+ file.reset(fopen(path.c_str(), "wb"));
+# endif
+
+ if (!file)
+ return false;
+
+ auto data = dump(indent, indent_char);
+
+ if (fwrite(data.data(), data.size(), 1, file.get()) != 1)
+ return false;
+
+ return true;
+}
+
+# endif
+
} // namespace crude_json
diff --git a/3rdparty/imgui-node-editor/crude_json.h b/3rdparty/imgui-node-editor/crude_json.h
index a95abb9..f1eff29 100644
--- a/3rdparty/imgui-node-editor/crude_json.h
+++ b/3rdparty/imgui-node-editor/crude_json.h
@@ -1,4 +1,6 @@
-// Crude implementation of JSON value object and parser.
+// Crude implementation of JSON value object and parser.
+//
+// VERSION 0.1
//
// LICENSE
// This software is dual-licensed to the public domain and under the following
@@ -24,6 +26,10 @@
# define CRUDE_ASSERT(expr) assert(expr)
# endif
+# ifndef CRUDE_JSON_IO
+# define CRUDE_JSON_IO 1
+# endif
+
namespace crude_json {
struct value;
@@ -92,6 +98,8 @@ struct value
void push_back(const value& value);
void push_back(value&& value);
+ size_t erase(const string& key);
+
bool is_primitive() const { return is_string() || is_number() || is_boolean() || is_null(); }
bool is_structured() const { return is_object() || is_array(); }
bool is_null() const { return m_Type == type_t::null; }
@@ -105,6 +113,9 @@ struct value
template <typename T> const T& get() const;
template <typename T> T& get();
+ template <typename T> const T* get_ptr() const;
+ template <typename T> T* get_ptr();
+
string dump(const int indent = -1, const char indent_char = ' ') const;
void swap(value& other);
@@ -114,6 +125,11 @@ struct value
// Returns discarded value for invalid inputs.
static value parse(const string& data);
+# if CRUDE_JSON_IO
+ static std::pair<value, bool> load(const string& path);
+ bool save(const string& path, const int indent = -1, const char indent_char = ' ') const;
+# endif
+
private:
struct parser;
@@ -217,6 +233,17 @@ template <> inline string& value::get<string>() { CRUDE_ASSERT(m_T
template <> inline boolean& value::get<boolean>() { CRUDE_ASSERT(m_Type == type_t::boolean); return *boolean_ptr(m_Storage); }
template <> inline number& value::get<number>() { CRUDE_ASSERT(m_Type == type_t::number); return *number_ptr(m_Storage); }
+template <> inline const object* value::get_ptr<object>() const { if (m_Type == type_t::object) return object_ptr(m_Storage); else return nullptr; }
+template <> inline const array* value::get_ptr<array>() const { if (m_Type == type_t::array) return array_ptr(m_Storage); else return nullptr; }
+template <> inline const string* value::get_ptr<string>() const { if (m_Type == type_t::string) return string_ptr(m_Storage); else return nullptr; }
+template <> inline const boolean* value::get_ptr<boolean>() const { if (m_Type == type_t::boolean) return boolean_ptr(m_Storage); else return nullptr; }
+template <> inline const number* value::get_ptr<number>() const { if (m_Type == type_t::number) return number_ptr(m_Storage); else return nullptr; }
+
+template <> inline object* value::get_ptr<object>() { if (m_Type == type_t::object) return object_ptr(m_Storage); else return nullptr; }
+template <> inline array* value::get_ptr<array>() { if (m_Type == type_t::array) return array_ptr(m_Storage); else return nullptr; }
+template <> inline string* value::get_ptr<string>() { if (m_Type == type_t::string) return string_ptr(m_Storage); else return nullptr; }
+template <> inline boolean* value::get_ptr<boolean>() { if (m_Type == type_t::boolean) return boolean_ptr(m_Storage); else return nullptr; }
+template <> inline number* value::get_ptr<number>() { if (m_Type == type_t::number) return number_ptr(m_Storage); else return nullptr; }
} // namespace crude_json
diff --git a/3rdparty/imgui-node-editor/imgui_bezier_math.h b/3rdparty/imgui-node-editor/imgui_bezier_math.h
index 09b76e7..85ead39 100644
--- a/3rdparty/imgui-node-editor/imgui_bezier_math.h
+++ b/3rdparty/imgui-node-editor/imgui_bezier_math.h
@@ -1,4 +1,6 @@
//------------------------------------------------------------------------------
+// VERSION 0.1
+//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
diff --git a/3rdparty/imgui-node-editor/imgui_bezier_math.inl b/3rdparty/imgui-node-editor/imgui_bezier_math.inl
index c2c7c43..3020bdb 100644
--- a/3rdparty/imgui-node-editor/imgui_bezier_math.inl
+++ b/3rdparty/imgui-node-editor/imgui_bezier_math.inl
@@ -1,4 +1,6 @@
//------------------------------------------------------------------------------
+// VERSION 0.1
+//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
@@ -249,6 +251,9 @@ inline ImRect ImCubicBezierBoundingRect(const ImVec2& p0, const ImVec2& p1, cons
for (int i = 0; i < 2; ++i)
{
+ if (IM_VEC2_INDEX(a, i) == 0.0f)
+ continue;
+
if (IM_VEC2_INDEX(delta_squared, i) >= 0)
{
auto delta = ImSqrt(IM_VEC2_INDEX(delta_squared, i));
diff --git a/3rdparty/imgui-node-editor/imgui_canvas.cpp b/3rdparty/imgui-node-editor/imgui_canvas.cpp
index 9c71051..c71a413 100644
--- a/3rdparty/imgui-node-editor/imgui_canvas.cpp
+++ b/3rdparty/imgui-node-editor/imgui_canvas.cpp
@@ -101,8 +101,13 @@ bool ImGuiEx::Canvas::Begin(ImGuiID id, const ImVec2& size)
UpdateViewTransformPosition();
+# if IMGUI_VERSION_NUM > 18415
+ if (ImGui::IsClippedEx(m_WidgetRect, id))
+ return false;
+# else
if (ImGui::IsClippedEx(m_WidgetRect, id, false))
return false;
+# endif
// Save current channel, so we can assert when user
// call canvas API with different one.
@@ -110,7 +115,7 @@ bool ImGuiEx::Canvas::Begin(ImGuiID id, const ImVec2& size)
// #debug: Canvas content.
//m_DrawList->AddRectFilled(m_StartPos, m_StartPos + m_CurrentSize, IM_COL32(0, 0, 0, 64));
- m_DrawList->AddRect(m_WidgetRect.Min, m_WidgetRect.Max, IM_COL32(255, 0, 255, 64));
+ //m_DrawList->AddRect(m_WidgetRect.Min, m_WidgetRect.Max, IM_COL32(255, 0, 255, 64));
ImGui::SetCursorScreenPos(ImVec2(0.0f, 0.0f));
@@ -121,6 +126,9 @@ bool ImGuiEx::Canvas::Begin(ImGuiID id, const ImVec2& size)
SaveInputState();
SaveViewportState();
+ // Record cursor max to prevent scrollbars from appearing.
+ m_WindowCursorMaxBackup = ImGui::GetCurrentWindow()->DC.CursorMaxPos;
+
EnterLocalSpace();
// Emit dummy widget matching bounds of the canvas.
@@ -152,6 +160,10 @@ void ImGuiEx::Canvas::End()
LeaveLocalSpace();
+ ImGui::GetCurrentWindow()->DC.CursorMaxPos = m_WindowCursorMaxBackup;
+
+ ImGui::SetItemAllowOverlap();
+
// Emit dummy widget matching bounds of the canvas.
ImGui::SetCursorScreenPos(m_WidgetPosition);
ImGui::Dummy(m_WidgetSize);
@@ -330,9 +342,6 @@ void ImGuiEx::Canvas::SaveInputState()
m_MousePosPrevBackup = io.MousePosPrev;
for (auto i = 0; i < IM_ARRAYSIZE(m_MouseClickedPosBackup); ++i)
m_MouseClickedPosBackup[i] = io.MouseClickedPos[i];
-
- // Record cursor max to prevent scrollbars from appearing.
- m_WindowCursorMaxBackup = ImGui::GetCurrentWindow()->DC.CursorMaxPos;
}
void ImGuiEx::Canvas::RestoreInputState()
@@ -342,26 +351,43 @@ void ImGuiEx::Canvas::RestoreInputState()
io.MousePosPrev = m_MousePosPrevBackup;
for (auto i = 0; i < IM_ARRAYSIZE(m_MouseClickedPosBackup); ++i)
io.MouseClickedPos[i] = m_MouseClickedPosBackup[i];
- ImGui::GetCurrentWindow()->DC.CursorMaxPos = m_WindowCursorMaxBackup;
}
void ImGuiEx::Canvas::SaveViewportState()
{
# if defined(IMGUI_HAS_VIEWPORT)
+ auto window = ImGui::GetCurrentWindow();
auto viewport = ImGui::GetWindowViewport();
+ m_WindowPosBackup = window->Pos;
m_ViewportPosBackup = viewport->Pos;
m_ViewportSizeBackup = viewport->Size;
+# if IMGUI_VERSION_NUM > 18002
+ m_ViewportWorkPosBackup = viewport->WorkPos;
+ m_ViewportWorkSizeBackup = viewport->WorkSize;
+# else
+ m_ViewportWorkOffsetMinBackup = viewport->WorkOffsetMin;
+ m_ViewportWorkOffsetMaxBackup = viewport->WorkOffsetMax;
+# endif
# endif
}
void ImGuiEx::Canvas::RestoreViewportState()
{
# if defined(IMGUI_HAS_VIEWPORT)
+ auto window = ImGui::GetCurrentWindow();
auto viewport = ImGui::GetWindowViewport();
+ window->Pos = m_WindowPosBackup;
viewport->Pos = m_ViewportPosBackup;
viewport->Size = m_ViewportSizeBackup;
+# if IMGUI_VERSION_NUM > 18002
+ viewport->WorkPos = m_ViewportWorkPosBackup;
+ viewport->WorkSize = m_ViewportWorkSizeBackup;
+# else
+ viewport->WorkOffsetMin = m_ViewportWorkOffsetMinBackup;
+ viewport->WorkOffsetMax = m_ViewportWorkOffsetMaxBackup;
+# endif
# endif
}
@@ -407,6 +433,9 @@ void ImGuiEx::Canvas::EnterLocalSpace()
m_DrawListStartVertexIndex = m_DrawList->_VtxCurrentIdx + ImVtxOffsetRef(m_DrawList);
# if defined(IMGUI_HAS_VIEWPORT)
+ auto window = ImGui::GetCurrentWindow();
+ window->Pos = ImVec2(0.0f, 0.0f);
+
auto viewport_min = m_ViewportPosBackup;
auto viewport_max = m_ViewportPosBackup + m_ViewportSizeBackup;
@@ -418,6 +447,14 @@ void ImGuiEx::Canvas::EnterLocalSpace()
auto viewport = ImGui::GetWindowViewport();
viewport->Pos = viewport_min;
viewport->Size = viewport_max - viewport_min;
+
+# if IMGUI_VERSION_NUM > 18002
+ viewport->WorkPos = m_ViewportWorkPosBackup * m_View.InvScale;
+ viewport->WorkSize = m_ViewportWorkSizeBackup * m_View.InvScale;
+# else
+ viewport->WorkOffsetMin = m_ViewportWorkOffsetMinBackup * m_View.InvScale;
+ viewport->WorkOffsetMax = m_ViewportWorkOffsetMaxBackup * m_View.InvScale;
+# endif
# endif
// Clip rectangle in parent canvas space and move it to local space.
diff --git a/3rdparty/imgui-node-editor/imgui_canvas.h b/3rdparty/imgui-node-editor/imgui_canvas.h
index acbb8ae..e5e4959 100644
--- a/3rdparty/imgui-node-editor/imgui_canvas.h
+++ b/3rdparty/imgui-node-editor/imgui_canvas.h
@@ -1,11 +1,11 @@
-// Canvas widget - view over infinite virtual space.
+// Canvas widget - view over infinite virtual space.
//
// Canvas allows you to draw your widgets anywhere over infinite space and provide
// view over it with support for panning and scaling.
//
// When you enter a canvas ImGui is moved to virtual space which mean:
// - ImGui::GetCursorScreenPos() return (0, 0) and which correspond to top left corner
-// of the canvas on the screen (this can be changed usign CanvasView()).
+// of the canvas on the screen (this can be changed using CanvasView()).
// - Mouse input is brought to canvas space, so widgets works as usual.
// - Everything you draw with ImDrawList will be in virtual space.
//
@@ -23,7 +23,7 @@
// as usual in ImGui.
//
// While drawing inside canvas you can translate position from world (usual ImGui space)
-// to virtual space and back usign CanvasFromWorld()/CanvasToWorld().
+// to virtual space and back using CanvasFromWorld()/CanvasToWorld().
//
// Canvas can be nested in each other (they are regular widgets after all). There
// is a way to transform position between current and parent canvas with
@@ -35,6 +35,8 @@
// Note:
// It is not valid to call canvas API outside of BeginCanvas() / EndCanvas() scope.
//
+// VERSION 0.1
+//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
@@ -248,8 +250,16 @@ private:
ImVec2 m_WindowCursorMaxBackup;
# if defined(IMGUI_HAS_VIEWPORT)
+ ImVec2 m_WindowPosBackup;
ImVec2 m_ViewportPosBackup;
ImVec2 m_ViewportSizeBackup;
+# if IMGUI_VERSION_NUM > 18002
+ ImVec2 m_ViewportWorkPosBackup;
+ ImVec2 m_ViewportWorkSizeBackup;
+# else
+ ImVec2 m_ViewportWorkOffsetMinBackup;
+ ImVec2 m_ViewportWorkOffsetMaxBackup;
+# endif
# endif
};
diff --git a/3rdparty/imgui-node-editor/imgui_extra_math.h b/3rdparty/imgui-node-editor/imgui_extra_math.h
index 2a3a2fe..3022055 100644
--- a/3rdparty/imgui-node-editor/imgui_extra_math.h
+++ b/3rdparty/imgui-node-editor/imgui_extra_math.h
@@ -1,4 +1,6 @@
//------------------------------------------------------------------------------
+// VERSION 0.9.1
+//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
diff --git a/3rdparty/imgui-node-editor/imgui_extra_math.inl b/3rdparty/imgui-node-editor/imgui_extra_math.inl
index 18fb25e..13cf990 100644
--- a/3rdparty/imgui-node-editor/imgui_extra_math.inl
+++ b/3rdparty/imgui-node-editor/imgui_extra_math.inl
@@ -1,4 +1,6 @@
//------------------------------------------------------------------------------
+// VERSION 0.9.1
+//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
diff --git a/3rdparty/imgui-node-editor/imgui_node_editor.cpp b/3rdparty/imgui-node-editor/imgui_node_editor.cpp
index c1911c3..1042bed 100644
--- a/3rdparty/imgui-node-editor/imgui_node_editor.cpp
+++ b/3rdparty/imgui-node-editor/imgui_node_editor.cpp
@@ -1,4 +1,6 @@
//------------------------------------------------------------------------------
+// VERSION 0.9.1
+//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
@@ -111,7 +113,15 @@ static const float c_LinkSelectThickness = 5.0f; // canvas pixels
static const float c_NavigationZoomMargin = 0.1f; // percentage of visible bounds
static const float c_MouseZoomDuration = 0.15f; // seconds
static const float c_SelectionFadeOutDuration = 0.15f; // seconds
-static const auto c_ScrollButtonIndex = 1;
+
+static const auto c_MaxMoveOverEdgeSpeed = 10.0f;
+static const auto c_MaxMoveOverEdgeDistance = 300.0f;
+
+#if IMGUI_VERSION_NUM > 18101
+static const auto c_AllRoundCornersFlags = ImDrawFlags_RoundCornersAll;
+#else
+static const auto c_AllRoundCornersFlags = 15;
+#endif
//------------------------------------------------------------------------------
@@ -461,7 +471,7 @@ static void ImDrawList_AddBezierWithArrows(ImDrawList* drawList, const ImCubicBe
if (fill)
{
- drawList->AddBezierCurve(curve.P0, curve.P1, curve.P2, curve.P3, color, thickness);
+ drawList->AddBezierCubic(curve.P0, curve.P1, curve.P2, curve.P3, color, thickness);
if (startArrowSize > 0.0f)
{
@@ -617,7 +627,7 @@ void ed::Node::Draw(ImDrawList* drawList, DrawFlags flags)
drawList->AddRect(
m_GroupBounds.Min,
m_GroupBounds.Max,
- m_GroupBorderColor, m_GroupRounding, 15, m_GroupBorderWidth);
+ m_GroupBorderColor, m_GroupRounding, c_AllRoundCornersFlags, m_GroupBorderWidth);
}
}
@@ -668,7 +678,7 @@ void ed::Node::DrawBorder(ImDrawList* drawList, ImU32 color, float thickness)
if (thickness > 0.0f)
{
drawList->AddRect(m_Bounds.Min, m_Bounds.Max,
- color, m_Rounding, 15, thickness);
+ color, m_Rounding, c_AllRoundCornersFlags, thickness);
}
}
@@ -1009,7 +1019,9 @@ ImRect ed::Link::GetBounds() const
//------------------------------------------------------------------------------
ed::EditorContext::EditorContext(const ax::NodeEditor::Config* config)
: m_IsFirstFrame(true)
- , m_IsWindowActive(false)
+ , m_IsFocused(false)
+ , m_IsHovered(false)
+ , m_IsHoveredWithoutOverlapp(false)
, m_ShortcutsEnabled(true)
, m_Style()
, m_Nodes()
@@ -1032,6 +1044,9 @@ ed::EditorContext::EditorContext(const ax::NodeEditor::Config* config)
, m_DeleteItemsAction(this)
, m_AnimationControllers{ &m_FlowAnimationController }
, m_FlowAnimationController(this)
+ , m_HoveredNode(0)
+ , m_HoveredPin(0)
+ , m_HoveredLink(0)
, m_DoubleClickedNode(0)
, m_DoubleClickedPin(0)
, m_DoubleClickedLink(0)
@@ -1040,6 +1055,7 @@ ed::EditorContext::EditorContext(const ax::NodeEditor::Config* config)
, m_IsInitialized(false)
, m_Settings()
, m_Config(config)
+ , m_DrawList(nullptr)
, m_ExternalChannel(0)
{
}
@@ -1071,10 +1087,10 @@ void ed::EditorContext::Begin(const char* id, const ImVec2& size)
for (auto pin : m_Pins) pin->Reset();
for (auto link : m_Links) link->Reset();
- auto drawList = ImGui::GetWindowDrawList();
+ m_DrawList = ImGui::GetWindowDrawList();
- ImDrawList_SwapSplitter(drawList, m_Splitter);
- m_ExternalChannel = drawList->_Splitter._Current;
+ ImDrawList_SwapSplitter(m_DrawList, m_Splitter);
+ m_ExternalChannel = m_DrawList->_Splitter._Current;
ImGui::PushID(id);
@@ -1085,6 +1101,18 @@ void ed::EditorContext::Begin(const char* id, const ImVec2& size)
if (canvasSize.y <= 0.0f)
canvasSize.y = ImMax(4.0f, availableContentSize.y);
+ if (m_CurrentAction && m_CurrentAction->IsDragging() && m_NavigateAction.MoveOverEdge(canvasSize))
+ {
+ auto& io = ImGui::GetIO();
+ auto offset = m_NavigateAction.GetMoveScreenOffset();
+ for (int i = 0; i < 5; ++i)
+ io.MouseClickedPos[i] = io.MouseClickedPos[i] - offset;
+ }
+ else
+ m_NavigateAction.StopMoveOverEdge();
+
+ auto previousSize = m_Canvas.Rect().GetSize();
+ auto previousVisibleRect = m_Canvas.ViewRect();
m_IsCanvasVisible = m_Canvas.Begin(id, canvasSize);
//ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0));
@@ -1093,22 +1121,28 @@ void ed::EditorContext::Begin(const char* id, const ImVec2& size)
// ImGuiWindowFlags_NoScrollbar |
// ImGuiWindowFlags_NoScrollWithMouse);
- ImGui::CaptureKeyboardFromApp();
-
- m_IsWindowActive = ImGui::IsWindowFocused();
+ m_IsFocused = ImGui::IsWindowFocused();
//
- m_NavigateAction.SetWindow(m_Canvas.ViewRect().Min, m_Canvas.ViewRect().GetSize());
+ m_NavigateAction.SetWindow(m_Canvas.ViewRect().Min, m_Canvas.ViewRect().GetSize());
- if (m_CurrentAction && m_CurrentAction->IsDragging() && m_NavigateAction.MoveOverEdge())
+ // Handle canvas size change. Scale to Y axis, center on X.
+ if (!ImRect_IsEmpty(previousVisibleRect) && previousSize != canvasSize)
{
- auto& io = ImGui::GetIO();
- auto offset = m_NavigateAction.GetMoveOffset();
- for (int i = 0; i < 5; ++i)
- io.MouseClickedPos[i] = io.MouseClickedPos[i] - offset;
+ m_NavigateAction.FinishNavigation();
+
+ auto currentVisibleRect = m_Canvas.ViewRect();
+ auto currentAspectRatio = currentVisibleRect.GetHeight() ? (currentVisibleRect.GetWidth() / currentVisibleRect.GetHeight()) : 0.0f;
+
+ auto centerX = (previousVisibleRect.Max.x + previousVisibleRect.Min.x) * 0.5f;
+ auto height = previousVisibleRect.GetHeight();
+ auto width = currentAspectRatio * height;
+
+ previousVisibleRect.Min.x = centerX - 0.5f * width;
+ previousVisibleRect.Max.x = centerX + 0.5f * width;
+
+ m_NavigateAction.NavigateTo(previousVisibleRect, Detail::NavigateAction::ZoomMode::Exact, 0.0f);
}
- else
- m_NavigateAction.StopMoveOverEdge();
m_Canvas.SetView(m_NavigateAction.GetView());
@@ -1117,7 +1151,7 @@ void ed::EditorContext::Begin(const char* id, const ImVec2& size)
// clipMin.x, clipMin.y, clipMax.x - clipMin.x, clipMax.y - clipMin.y, clipMax.x, clipMax.y);
// Reserve channels for background and links
- ImDrawList_ChannelsGrow(drawList, c_NodeStartChannel);
+ ImDrawList_ChannelsGrow(m_DrawList, c_NodeStartChannel);
if (HasSelectionChanged())
++m_SelectionId;
@@ -1129,9 +1163,11 @@ void ed::EditorContext::End()
{
//auto& io = ImGui::GetIO();
auto control = BuildControl(m_CurrentAction && m_CurrentAction->IsDragging()); // NavigateAction.IsMovingOverEdge()
- auto drawList = ImGui::GetWindowDrawList();
//auto& editorStyle = GetStyle();
+ m_HoveredNode = control.HotNode && m_CurrentAction == nullptr ? control.HotNode->m_ID : 0;
+ m_HoveredPin = control.HotPin && m_CurrentAction == nullptr ? control.HotPin->m_ID : 0;
+ m_HoveredLink = control.HotLink && m_CurrentAction == nullptr ? control.HotLink->m_ID : 0;
m_DoubleClickedNode = control.DoubleClickedNode ? control.DoubleClickedNode->m_ID : 0;
m_DoubleClickedPin = control.DoubleClickedPin ? control.DoubleClickedPin->m_ID : 0;
m_DoubleClickedLink = control.DoubleClickedLink ? control.DoubleClickedLink->m_ID : 0;
@@ -1150,12 +1186,12 @@ void ed::EditorContext::End()
// Draw nodes
for (auto node : m_Nodes)
if (node->m_IsLive && node->IsVisible())
- node->Draw(drawList);
+ node->Draw(m_DrawList);
// Draw links
for (auto link : m_Links)
if (link->m_IsLive && link->IsVisible())
- link->Draw(drawList);
+ link->Draw(m_DrawList);
// Highlight selected objects
{
@@ -1165,7 +1201,7 @@ void ed::EditorContext::End()
for (auto selectedObject : *selectedObjects)
if (selectedObject->IsVisible())
- selectedObject->Draw(drawList, Object::Selected);
+ selectedObject->Draw(m_DrawList, Object::Selected);
}
if (!isSelecting)
@@ -1177,12 +1213,12 @@ void ed::EditorContext::End()
hoveredObject = sizeAction->m_SizedNode;
if (hoveredObject && !IsSelected(hoveredObject) && hoveredObject->IsVisible())
- hoveredObject->Draw(drawList, Object::Hovered);
+ hoveredObject->Draw(m_DrawList, Object::Hovered);
}
// Draw animations
for (auto controller : m_AnimationControllers)
- controller->Draw(drawList);
+ controller->Draw(m_DrawList);
if (m_CurrentAction && !m_CurrentAction->Process(control))
m_CurrentAction = nullptr;
@@ -1236,7 +1272,7 @@ void ed::EditorContext::End()
ImGui::SetMouseCursor(m_CurrentAction->GetCursor());
// Draw selection rectangle
- m_SelectAction.Draw(drawList);
+ m_SelectAction.Draw(m_DrawList);
bool sortGroups = false;
if (control.ActiveNode)
@@ -1281,6 +1317,12 @@ void ed::EditorContext::End()
});
}
+ // Apply Z order
+ std::stable_sort(m_Nodes.begin(), m_Nodes.end(), [](const auto& lhs, const auto& rhs)
+ {
+ return lhs->m_ZPosition < rhs->m_ZPosition;
+ });
+
# if 1
// Every node has few channels assigned. Grow channel list
// to hold twice as much of channels and place them in
@@ -1290,18 +1332,18 @@ void ed::EditorContext::End()
auto liveNodeCount = static_cast<int>(std::count_if(m_Nodes.begin(), m_Nodes.end(), [](Node* node) { return node->m_IsLive; }));
// Reserve two additional channels for sorted list of channels
- auto nodeChannelCount = drawList->_Splitter._Count;
- ImDrawList_ChannelsGrow(drawList, drawList->_Splitter._Count + c_ChannelsPerNode * liveNodeCount + c_LinkChannelCount);
+ auto nodeChannelCount = m_DrawList->_Splitter._Count;
+ ImDrawList_ChannelsGrow(m_DrawList, m_DrawList->_Splitter._Count + c_ChannelsPerNode * liveNodeCount + c_LinkChannelCount);
int targetChannel = nodeChannelCount;
- auto copyNode = [&targetChannel, drawList](Node* node)
+ auto copyNode = [this, &targetChannel](Node* node)
{
if (!node->m_IsLive)
return;
for (int i = 0; i < c_ChannelsPerNode; ++i)
- ImDrawList_SwapChannels(drawList, node->m_Channel + i, targetChannel + i);
+ ImDrawList_SwapChannels(m_DrawList, node->m_Channel + i, targetChannel + i);
node->m_Channel = targetChannel;
targetChannel += c_ChannelsPerNode;
@@ -1314,7 +1356,7 @@ void ed::EditorContext::End()
// Copy links
for (int i = 0; i < c_LinkChannelCount; ++i, ++targetChannel)
- ImDrawList_SwapChannels(drawList, c_LinkStartChannel + i, targetChannel);
+ ImDrawList_SwapChannels(m_DrawList, c_LinkStartChannel + i, targetChannel);
// Copy normal nodes
std::for_each(groupsItEnd, m_Nodes.end(), copyNode);
@@ -1328,7 +1370,7 @@ void ed::EditorContext::End()
{
//auto& style = ImGui::GetStyle();
- drawList->ChannelsSetCurrent(c_UserChannel_Grid);
+ m_DrawList->ChannelsSetCurrent(c_UserChannel_Grid);
ImVec2 offset = m_Canvas.ViewOrigin() * (1.0f / m_Canvas.ViewScale());
ImU32 GRID_COLOR = GetColor(StyleColor_Grid, ImClamp(m_Canvas.ViewScale() * m_Canvas.ViewScale(), 0.0f, 1.0f));
@@ -1337,12 +1379,12 @@ void ed::EditorContext::End()
ImVec2 VIEW_POS = m_Canvas.ViewRect().Min;
ImVec2 VIEW_SIZE = m_Canvas.ViewRect().GetSize();
- drawList->AddRectFilled(VIEW_POS, VIEW_POS + VIEW_SIZE, GetColor(StyleColor_Bg));
+ m_DrawList->AddRectFilled(VIEW_POS, VIEW_POS + VIEW_SIZE, GetColor(StyleColor_Bg));
for (float x = fmodf(offset.x, GRID_SX); x < VIEW_SIZE.x; x += GRID_SX)
- drawList->AddLine(ImVec2(x, 0.0f) + VIEW_POS, ImVec2(x, VIEW_SIZE.y) + VIEW_POS, GRID_COLOR);
+ m_DrawList->AddLine(ImVec2(x, 0.0f) + VIEW_POS, ImVec2(x, VIEW_SIZE.y) + VIEW_POS, GRID_COLOR);
for (float y = fmodf(offset.y, GRID_SY); y < VIEW_SIZE.y; y += GRID_SY)
- drawList->AddLine(ImVec2(0.0f, y) + VIEW_POS, ImVec2(VIEW_SIZE.x, y) + VIEW_POS, GRID_COLOR);
+ m_DrawList->AddLine(ImVec2(0.0f, y) + VIEW_POS, ImVec2(VIEW_SIZE.x, y) + VIEW_POS, GRID_COLOR);
}
# endif
@@ -1386,9 +1428,9 @@ void ed::EditorContext::End()
// These channels already have clip planes in global space, so
// we move them to clip plane. Batch transformation in canvas
// will bring them back to global space.
- auto preTransformClipRect = [this, drawList](int channelIndex)
+ auto preTransformClipRect = [this](int channelIndex)
{
- ImDrawChannel& channel = drawList->_Splitter._Channels[channelIndex];
+ ImDrawChannel& channel = m_DrawList->_Splitter._Channels[channelIndex];
for (ImDrawCmd& cmd : channel._CmdBuffer)
{
auto a = ToCanvas(ImVec2(cmd.ClipRect.x, cmd.ClipRect.y));
@@ -1397,13 +1439,13 @@ void ed::EditorContext::End()
}
};
- drawList->ChannelsSetCurrent(0);
+ m_DrawList->ChannelsSetCurrent(0);
- auto channelCount = drawList->_Splitter._Count;
- ImDrawList_ChannelsGrow(drawList, channelCount + 3);
- ImDrawList_SwapChannels(drawList, c_UserChannel_HintsBackground, channelCount + 0);
- ImDrawList_SwapChannels(drawList, c_UserChannel_Hints, channelCount + 1);
- ImDrawList_SwapChannels(drawList, c_UserChannel_Content, channelCount + 2);
+ auto channelCount = m_DrawList->_Splitter._Count;
+ ImDrawList_ChannelsGrow(m_DrawList, channelCount + 3);
+ ImDrawList_SwapChannels(m_DrawList, c_UserChannel_HintsBackground, channelCount + 0);
+ ImDrawList_SwapChannels(m_DrawList, c_UserChannel_Hints, channelCount + 1);
+ ImDrawList_SwapChannels(m_DrawList, c_UserChannel_Content, channelCount + 2);
preTransformClipRect(channelCount + 0);
preTransformClipRect(channelCount + 1);
@@ -1413,7 +1455,7 @@ void ed::EditorContext::End()
UpdateAnimations();
- drawList->ChannelsMerge();
+ m_DrawList->ChannelsMerge();
// #debug
// drawList->AddRectFilled(ImVec2(-10.0f, -10.0f), ImVec2(10.0f, 10.0f), IM_COL32(255, 0, 255, 255));
@@ -1423,17 +1465,18 @@ void ed::EditorContext::End()
if (m_IsCanvasVisible)
m_Canvas.End();
- ImDrawList_SwapSplitter(drawList, m_Splitter);
+ ImDrawList_SwapSplitter(m_DrawList, m_Splitter);
// Draw border
{
auto& style = ImGui::GetStyle();
auto borderShadoColor = style.Colors[ImGuiCol_BorderShadow];
auto borderColor = style.Colors[ImGuiCol_Border];
- drawList->AddRect(m_Canvas.Rect().Min + ImVec2(1, 1), m_Canvas.Rect().Max - ImVec2(1, 1), ImColor(borderShadoColor));
- drawList->AddRect(m_Canvas.Rect().Min, m_Canvas.Rect().Max, ImColor(borderColor));
+ m_DrawList->AddRect(m_Canvas.Rect().Min + ImVec2(1, 1), m_Canvas.Rect().Max - ImVec2(1, 1), ImColor(borderShadoColor));
+ m_DrawList->AddRect(m_Canvas.Rect().Min, m_Canvas.Rect().Max, ImColor(borderColor));
}
+ // #metrics
// ShowMetrics(control);
ImGui::PopID();
@@ -1452,6 +1495,7 @@ void ed::EditorContext::End()
if (m_Settings.m_IsDirty && !m_CurrentAction)
SaveSettings();
+ m_DrawList = nullptr;
m_IsFirstFrame = false;
}
@@ -1497,6 +1541,26 @@ void ed::EditorContext::SetNodePosition(NodeId nodeId, const ImVec2& position)
}
}
+void ed::EditorContext::SetGroupSize(NodeId nodeId, const ImVec2& size)
+{
+ auto node = FindNode(nodeId);
+ if (!node)
+ {
+ node = CreateNode(nodeId);
+ node->m_IsLive = false;
+ }
+
+ node->m_Type = NodeType::Group;
+
+ if (node->m_GroupBounds.GetSize() != size)
+ {
+ node->m_GroupBounds.Min = node->m_Bounds.Min;
+ node->m_GroupBounds.Max = node->m_Bounds.Min + size;
+ node->m_GroupBounds.Floor();
+ MakeDirty(NodeEditor::SaveReasonFlags::Size, node);
+ }
+}
+
ImVec2 ed::EditorContext::GetNodePosition(NodeId nodeId)
{
auto node = FindNode(nodeId);
@@ -1515,21 +1579,59 @@ ImVec2 ed::EditorContext::GetNodeSize(NodeId nodeId)
return node->m_Bounds.GetSize();
}
+void ed::EditorContext::SetNodeZPosition(NodeId nodeId, float z)
+{
+ auto node = FindNode(nodeId);
+ if (!node)
+ {
+ node = CreateNode(nodeId);
+ node->m_IsLive = false;
+ }
+
+ node->m_ZPosition = z;
+}
+
+float ed::EditorContext::GetNodeZPosition(NodeId nodeId)
+{
+ auto node = FindNode(nodeId);
+ if (!node)
+ return 0.0f;
+
+ return node->m_ZPosition;
+}
+
void ed::EditorContext::MarkNodeToRestoreState(Node* node)
{
node->m_RestoreState = true;
}
-void ed::EditorContext::RestoreNodeState(Node* node)
+void ed::EditorContext::UpdateNodeState(Node* node)
{
+ bool tryLoadState = node->m_RestoreState;
+
+ node->m_RestoreState = false;
+
auto settings = m_Settings.FindNode(node->m_ID);
if (!settings)
return;
- // Load state from config (if possible)
- if (!NodeSettings::Parse(m_Config.LoadNode(node->m_ID), *settings))
+ if (!tryLoadState && settings->m_WasUsed)
return;
+ if (!settings->m_WasUsed)
+ {
+ MakeDirty(SaveReasonFlags::AddNode, node);
+ settings->m_WasUsed = true;
+ }
+
+ // Load state from config (if possible)
+ if (tryLoadState)
+ {
+ NodeSettings newSettings = *settings;
+ if (NodeSettings::Parse(m_Config.LoadNode(node->m_ID), newSettings))
+ *settings = newSettings;
+ }
+
node->m_Bounds.Min = settings->m_Location;
node->m_Bounds.Max = node->m_Bounds.Min + settings->m_Size;
node->m_Bounds.Floor();
@@ -1538,6 +1640,15 @@ void ed::EditorContext::RestoreNodeState(Node* node)
node->m_GroupBounds.Floor();
}
+void ed::EditorContext::RemoveSettings(Object* object)
+{
+ if (auto node = object->AsNode())
+ {
+ m_Settings.RemoveNode(node->m_ID);
+ MakeDirty(SaveReasonFlags::RemoveNode, node);
+ }
+}
+
void ed::EditorContext::ClearSelection()
{
m_SelectedObjects.clear();
@@ -1637,6 +1748,68 @@ void ed::EditorContext::FindLinksInRect(const ImRect& r, vector<Link*>& result,
result.push_back(link);
}
+bool ed::EditorContext::HasAnyLinks(NodeId nodeId) const
+{
+ for (auto link : m_Links)
+ {
+ if (!link->m_IsLive)
+ continue;
+
+ if (link->m_StartPin->m_Node->m_ID == nodeId || link->m_EndPin->m_Node->m_ID == nodeId)
+ return true;
+ }
+
+ return false;
+}
+
+bool ed::EditorContext::HasAnyLinks(PinId pinId) const
+{
+ for (auto link : m_Links)
+ {
+ if (!link->m_IsLive)
+ continue;
+
+ if (link->m_StartPin->m_ID == pinId || link->m_EndPin->m_ID == pinId)
+ return true;
+ }
+
+ return false;
+}
+
+int ed::EditorContext::BreakLinks(NodeId nodeId)
+{
+ int result = 0;
+ for (auto link : m_Links)
+ {
+ if (!link->m_IsLive)
+ continue;
+
+ if (link->m_StartPin->m_Node->m_ID == nodeId || link->m_EndPin->m_Node->m_ID == nodeId)
+ {
+ if (GetItemDeleter().Add(link))
+ ++result;
+ }
+ }
+ return result;
+}
+
+int ed::EditorContext::BreakLinks(PinId pinId)
+{
+ int result = 0;
+ for (auto link : m_Links)
+ {
+ if (!link->m_IsLive)
+ continue;
+
+ if (link->m_StartPin->m_ID == pinId || link->m_EndPin->m_ID == pinId)
+ {
+ if (GetItemDeleter().Add(link))
+ ++result;
+ }
+ }
+ return result;
+}
+
void ed::EditorContext::FindLinksForNode(NodeId nodeId, vector<Link*>& result, bool add)
{
if (!add)
@@ -1669,24 +1842,24 @@ void ed::EditorContext::NotifyLinkDeleted(Link* link)
void ed::EditorContext::Suspend(SuspendFlags flags)
{
- auto drawList = ImGui::GetWindowDrawList();
- auto lastChannel = drawList->_Splitter._Current;
- drawList->ChannelsSetCurrent(m_ExternalChannel);
+ IM_ASSERT(m_DrawList != nullptr && "Suspend was called outiside of Begin/End.");
+ auto lastChannel = m_DrawList->_Splitter._Current;
+ m_DrawList->ChannelsSetCurrent(m_ExternalChannel);
m_Canvas.Suspend();
- drawList->ChannelsSetCurrent(lastChannel);
+ m_DrawList->ChannelsSetCurrent(lastChannel);
if ((flags & SuspendFlags::KeepSplitter) != SuspendFlags::KeepSplitter)
- ImDrawList_SwapSplitter(drawList, m_Splitter);
+ ImDrawList_SwapSplitter(m_DrawList, m_Splitter);
}
void ed::EditorContext::Resume(SuspendFlags flags)
{
- auto drawList = ImGui::GetWindowDrawList();
+ IM_ASSERT(m_DrawList != nullptr && "Reasume was called outiside of Begin/End.");
if ((flags & SuspendFlags::KeepSplitter) != SuspendFlags::KeepSplitter)
- ImDrawList_SwapSplitter(drawList, m_Splitter);
- auto lastChannel = drawList->_Splitter._Current;
- drawList->ChannelsSetCurrent(m_ExternalChannel);
+ ImDrawList_SwapSplitter(m_DrawList, m_Splitter);
+ auto lastChannel = m_DrawList->_Splitter._Current;
+ m_DrawList->ChannelsSetCurrent(m_ExternalChannel);
m_Canvas.Resume();
- drawList->ChannelsSetCurrent(lastChannel);
+ m_DrawList->ChannelsSetCurrent(lastChannel);
}
bool ed::EditorContext::IsSuspended()
@@ -1694,9 +1867,39 @@ bool ed::EditorContext::IsSuspended()
return m_Canvas.IsSuspended();
}
-bool ed::EditorContext::IsActive()
+bool ed::EditorContext::IsFocused()
+{
+ return m_IsFocused;
+}
+
+bool ed::EditorContext::IsHovered() const
+{
+ return m_IsHovered;
+}
+
+bool ed::EditorContext::IsHoveredWithoutOverlapp() const
{
- return m_IsWindowActive;
+ return m_IsHoveredWithoutOverlapp;
+}
+
+bool ed::EditorContext::CanAcceptUserInput() const
+{
+ return m_IsFocused && m_IsHovered;
+}
+
+int ed::EditorContext::CountLiveNodes() const
+{
+ return (int)std::count_if(m_Nodes.begin(), m_Nodes.end(), [](const Node* node) { return node->m_IsLive; });
+}
+
+int ed::EditorContext::CountLivePins() const
+{
+ return (int)std::count_if(m_Pins.begin(), m_Pins.end(), [](const Pin* pin) { return pin->m_IsLive; });
+}
+
+int ed::EditorContext::CountLiveLinks() const
+{
+ return (int)std::count_if(m_Links.begin(), m_Links.end(), [](const Link* link) { return link->m_IsLive; });
}
ed::Pin* ed::EditorContext::CreatePin(PinId id, PinKind kind)
@@ -1719,23 +1922,10 @@ ed::Node* ed::EditorContext::CreateNode(NodeId id)
if (!settings)
settings = m_Settings.AddNode(id);
- if (!settings->m_WasUsed)
- {
- settings->m_WasUsed = true;
- RestoreNodeState(node);
- }
-
- node->m_Bounds.Min = settings->m_Location;
- node->m_Bounds.Max = node->m_Bounds.Min;
- node->m_Bounds.Floor();
+ UpdateNodeState(node);
if (settings->m_GroupSize.x > 0 || settings->m_GroupSize.y > 0)
- {
- node->m_Type = NodeType::Group;
- node->m_GroupBounds.Min = settings->m_Location;
- node->m_GroupBounds.Max = node->m_GroupBounds.Min + settings->m_GroupSize;
- node->m_GroupBounds.Floor();
- }
+ node->m_Type = NodeType::Group;
node->m_IsLive = false;
@@ -1852,8 +2042,15 @@ void ed::EditorContext::LoadSettings()
{
ed::Settings::Parse(m_Config.Load(), m_Settings);
- m_NavigateAction.m_Scroll = m_Settings.m_ViewScroll;
- m_NavigateAction.m_Zoom = m_Settings.m_ViewZoom;
+ if (ImRect_IsEmpty(m_Settings.m_VisibleRect))
+ {
+ m_NavigateAction.m_Scroll = m_Settings.m_ViewScroll;
+ m_NavigateAction.m_Zoom = m_Settings.m_ViewZoom;
+ }
+ else
+ {
+ m_NavigateAction.NavigateTo(m_Settings.m_VisibleRect, NavigateAction::ZoomMode::Exact, 0.0f);
+ }
}
void ed::EditorContext::SaveSettings()
@@ -1879,8 +2076,9 @@ void ed::EditorContext::SaveSettings()
for (auto& object : m_SelectedObjects)
m_Settings.m_Selection.push_back(object->ID());
- m_Settings.m_ViewScroll = m_NavigateAction.m_Scroll;
- m_Settings.m_ViewZoom = m_NavigateAction.m_Zoom;
+ m_Settings.m_ViewScroll = m_NavigateAction.m_Scroll;
+ m_Settings.m_ViewZoom = m_NavigateAction.m_Zoom;
+ m_Settings.m_VisibleRect = m_NavigateAction.m_VisibleRect;
if (m_Config.Save(m_Settings.Serialize(), m_Settings.m_DirtyReason))
m_Settings.ClearDirty();
@@ -1918,6 +2116,26 @@ ImU32 ed::EditorContext::GetColor(StyleColor colorIndex, float alpha) const
return ImColor(color.x, color.y, color.z, color.w * alpha);
}
+int ed::EditorContext::GetNodeIds(NodeId* nodes, int size) const
+{
+ if (size <= 0)
+ return 0;
+
+ int result = 0;
+ for (auto node : m_Nodes)
+ {
+ if (!node->m_IsLive)
+ continue;
+
+ *nodes++ = node->m_ID;
+ ++result;
+ if (--size == 0)
+ break;
+ }
+
+ return result;
+}
+
void ed::EditorContext::RegisterAnimation(Animation* animation)
{
m_LiveAnimations.push_back(animation);
@@ -1943,9 +2161,9 @@ void ed::EditorContext::UpdateAnimations()
}
}
-void ed::EditorContext::Flow(Link* link)
+void ed::EditorContext::Flow(Link* link, FlowDirection direction)
{
- m_FlowAnimationController.Flow(link);
+ m_FlowAnimationController.Flow(link, direction);
}
void ed::EditorContext::SetUserContext(bool globalSpace)
@@ -1963,8 +2181,7 @@ void ed::EditorContext::SetUserContext(bool globalSpace)
if (!IsSuspended())
{
- auto drawList = ImGui::GetWindowDrawList();
- drawList->ChannelsSetCurrent(c_UserChannel_Content);
+ m_DrawList->ChannelsSetCurrent(c_UserChannel_Content);
}
// #debug
@@ -1983,8 +2200,14 @@ bool ed::EditorContext::AreShortcutsEnabled()
ed::Control ed::EditorContext::BuildControl(bool allowOffscreen)
{
- if (!allowOffscreen && !ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
- return Control(nullptr, nullptr, nullptr, nullptr, false, false, false, false);
+ m_IsHovered = false;
+ m_IsHoveredWithoutOverlapp = false;
+
+ const auto windowHovered = ImGui::IsWindowHovered();
+ const auto widgetHovered = ImGui::IsMouseHoveringRect(m_Canvas.ViewRect().Min, m_Canvas.ViewRect().Max, true);
+
+ if (!allowOffscreen && !windowHovered && !widgetHovered)
+ return Control();
const auto mousePos = ImGui::GetMousePos();
@@ -2005,8 +2228,37 @@ ed::Control ed::EditorContext::BuildControl(bool allowOffscreen)
Object* clickedObject = nullptr;
Object* doubleClickedObject = nullptr;
+ ImGuiButtonFlags extraFlags = ImGuiButtonFlags_None;
+ extraFlags |= ImGuiButtonFlags_MouseButtonLeft;
+ extraFlags |= ImGuiButtonFlags_MouseButtonRight;
+ extraFlags |= ImGuiButtonFlags_MouseButtonMiddle;
+
+ static auto invisibleButtonEx = [](const char* str_id, const ImVec2& size_arg, ImGuiButtonFlags extraFlags)
+ {
+ using namespace ImGui;
+
+ ImGuiWindow* window = GetCurrentWindow();
+ if (window->SkipItems)
+ return false;
+
+ if (size_arg.x == 0.0f || size_arg.y == 0.0f)
+ return false;
+
+ const ImGuiID id = window->GetID(str_id);
+ ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);
+ const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
+ ItemSize(size);
+ if (!ItemAdd(bb, id))
+ return false;
+
+ bool hovered, held;
+ bool pressed = ButtonBehavior(bb, id, &hovered, &held, extraFlags);
+
+ return pressed;
+ };
+
// Emits invisible button and returns true if it is clicked.
- auto emitInteractiveArea = [](ObjectId id, const ImRect& rect)
+ auto emitInteractiveArea = [extraFlags](ObjectId id, const ImRect& rect)
{
char idString[33] = { 0 }; // itoa can output 33 bytes maximum
snprintf(idString, 32, "%p", id.AsPointer());
@@ -2015,20 +2267,20 @@ ed::Control ed::EditorContext::BuildControl(bool allowOffscreen)
// debug
//if (id < 0) return ImGui::Button(idString, to_imvec(rect.size));
- auto result = ImGui::InvisibleButton(idString, rect.GetSize());
+ auto result = invisibleButtonEx(idString, rect.GetSize(), extraFlags);
// #debug
- //ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(0, 255, 0, 64));
+ //m_DrawList->AddRectFilled(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(0, 255, 0, 64));
return result;
};
// Check input interactions over area.
- auto checkInteractionsInArea = [&emitInteractiveArea, &hotObject, &activeObject, &clickedObject, &doubleClickedObject](ObjectId id, const ImRect& rect, Object* object)
+ auto checkInteractionsInArea = [this, &emitInteractiveArea, &hotObject, &activeObject, &clickedObject, &doubleClickedObject](ObjectId id, const ImRect& rect, Object* object)
{
if (emitInteractiveArea(id, rect))
clickedObject = object;
- if (!doubleClickedObject && ImGui::IsMouseDoubleClicked(0) && ImGui::IsItemHovered())
+ if (!doubleClickedObject && ImGui::IsMouseDoubleClicked(m_Config.DragButtonIndex) && ImGui::IsItemHovered())
doubleClickedObject = object;
if (!hotObject && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
@@ -2100,7 +2352,7 @@ ed::Control ed::EditorContext::BuildControl(bool allowOffscreen)
// Check for interaction with background.
auto backgroundClicked = emitInteractiveArea(NodeId(0), editorRect);
- auto backgroundDoubleClicked = !doubleClickedObject && ImGui::IsItemHovered() ? ImGui::IsMouseDoubleClicked(0) : false;
+ auto backgroundDoubleClicked = !doubleClickedObject && ImGui::IsItemHovered() ? ImGui::IsMouseDoubleClicked(m_Config.DragButtonIndex) : false;
auto isBackgroundActive = ImGui::IsItemActive();
auto isBackgroundHot = !hotObject;
auto isDragging = ImGui::IsMouseDragging(0, 1) || ImGui::IsMouseDragging(1, 1) || ImGui::IsMouseDragging(2, 1);
@@ -2140,6 +2392,16 @@ ed::Control ed::EditorContext::BuildControl(bool allowOffscreen)
backgroundDoubleClicked = false;
}
+ m_IsHovered = ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly);
+ m_IsHoveredWithoutOverlapp = ImGui::IsItemHovered();
+ if (!allowOffscreen && !m_IsHovered)
+ return Control();
+
+# if IMGUI_VERSION_NUM >= 17909
+ if (m_IsHoveredWithoutOverlapp)
+ ImGui::SetItemUsingMouseWheel();
+# endif
+
return Control(hotObject, activeObject, clickedObject, doubleClickedObject,
isBackgroundHot, isBackgroundActive, backgroundClicked, backgroundDoubleClicked);
}
@@ -2177,9 +2439,9 @@ void ed::EditorContext::ShowMetrics(const Control& control)
return "<unknown>";
};
- auto liveNodeCount = (int)std::count_if(m_Nodes.begin(), m_Nodes.end(), [](Node* node) { return node->m_IsLive; });
- auto livePinCount = (int)std::count_if(m_Pins.begin(), m_Pins.end(), [](Pin* pin) { return pin->m_IsLive; });
- auto liveLinkCount = (int)std::count_if(m_Links.begin(), m_Links.end(), [](Link* link) { return link->m_IsLive; });
+ auto liveNodeCount = CountLiveNodes();
+ auto livePinCount = CountLivePins();
+ auto liveLinkCount = CountLiveLinks();
auto canvasRect = m_Canvas.Rect();
auto viewRect = m_Canvas.ViewRect();
@@ -2188,7 +2450,10 @@ void ed::EditorContext::ShowMetrics(const Control& control)
ImGui::SetCursorScreenPos(canvasRect.Min + ImVec2(5, 5));
ImGui::BeginGroup();
- ImGui::Text("Is Editor Active: %s", ImGui::IsWindowHovered() ? "true" : "false");
+ ImGui::Text("Is Focused: %s", m_IsFocused ? "true" : "false");
+ ImGui::Text("Is Hovered: %s", m_IsHovered ? "true" : "false");
+ ImGui::Text("Is Hovered (without overlapp): %s", m_IsHoveredWithoutOverlapp ? "true" : "false");
+ ImGui::Text("Accept Input: %s", CanAcceptUserInput() ? "true" : "false");
ImGui::Text("View Position: { x=%g y=%g }", viewRect.Min.x, viewRect.Min.y);
ImGui::Text("View Size: { w=%g h=%g }", viewRect.GetWidth(), viewRect.GetHeight());
ImGui::Text("Canvas Size: { w=%g h=%g }", canvasRect.GetWidth(), canvasRect.GetHeight());
@@ -2320,6 +2585,15 @@ ed::NodeSettings* ed::Settings::FindNode(NodeId id)
return nullptr;
}
+void ed::Settings::RemoveNode(NodeId id)
+{
+ auto node = FindNode(id);
+ if (!node)
+ return;
+
+ *node = NodeSettings(id);
+}
+
void ed::Settings::ClearDirty(Node* node)
{
if (node)
@@ -2384,6 +2658,10 @@ std::string ed::Settings::Serialize()
view["scroll"]["x"] = m_ViewScroll.x;
view["scroll"]["y"] = m_ViewScroll.y;
view["zoom"] = m_ViewZoom;
+ view["visible_rect"]["min"]["x"] = m_VisibleRect.Min.x;
+ view["visible_rect"]["min"]["y"] = m_VisibleRect.Min.y;
+ view["visible_rect"]["max"]["x"] = m_VisibleRect.Max.x;
+ view["visible_rect"]["max"]["y"] = m_VisibleRect.Max.y;
return result.dump();
}
@@ -2475,6 +2753,9 @@ bool ed::Settings::Parse(const std::string& string, Settings& settings)
result.m_ViewScroll = ImVec2(0, 0);
result.m_ViewZoom = viewZoomValue.is_number() ? static_cast<float>(viewZoomValue.get<double>()) : 1.0f;
+
+ if (!viewValue.contains("visible_rect") || !tryParseVector(viewValue["visible_rect"]["min"], result.m_VisibleRect.Min) || !tryParseVector(viewValue["visible_rect"]["max"], result.m_VisibleRect.Max))
+ result.m_VisibleRect = {};
}
settings = std::move(result);
@@ -2519,7 +2800,7 @@ void ed::Animation::Play(float duration)
Editor->RegisterAnimation(this);
if (duration == 0.0f)
- Stop();
+ Finish();
}
void ed::Animation::Stop()
@@ -2662,6 +2943,8 @@ void ed::FlowAnimation::Draw(ImDrawList* drawList)
UpdatePath();
m_Offset = fmodf(m_Offset, m_MarkerDistance);
+ if (m_Offset < 0)
+ m_Offset += m_MarkerDistance;
const auto progress = GetProgress();
@@ -2725,7 +3008,7 @@ void ed::FlowAnimation::ClearPath()
m_PathLength = 0.0f;
}
-ImVec2 ed::FlowAnimation::SamplePath(float distance)
+ImVec2 ed::FlowAnimation::SamplePath(float distance) const
{
//distance = ImMax(0.0f, std::min(distance, PathLength));
@@ -2773,7 +3056,7 @@ ed::FlowAnimationController::~FlowAnimationController()
delete animation;
}
-void ed::FlowAnimationController::Flow(Link* link)
+void ed::FlowAnimationController::Flow(Link* link, FlowDirection direction)
{
if (!link || !link->m_IsLive)
return;
@@ -2782,7 +3065,11 @@ void ed::FlowAnimationController::Flow(Link* link)
auto animation = GetOrCreate(link);
- animation->Flow(link, editorStyle.FlowMarkerDistance, editorStyle.FlowSpeed, editorStyle.FlowDuration);
+ float speedDirection = 1.0f;
+ if (direction == FlowDirection::Backward)
+ speedDirection = -1.0f;
+
+ animation->Flow(link, editorStyle.FlowMarkerDistance, editorStyle.FlowSpeed * speedDirection, editorStyle.FlowDuration);
}
void ed::FlowAnimationController::Draw(ImDrawList* drawList)
@@ -2843,6 +3130,7 @@ ed::NavigateAction::NavigateAction(EditorContext* editor, ImGuiEx::Canvas& canva
EditorAction(editor),
m_IsActive(false),
m_Zoom(1),
+ m_VisibleRect(),
m_Scroll(0, 0),
m_ScrollStart(0, 0),
m_ScrollDelta(0, 0),
@@ -2854,7 +3142,7 @@ ed::NavigateAction::NavigateAction(EditorContext* editor, ImGuiEx::Canvas& canva
m_LastSelectionId(0),
m_LastObject(nullptr),
m_MovingOverEdge(false),
- m_MoveOffset(0, 0)
+ m_MoveScreenOffset(0, 0)
{
}
@@ -2865,19 +3153,19 @@ ed::EditorAction::AcceptResult ed::NavigateAction::Accept(const Control& control
if (m_IsActive)
return False;
- if (ImGui::IsWindowHovered() /*&& !ImGui::IsAnyItemActive()*/ && ImGui::IsMouseDragging(c_ScrollButtonIndex, 0.0f))
+ if (Editor->CanAcceptUserInput() /*&& !ImGui::IsAnyItemActive()*/ && ImGui::IsMouseDragging(Editor->GetConfig().NavigateButtonIndex, 0.0f))
{
m_IsActive = true;
m_ScrollStart = m_Scroll;
- m_ScrollDelta = ImGui::GetMouseDragDelta(c_ScrollButtonIndex);
+ m_ScrollDelta = ImGui::GetMouseDragDelta(Editor->GetConfig().NavigateButtonIndex);
m_Scroll = m_ScrollStart - m_ScrollDelta * m_Zoom;
}
auto& io = ImGui::GetIO();
- if (ImGui::IsWindowFocused() && ImGui::IsKeyPressed(GetKeyIndexForF()) && Editor->AreShortcutsEnabled())
+ if (Editor->CanAcceptUserInput() && ImGui::IsKeyPressed(GetKeyIndexForF()) && Editor->AreShortcutsEnabled())
{
- const auto allowZoomIn = io.KeyShift;
+ const auto zoomMode = io.KeyShift ? NavigateAction::ZoomMode::WithMargin : NavigateAction::ZoomMode::None;
auto findHotObjectToZoom = [this, &control, &io]() -> Object*
{
@@ -2901,21 +3189,21 @@ ed::EditorAction::AcceptResult ed::NavigateAction::Accept(const Control& control
bool navigateToContent = false;
if (!Editor->GetSelectedObjects().empty())
{
- if (m_Reason != NavigationReason::Selection || m_LastSelectionId != Editor->GetSelectionId() || allowZoomIn)
+ if (m_Reason != NavigationReason::Selection || m_LastSelectionId != Editor->GetSelectionId() || (zoomMode != NavigateAction::ZoomMode::None))
{
m_LastSelectionId = Editor->GetSelectionId();
- NavigateTo(Editor->GetSelectionBounds(), allowZoomIn, -1.0f, NavigationReason::Selection);
+ NavigateTo(Editor->GetSelectionBounds(), zoomMode, -1.0f, NavigationReason::Selection);
}
else
navigateToContent = true;
}
else if(auto hotObject = findHotObjectToZoom())
{
- if (m_Reason != NavigationReason::Object || m_LastObject != hotObject || allowZoomIn)
+ if (m_Reason != NavigationReason::Object || m_LastObject != hotObject || (zoomMode != NavigateAction::ZoomMode::None))
{
m_LastObject = hotObject;
auto bounds = hotObject->GetBounds();
- NavigateTo(bounds, allowZoomIn, -1.0f, NavigationReason::Object);
+ NavigateTo(bounds, zoomMode, -1.0f, NavigationReason::Object);
}
else
navigateToContent = true;
@@ -2924,12 +3212,19 @@ ed::EditorAction::AcceptResult ed::NavigateAction::Accept(const Control& control
navigateToContent = true;
if (navigateToContent)
- NavigateTo(Editor->GetContentBounds(), true, -1.0f, NavigationReason::Content);
+ NavigateTo(Editor->GetContentBounds(), NavigateAction::ZoomMode::WithMargin, -1.0f, NavigationReason::Content);
+ }
+
+ auto visibleRect = GetViewRect();
+ if (m_VisibleRect.Min != visibleRect.Min || m_VisibleRect.Max != visibleRect.Max)
+ {
+ m_VisibleRect = visibleRect;
+ Editor->MakeDirty(SaveReasonFlags::Navigation);
}
// // #debug
- // if (auto drawList = ImGui::GetWindowDrawList())
- // drawList->AddCircleFilled(io.MousePos, 4.0f, IM_COL32(255, 0, 255, 255));
+ // if (m_DrawList)
+ // m_DrawList->AddCircleFilled(io.MousePos, 4.0f, IM_COL32(255, 0, 255, 255));
if (HandleZoom(control))
return True;
@@ -2944,11 +3239,11 @@ bool ed::NavigateAction::Process(const Control& control)
if (!m_IsActive)
return false;
- if (ImGui::IsMouseDragging(c_ScrollButtonIndex, 0.0f))
+ if (ImGui::IsMouseDragging(Editor->GetConfig().NavigateButtonIndex, 0.0f))
{
- m_ScrollDelta = ImGui::GetMouseDragDelta(c_ScrollButtonIndex);
+ m_ScrollDelta = ImGui::GetMouseDragDelta(Editor->GetConfig().NavigateButtonIndex);
m_Scroll = m_ScrollStart - m_ScrollDelta * m_Zoom;
-
+ m_VisibleRect = GetViewRect();
// if (IsActive && Animation.IsPlaying())
// Animation.Target = Animation.Target - ScrollDelta * Animation.TargetZoom;
}
@@ -2975,7 +3270,7 @@ bool ed::NavigateAction::HandleZoom(const Control& control)
auto& io = ImGui::GetIO();
- if (!io.MouseWheel || (!allowOffscreen && !ImGui::IsWindowHovered()))// && !ImGui::IsAnyItemActive())
+ if (!io.MouseWheel || (!allowOffscreen && !Editor->IsHoveredWithoutOverlapp()))// && !ImGui::IsAnyItemActive())
return false;
auto savedScroll = m_Scroll;
@@ -2997,10 +3292,13 @@ bool ed::NavigateAction::HandleZoom(const Control& control)
auto offset = (canvasPos - mousePos) * m_Zoom;
auto targetScroll = m_Scroll - offset;
- if (m_Scroll != savedScroll || m_Zoom != savedZoom)
+ auto visibleRect = GetViewRect();
+
+ if (m_Scroll != savedScroll || m_Zoom != savedZoom || m_VisibleRect.Min != visibleRect.Min || m_VisibleRect.Max != visibleRect.Max)
{
- m_Scroll = savedScroll;
- m_Zoom = savedZoom;
+ m_Scroll = savedScroll;
+ m_Zoom = savedZoom;
+ m_VisibleRect = visibleRect;
Editor->MakeDirty(SaveReasonFlags::Navigation);
}
@@ -3020,9 +3318,14 @@ void ed::NavigateAction::ShowMetrics()
ImGui::Text(" Active: %s", m_IsActive ? "yes" : "no");
ImGui::Text(" Scroll: { x=%g y=%g }", m_Scroll.x, m_Scroll.y);
ImGui::Text(" Zoom: %g", m_Zoom);
+ ImGui::Text(" Visible Rect: { l=%g t=%g, r=%g b=%g w=%g h=%g }",
+ m_VisibleRect.Min.x, m_VisibleRect.Min.y,
+ m_VisibleRect.Max.x, m_VisibleRect.Max.y,
+ m_VisibleRect.Max.x - m_VisibleRect.Min.x,
+ m_VisibleRect.Max.y - m_VisibleRect.Min.y);
}
-void ed::NavigateAction::NavigateTo(const ImRect& bounds, bool zoomIn, float duration, NavigationReason reason)
+void ed::NavigateAction::NavigateTo(const ImRect& bounds, ZoomMode zoomMode, float duration, NavigationReason reason)
{
if (ImRect_IsEmpty(bounds))
return;
@@ -3030,7 +3333,7 @@ void ed::NavigateAction::NavigateTo(const ImRect& bounds, bool zoomIn, float dur
if (duration < 0.0f)
duration = GetStyle().ScrollDuration;
- if (!zoomIn)
+ if (zoomMode == ZoomMode::None)
{
auto viewRect = m_Canvas.ViewRect();
auto viewRectCenter = viewRect.GetCenter();
@@ -3045,8 +3348,12 @@ void ed::NavigateAction::NavigateTo(const ImRect& bounds, bool zoomIn, float dur
// Grow rect by 5% to leave some reasonable margin
// from the edges of the canvas.
auto rect = bounds;
- auto extend = ImMax(rect.GetWidth(), rect.GetHeight());
- rect.Expand(extend * c_NavigationZoomMargin * 0.5f);
+
+ if (zoomMode == ZoomMode::WithMargin)
+ {
+ auto extend = ImMax(rect.GetWidth(), rect.GetHeight());
+ rect.Expand(extend * c_NavigationZoomMargin * 0.5f);
+ }
NavigateTo(rect, duration, reason);
}
@@ -3069,28 +3376,38 @@ void ed::NavigateAction::FinishNavigation()
m_Animation.Finish();
}
-bool ed::NavigateAction::MoveOverEdge()
+bool ed::NavigateAction::MoveOverEdge(const ImVec2& canvasSize)
{
// Don't interrupt non-edge animations
if (m_Animation.IsPlaying())
return false;
- auto& io = ImGui::GetIO();
- const auto screenRect = m_Canvas.ViewRect();
- const auto screenMousePos = io.MousePos;
+ auto& io = ImGui::GetIO();
+
+ const auto screenMousePos = io.MousePos;
+ const auto screenRect = ImRect(ImGui::GetCursorScreenPos(), ImGui::GetCursorScreenPos() + canvasSize);
// Mouse is over screen, do nothing
if (screenRect.Contains(screenMousePos))
return false;
- const auto screenPointOnEdge = ImRect_ClosestPoint(screenRect, screenMousePos, true);
- const auto direction = screenPointOnEdge - screenMousePos;
- const auto offset = -direction * io.DeltaTime * 10.0f;
+ // Several backend move mouse position to -FLT_MAX to indicate
+ // uninitialized/unknown state. To prevent all sorts
+ // of math problems, we just ignore such state.
+ if (screenMousePos.x <= -FLT_MAX || screenMousePos.y <= -FLT_MAX)
+ return false;
+
+ const auto minDistance = ImVec2(-c_MaxMoveOverEdgeDistance, -c_MaxMoveOverEdgeDistance);
+ const auto maxDistance = ImVec2( c_MaxMoveOverEdgeDistance, c_MaxMoveOverEdgeDistance);
- m_Scroll = m_Scroll + offset;
+ const auto screenPointOnEdge = ImRect_ClosestPoint(screenRect, screenMousePos, true);
+ const auto offset = ImMin(ImMax(screenPointOnEdge - screenMousePos, minDistance), maxDistance);
+ const auto relativeOffset = -offset * io.DeltaTime * c_MaxMoveOverEdgeSpeed;
- m_MoveOffset = offset;
- m_MovingOverEdge = true;
+ m_Scroll = m_Scroll + relativeOffset;
+
+ m_MoveScreenOffset = relativeOffset;
+ m_MovingOverEdge = true;
return true;
}
@@ -3101,8 +3418,8 @@ void ed::NavigateAction::StopMoveOverEdge()
{
Editor->MakeDirty(SaveReasonFlags::Navigation);
- m_MoveOffset = ImVec2(0, 0);
- m_MovingOverEdge = false;
+ m_MoveScreenOffset = ImVec2(0, 0);
+ m_MovingOverEdge = false;
}
}
@@ -3217,7 +3534,7 @@ ed::EditorAction::AcceptResult ed::SizeAction::Accept(const Control& control)
if (m_IsActive)
return False;
- if (control.ActiveNode && IsGroup(control.ActiveNode) && ImGui::IsMouseDragging(0, 0))
+ if (control.ActiveNode && IsGroup(control.ActiveNode) && ImGui::IsMouseDragging(Editor->GetConfig().DragButtonIndex, 0))
{
//const auto mousePos = to_point(ImGui::GetMousePos());
//const auto closestPoint = control.ActiveNode->Bounds.get_closest_point_hollow(mousePos, static_cast<int>(control.ActiveNode->Rounding));
@@ -3389,7 +3706,7 @@ ed::EditorAction::AcceptResult ed::DragAction::Accept(const Control& control)
if (m_IsActive)
return False;
- if (control.ActiveObject && ImGui::IsMouseDragging(0))
+ if (Editor->CanAcceptUserInput() && control.ActiveObject && ImGui::IsMouseDragging(Editor->GetConfig().DragButtonIndex))
{
if (!control.ActiveObject->AcceptDrag())
return False;
@@ -3457,7 +3774,7 @@ bool ed::DragAction::Process(const Control& control)
if (control.ActiveObject == m_DraggedObject)
{
- auto dragOffset = ImGui::GetMouseDragDelta(0, 0.0f);
+ auto dragOffset = ImGui::GetMouseDragDelta(Editor->GetConfig().DragButtonIndex, 0.0f);
auto draggedOrigin = m_DraggedObject->DragStartLocation();
auto alignPivot = ImVec2(0, 0);
@@ -3564,7 +3881,7 @@ ed::EditorAction::AcceptResult ed::SelectAction::Accept(const Control& control)
m_SelectedObjectsAtStart.clear();
- if (control.BackgroundActive && ImGui::IsMouseDragging(0, 1))
+ if (Editor->CanAcceptUserInput() && control.BackgroundHot && ImGui::IsMouseDragging(Editor->GetConfig().SelectButtonIndex, 1))
{
m_IsActive = true;
m_StartPoint = ImGui::GetMousePos();
@@ -3628,7 +3945,7 @@ bool ed::SelectAction::Process(const Control& control)
if (!m_IsActive)
return false;
- if (ImGui::IsMouseDragging(0, 0))
+ if (ImGui::IsMouseDragging(Editor->GetConfig().SelectButtonIndex, 0))
{
m_EndPoint = ImGui::GetMousePos();
@@ -3729,9 +4046,9 @@ ed::ContextMenuAction::ContextMenuAction(EditorContext* editor):
ed::EditorAction::AcceptResult ed::ContextMenuAction::Accept(const Control& control)
{
- const auto isPressed = ImGui::IsMouseClicked(1);
- const auto isReleased = ImGui::IsMouseReleased(1);
- const auto isDragging = ImGui::IsMouseDragging(1);
+ const auto isPressed = ImGui::IsMouseClicked(Editor->GetConfig().ContextMenuButtonIndex);
+ const auto isReleased = ImGui::IsMouseReleased(Editor->GetConfig().ContextMenuButtonIndex);
+ const auto isDragging = ImGui::IsMouseDragging(Editor->GetConfig().ContextMenuButtonIndex);
if (isPressed || isReleased || isDragging)
{
@@ -3874,7 +4191,7 @@ ed::ShortcutAction::ShortcutAction(EditorContext* editor):
ed::EditorAction::AcceptResult ed::ShortcutAction::Accept(const Control& control)
{
- if (!Editor->IsActive() || !Editor->AreShortcutsEnabled())
+ if (!Editor->IsFocused() || !Editor->AreShortcutsEnabled())
return False;
Action candidateAction = None;
@@ -4097,7 +4414,7 @@ ed::EditorAction::AcceptResult ed::CreateItemAction::Accept(const Control& contr
if (m_IsActive)
return EditorAction::False;
- if (control.ActivePin && ImGui::IsMouseDragging(0))
+ if (control.ActivePin && ImGui::IsMouseDragging(Editor->GetConfig().DragButtonIndex))
{
m_DraggedPin = control.ActivePin;
DragStart(m_DraggedPin);
@@ -4151,7 +4468,7 @@ bool ed::CreateItemAction::Process(const Control& control)
else
DropNothing();
- auto drawList = ImGui::GetWindowDrawList();
+ auto drawList = Editor->GetDrawList();
drawList->ChannelsSetCurrent(c_LinkChannel_NewLink);
candidate.UpdateEndpoints();
@@ -4159,7 +4476,7 @@ bool ed::CreateItemAction::Process(const Control& control)
}
else if (m_CurrentStage == Possible || !control.ActivePin)
{
- if (!ImGui::IsWindowHovered())
+ if (!Editor->CanAcceptUserInput())
{
m_DraggedPin = nullptr;
DropNothing();
@@ -4234,7 +4551,7 @@ bool ed::CreateItemAction::Begin()
if (m_CurrentStage == None)
return false;
- m_LastChannel = ImGui::GetWindowDrawList()->_Splitter._Current;
+ m_LastChannel = Editor->GetDrawList()->_Splitter._Current;
return true;
}
@@ -4248,9 +4565,9 @@ void ed::CreateItemAction::End()
ImGui::PopClipRect();
Editor->Resume(SuspendFlags::KeepSplitter);
- auto currentChannel = ImGui::GetWindowDrawList()->_Splitter._Current;
+ auto currentChannel = Editor->GetDrawList()->_Splitter._Current;
if (currentChannel != m_LastChannel)
- ImGui::GetWindowDrawList()->ChannelsSetCurrent(m_LastChannel);
+ Editor->GetDrawList()->ChannelsSetCurrent(m_LastChannel);
m_IsInGlobalSpace = false;
}
@@ -4408,6 +4725,20 @@ ed::DeleteItemsAction::DeleteItemsAction(EditorContext* editor):
{
}
+void ed::DeleteItemsAction::DeleteDeadLinks(NodeId nodeId)
+{
+ vector<ed::Link*> links;
+ Editor->FindLinksForNode(nodeId, links, true);
+ for (auto link : links)
+ {
+ auto it = std::find(m_CandidateObjects.begin(), m_CandidateObjects.end(), link);
+ if (it != m_CandidateObjects.end())
+ continue;
+
+ m_CandidateObjects.push_back(link);
+ }
+}
+
ed::EditorAction::AcceptResult ed::DeleteItemsAction::Accept(const Control& control)
{
IM_ASSERT(!m_IsActive);
@@ -4415,33 +4746,13 @@ ed::EditorAction::AcceptResult ed::DeleteItemsAction::Accept(const Control& cont
if (m_IsActive)
return False;
- auto addDeadLinks = [this]()
- {
- vector<ed::Link*> links;
- for (auto object : m_CandidateObjects)
- {
- auto node = object->AsNode();
- if (!node)
- continue;
-
- Editor->FindLinksForNode(node->m_ID, links, true);
- }
- if (!links.empty())
- {
- std::sort(links.begin(), links.end());
- links.erase(std::unique(links.begin(), links.end()), links.end());
- m_CandidateObjects.insert(m_CandidateObjects.end(), links.begin(), links.end());
- }
- };
-
auto& io = ImGui::GetIO();
- if (ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete)) && Editor->AreShortcutsEnabled())
+ if (Editor->CanAcceptUserInput() && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete)) && Editor->AreShortcutsEnabled())
{
auto& selection = Editor->GetSelectedObjects();
if (!selection.empty())
{
m_CandidateObjects = selection;
- addDeadLinks();
m_IsActive = true;
return True;
}
@@ -4458,7 +4769,6 @@ ed::EditorAction::AcceptResult ed::DeleteItemsAction::Accept(const Control& cont
{
m_CandidateObjects = m_ManuallyDeletedObjects;
m_ManuallyDeletedObjects.clear();
- addDeadLinks();
m_IsActive = true;
return True;
}
@@ -4612,14 +4922,14 @@ bool ed::DeleteItemsAction::QueryItem(ObjectId* itemId, IteratorType itemType)
return false;
}
-bool ed::DeleteItemsAction::AcceptItem()
+bool ed::DeleteItemsAction::AcceptItem(bool deleteDependencies)
{
if (!m_InInteraction)
return false;
m_UserAction = Accepted;
- RemoveItem();
+ RemoveItem(deleteDependencies);
return true;
}
@@ -4631,16 +4941,21 @@ void ed::DeleteItemsAction::RejectItem()
m_UserAction = Rejected;
- RemoveItem();
+ RemoveItem(false);
}
-void ed::DeleteItemsAction::RemoveItem()
+void ed::DeleteItemsAction::RemoveItem(bool deleteDependencies)
{
auto item = m_CandidateObjects[m_CandidateItemIndex];
m_CandidateObjects.erase(m_CandidateObjects.begin() + m_CandidateItemIndex);
Editor->DeselectObject(item);
+ Editor->RemoveSettings(item);
+
+ if (deleteDependencies && m_CurrentItemType == Node)
+ DeleteDeadLinks(item->ID().AsNodeId());
+
if (m_CurrentItemType == Link)
Editor->NotifyLinkDeleted(item->AsLink());
}
@@ -4672,11 +4987,7 @@ void ed::NodeBuilder::Begin(NodeId nodeId)
m_CurrentNode = Editor->GetNode(nodeId);
- if (m_CurrentNode->m_RestoreState)
- {
- Editor->RestoreNodeState(m_CurrentNode);
- m_CurrentNode->m_RestoreState = false;
- }
+ Editor->UpdateNodeState(m_CurrentNode);
if (m_CurrentNode->m_CenterOnScreen)
{
@@ -4730,7 +5041,7 @@ void ed::NodeBuilder::Begin(NodeId nodeId)
m_IsGroup = false;
// Grow channel list and select user channel
- if (auto drawList = ImGui::GetWindowDrawList())
+ if (auto drawList = Editor->GetDrawList())
{
m_CurrentNode->m_Channel = drawList->_Splitter._Count;
ImDrawList_ChannelsGrow(drawList, drawList->_Splitter._Count + c_ChannelsPerNode);
@@ -4755,7 +5066,7 @@ void ed::NodeBuilder::End()
{
IM_ASSERT(nullptr != m_CurrentNode);
- if (auto drawList = ImGui::GetWindowDrawList())
+ if (auto drawList = Editor->GetDrawList())
{
IM_ASSERT(drawList->_Splitter._Count == 1); // Did you forgot to call drawList->ChannelsMerge()?
ImDrawList_SwapSplitter(drawList, m_Splitter);
@@ -4768,7 +5079,9 @@ void ed::NodeBuilder::End()
{
ImGui::EndGroup();
ImGui::SameLine(0, editorStyle.NodePadding.z);
+ ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
ImGui::Dummy(ImVec2(0, 0));
+ ImGui::PopStyleVar();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + editorStyle.NodePadding.w);
}
@@ -4832,7 +5145,7 @@ void ed::NodeBuilder::BeginPin(PinId pinId, PinKind kind)
m_ResolvePinRect = true;
m_ResolvePivot = true;
- if (auto drawList = ImGui::GetWindowDrawList())
+ if (auto drawList = Editor->GetDrawList())
{
m_PinSplitter.Clear();
ImDrawList_SwapSplitter(drawList, m_PinSplitter);
@@ -4845,7 +5158,7 @@ void ed::NodeBuilder::EndPin()
{
IM_ASSERT(nullptr != m_CurrentPin);
- if (auto drawList = ImGui::GetWindowDrawList())
+ if (auto drawList = Editor->GetDrawList())
{
IM_ASSERT(drawList->_Splitter._Count == 1); // Did you forgot to call drawList->ChannelsMerge()?
ImDrawList_SwapSplitter(drawList, m_PinSplitter);
@@ -4870,10 +5183,10 @@ void ed::NodeBuilder::EndPin()
}
// #debug: Draw pin bounds
- //ImGui::GetWindowDrawList()->AddRect(m_CurrentPin->m_Bounds.Min, m_CurrentPin->m_Bounds.Max, IM_COL32(255, 255, 0, 255));
+ //Editor->GetDrawList()->AddRect(m_CurrentPin->m_Bounds.Min, m_CurrentPin->m_Bounds.Max, IM_COL32(255, 255, 0, 255));
// #debug: Draw pin pivot rectangle
- //ImGui::GetWindowDrawList()->AddRect(m_CurrentPin->m_Pivot.Min, m_CurrentPin->m_Pivot.Max, IM_COL32(255, 0, 255, 255));
+ //Editor->GetDrawList()->AddRect(m_CurrentPin->m_Pivot.Min, m_CurrentPin->m_Pivot.Max, IM_COL32(255, 0, 255, 255));
m_CurrentPin = nullptr;
}
@@ -4945,7 +5258,7 @@ ImDrawList* ed::NodeBuilder::GetUserBackgroundDrawList(Node* node) const
{
if (node && node->m_IsLive)
{
- auto drawList = ImGui::GetWindowDrawList();
+ auto drawList = Editor->GetDrawList();
drawList->ChannelsSetCurrent(node->m_Channel + c_NodeUserBackgroundChannel);
return drawList;
}
@@ -4987,16 +5300,16 @@ bool ed::HintBuilder::Begin(NodeId nodeId)
m_CurrentNode = node;
- m_LastChannel = ImGui::GetWindowDrawList()->_Splitter._Current;
+ m_LastChannel = Editor->GetDrawList()->_Splitter._Current;
Editor->Suspend(SuspendFlags::KeepSplitter);
const auto alpha = ImMax(0.0f, std::min(1.0f, (view.Scale - c_min_zoom) / (c_max_zoom - c_min_zoom)));
- ImGui::GetWindowDrawList()->ChannelsSetCurrent(c_UserChannel_HintsBackground);
+ Editor->GetDrawList()->ChannelsSetCurrent(c_UserChannel_HintsBackground);
ImGui::PushClipRect(rect.Min + ImVec2(1, 1), rect.Max - ImVec2(1, 1), false);
- ImGui::GetWindowDrawList()->ChannelsSetCurrent(c_UserChannel_Hints);
+ Editor->GetDrawList()->ChannelsSetCurrent(c_UserChannel_Hints);
ImGui::PushClipRect(rect.Min + ImVec2(1, 1), rect.Max - ImVec2(1, 1), false);
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha);
@@ -5013,13 +5326,13 @@ void ed::HintBuilder::End()
ImGui::PopStyleVar();
- ImGui::GetWindowDrawList()->ChannelsSetCurrent(c_UserChannel_Hints);
+ Editor->GetDrawList()->ChannelsSetCurrent(c_UserChannel_Hints);
ImGui::PopClipRect();
- ImGui::GetWindowDrawList()->ChannelsSetCurrent(c_UserChannel_HintsBackground);
+ Editor->GetDrawList()->ChannelsSetCurrent(c_UserChannel_HintsBackground);
ImGui::PopClipRect();
- ImGui::GetWindowDrawList()->ChannelsSetCurrent(m_LastChannel);
+ Editor->GetDrawList()->ChannelsSetCurrent(m_LastChannel);
Editor->Resume(SuspendFlags::KeepSplitter);
@@ -5045,7 +5358,7 @@ ImDrawList* ed::HintBuilder::GetForegroundDrawList()
{
IM_ASSERT(nullptr != m_CurrentNode);
- auto drawList = ImGui::GetWindowDrawList();
+ auto drawList = Editor->GetDrawList();
drawList->ChannelsSetCurrent(c_UserChannel_Hints);
@@ -5056,7 +5369,7 @@ ImDrawList* ed::HintBuilder::GetBackgroundDrawList()
{
IM_ASSERT(nullptr != m_CurrentNode);
- auto drawList = ImGui::GetWindowDrawList();
+ auto drawList = Editor->GetDrawList();
drawList->ChannelsSetCurrent(c_UserChannel_HintsBackground);
diff --git a/3rdparty/imgui-node-editor/imgui_node_editor.h b/3rdparty/imgui-node-editor/imgui_node_editor.h
index 02282a1..e9bce90 100644
--- a/3rdparty/imgui-node-editor/imgui_node_editor.h
+++ b/3rdparty/imgui-node-editor/imgui_node_editor.h
@@ -1,4 +1,6 @@
//------------------------------------------------------------------------------
+// VERSION 0.9.1
+//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
@@ -37,7 +39,9 @@ enum class SaveReasonFlags: uint32_t
Position = 0x00000002,
Size = 0x00000004,
Selection = 0x00000008,
- User = 0x00000010
+ AddNode = 0x00000010,
+ RemoveNode = 0x00000020,
+ User = 0x00000040
};
inline SaveReasonFlags operator |(SaveReasonFlags lhs, SaveReasonFlags rhs) { return static_cast<SaveReasonFlags>(static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs)); }
@@ -61,6 +65,10 @@ struct Config
ConfigSaveNodeSettings SaveNodeSettings;
ConfigLoadNodeSettings LoadNodeSettings;
void* UserPointer;
+ int DragButtonIndex; // Mouse button index drag action will react to (0-left, 1-right, 2-middle)
+ int SelectButtonIndex; // Mouse button index select action will react to (0-left, 1-right, 2-middle)
+ int NavigateButtonIndex; // Mouse button index navigate action will react to (0-left, 1-right, 2-middle)
+ int ContextMenuButtonIndex; // Mouse button index context menu action will react to (0-left, 1-right, 2-middle)
Config()
: SettingsFile("NodeEditor.json")
@@ -71,6 +79,10 @@ struct Config
, SaveNodeSettings(nullptr)
, LoadNodeSettings(nullptr)
, UserPointer(nullptr)
+ , DragButtonIndex(0)
+ , SelectButtonIndex(0)
+ , NavigateButtonIndex(1)
+ , ContextMenuButtonIndex(1)
{
}
};
@@ -83,6 +95,12 @@ enum class PinKind
Output
};
+enum class FlowDirection
+{
+ Forward,
+ Backward
+};
+
//------------------------------------------------------------------------------
enum StyleColor
@@ -184,7 +202,11 @@ struct Style
PivotAlignment = ImVec2(0.5f, 0.5f);
PivotSize = ImVec2(0.0f, 0.0f);
PivotScale = ImVec2(1, 1);
+#if IMGUI_VERSION_NUM > 18101
+ PinCorners = ImDrawFlags_RoundCornersAll;
+#else
PinCorners = ImDrawCornerFlags_All;
+#endif
PinRadius = 0.0f;
PinArrowSize = 0.0f;
PinArrowWidth = 0.0f;
@@ -260,7 +282,7 @@ ImDrawList* GetNodeBackgroundDrawList(NodeId nodeId);
bool Link(LinkId id, PinId startPinId, PinId endPinId, const ImVec4& color = ImVec4(1, 1, 1, 1), float thickness = 1.0f);
-void Flow(LinkId linkId);
+void Flow(LinkId linkId, FlowDirection direction = FlowDirection::Forward);
bool BeginCreate(const ImVec4& color = ImVec4(1, 1, 1, 1), float thickness = 1.0f);
bool QueryNewLink(PinId* startId, PinId* endId);
@@ -276,14 +298,17 @@ void EndCreate();
bool BeginDelete();
bool QueryDeletedLink(LinkId* linkId, PinId* startId = nullptr, PinId* endId = nullptr);
bool QueryDeletedNode(NodeId* nodeId);
-bool AcceptDeletedItem();
+bool AcceptDeletedItem(bool deleteDependencies = true);
void RejectDeletedItem();
void EndDelete();
void SetNodePosition(NodeId nodeId, const ImVec2& editorPosition);
+void SetGroupSize(NodeId nodeId, const ImVec2& size);
ImVec2 GetNodePosition(NodeId nodeId);
ImVec2 GetNodeSize(NodeId nodeId);
void CenterNodeOnScreen(NodeId nodeId);
+void SetNodeZPosition(NodeId nodeId, float z); // Sets node z position, nodes with higher value are drawn over nodes with lower value
+float GetNodeZPosition(NodeId nodeId); // Returns node z position, defaults is 0.0f
void RestoreNodeState(NodeId nodeId);
@@ -297,6 +322,8 @@ bool HasSelectionChanged();
int GetSelectedObjectCount();
int GetSelectedNodes(NodeId* nodes, int size);
int GetSelectedLinks(LinkId* links, int size);
+bool IsNodeSelected(NodeId nodeId);
+bool IsLinkSelected(LinkId linkId);
void ClearSelection();
void SelectNode(NodeId nodeId, bool append = false);
void SelectLink(LinkId linkId, bool append = false);
@@ -306,6 +333,11 @@ void DeselectLink(LinkId linkId);
bool DeleteNode(NodeId nodeId);
bool DeleteLink(LinkId linkId);
+bool HasAnyLinks(NodeId nodeId); // Returns true if node has any link connected
+bool HasAnyLinks(PinId pinId); // Return true if pin has any link connected
+int BreakLinks(NodeId nodeId); // Break all links connected to this node
+int BreakLinks(PinId pinId); // Break all links connected to this pin
+
void NavigateToContent(float duration = -1);
void NavigateToSelection(bool zoomIn = false, float duration = -1);
@@ -330,20 +362,25 @@ void EndShortcut();
float GetCurrentZoom();
+NodeId GetHoveredNode();
+PinId GetHoveredPin();
+LinkId GetHoveredLink();
NodeId GetDoubleClickedNode();
PinId GetDoubleClickedPin();
LinkId GetDoubleClickedLink();
bool IsBackgroundClicked();
bool IsBackgroundDoubleClicked();
+bool GetLinkPins(LinkId linkId, PinId* startPinId, PinId* endPinId); // pass nullptr if particular pin do not interest you
+
bool PinHadAnyLinks(PinId pinId);
ImVec2 GetScreenSize();
ImVec2 ScreenToCanvas(const ImVec2& pos);
ImVec2 CanvasToScreen(const ImVec2& pos);
-
-
+int GetNodeCount(); // Returns number of submitted nodes since Begin() call
+int GetOrderedNodeIds(NodeId* nodes, int size); // Fills an array with node id's in order they're drawn; up to 'size` elements are set. Returns actual size of filled id's.
diff --git a/3rdparty/imgui-node-editor/imgui_node_editor_api.cpp b/3rdparty/imgui-node-editor/imgui_node_editor_api.cpp
index d468b4e..cc6cdc0 100644
--- a/3rdparty/imgui-node-editor/imgui_node_editor_api.cpp
+++ b/3rdparty/imgui-node-editor/imgui_node_editor_api.cpp
@@ -1,4 +1,6 @@
//------------------------------------------------------------------------------
+// VERSION 0.9.1
+//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
@@ -31,8 +33,7 @@ static int BuildIdList(C& container, I* list, int listSize, F&& accept)
{
list[count] = I(object->ID().AsPointer());
++count;
- --listSize;
- }
+ --listSize;}
}
return count;
@@ -50,12 +51,18 @@ ax::NodeEditor::EditorContext* ax::NodeEditor::CreateEditor(const Config* config
void ax::NodeEditor::DestroyEditor(EditorContext* ctx)
{
- if (GetCurrentEditor() == ctx)
- SetCurrentEditor(nullptr);
+ auto lastContext = GetCurrentEditor();
+
+ // Set context we're about to destroy as current, to give callback valid context
+ if (lastContext != ctx)
+ SetCurrentEditor(ctx);
auto editor = reinterpret_cast<ax::NodeEditor::Detail::EditorContext*>(ctx);
delete editor;
+
+ if (lastContext != ctx)
+ SetCurrentEditor(lastContext);
}
void ax::NodeEditor::SetCurrentEditor(EditorContext* ctx)
@@ -211,10 +218,10 @@ bool ax::NodeEditor::Link(LinkId id, PinId startPinId, PinId endPinId, const ImV
return s_Editor->DoLink(id, startPinId, endPinId, ImColor(color), thickness);
}
-void ax::NodeEditor::Flow(LinkId linkId)
+void ax::NodeEditor::Flow(LinkId linkId, FlowDirection direction)
{
if (auto link = s_Editor->FindLink(linkId))
- s_Editor->Flow(link);
+ s_Editor->Flow(link, direction);
}
bool ax::NodeEditor::BeginCreate(const ImVec4& color, float thickness)
@@ -341,11 +348,11 @@ bool ax::NodeEditor::QueryDeletedNode(NodeId* nodeId)
return context.QueryNode(nodeId);
}
-bool ax::NodeEditor::AcceptDeletedItem()
+bool ax::NodeEditor::AcceptDeletedItem(bool deleteDependencies)
{
auto& context = s_Editor->GetItemDeleter();
- return context.AcceptItem();
+ return context.AcceptItem(deleteDependencies);
}
void ax::NodeEditor::RejectDeletedItem()
@@ -367,6 +374,11 @@ void ax::NodeEditor::SetNodePosition(NodeId nodeId, const ImVec2& position)
s_Editor->SetNodePosition(nodeId, position);
}
+void ax::NodeEditor::SetGroupSize(NodeId nodeId, const ImVec2& size)
+{
+ s_Editor->SetGroupSize(nodeId, size);
+}
+
ImVec2 ax::NodeEditor::GetNodePosition(NodeId nodeId)
{
return s_Editor->GetNodePosition(nodeId);
@@ -383,6 +395,16 @@ void ax::NodeEditor::CenterNodeOnScreen(NodeId nodeId)
node->CenterOnScreenInNextFrame();
}
+void ax::NodeEditor::SetNodeZPosition(NodeId nodeId, float z)
+{
+ s_Editor->SetNodeZPosition(nodeId, z);
+}
+
+float ax::NodeEditor::GetNodeZPosition(NodeId nodeId)
+{
+ return s_Editor->GetNodeZPosition(nodeId);
+}
+
void ax::NodeEditor::RestoreNodeState(NodeId nodeId)
{
if (auto node = s_Editor->FindNode(nodeId))
@@ -406,7 +428,7 @@ bool ax::NodeEditor::IsSuspended()
bool ax::NodeEditor::IsActive()
{
- return s_Editor->IsActive();
+ return s_Editor->IsFocused();
}
bool ax::NodeEditor::HasSelectionChanged()
@@ -435,6 +457,22 @@ int ax::NodeEditor::GetSelectedLinks(LinkId* links, int size)
});
}
+bool ax::NodeEditor::IsNodeSelected(NodeId nodeId)
+{
+ if (auto node = s_Editor->FindNode(nodeId))
+ return s_Editor->IsSelected(node);
+ else
+ return false;
+}
+
+bool ax::NodeEditor::IsLinkSelected(LinkId linkId)
+{
+ if (auto link = s_Editor->FindLink(linkId))
+ return s_Editor->IsSelected(link);
+ else
+ return false;
+}
+
void ax::NodeEditor::ClearSelection()
{
s_Editor->ClearSelection();
@@ -490,6 +528,26 @@ bool ax::NodeEditor::DeleteLink(LinkId linkId)
return false;
}
+bool ax::NodeEditor::HasAnyLinks(NodeId nodeId)
+{
+ return s_Editor->HasAnyLinks(nodeId);
+}
+
+bool ax::NodeEditor::HasAnyLinks(PinId pinId)
+{
+ return s_Editor->HasAnyLinks(pinId);
+}
+
+int ax::NodeEditor::BreakLinks(NodeId nodeId)
+{
+ return s_Editor->BreakLinks(nodeId);
+}
+
+int ax::NodeEditor::BreakLinks(PinId pinId)
+{
+ return s_Editor->BreakLinks(pinId);
+}
+
void ax::NodeEditor::NavigateToContent(float duration)
{
s_Editor->NavigateTo(s_Editor->GetContentBounds(), true, duration);
@@ -591,6 +649,21 @@ float ax::NodeEditor::GetCurrentZoom()
return s_Editor->GetView().InvScale;
}
+ax::NodeEditor::NodeId ax::NodeEditor::GetHoveredNode()
+{
+ return s_Editor->GetHoveredNode();
+}
+
+ax::NodeEditor::PinId ax::NodeEditor::GetHoveredPin()
+{
+ return s_Editor->GetHoveredPin();
+}
+
+ax::NodeEditor::LinkId ax::NodeEditor::GetHoveredLink()
+{
+ return s_Editor->GetHoveredLink();
+}
+
ax::NodeEditor::NodeId ax::NodeEditor::GetDoubleClickedNode()
{
return s_Editor->GetDoubleClickedNode();
@@ -616,6 +689,20 @@ bool ax::NodeEditor::IsBackgroundDoubleClicked()
return s_Editor->IsBackgroundDoubleClicked();
}
+bool ax::NodeEditor::GetLinkPins(LinkId linkId, PinId* startPinId, PinId* endPinId)
+{
+ auto link = s_Editor->FindLink(linkId);
+ if (!link)
+ return false;
+
+ if (startPinId)
+ *startPinId = link->m_StartPin->m_ID;
+ if (endPinId)
+ *endPinId = link->m_EndPin->m_ID;
+
+ return true;
+}
+
bool ax::NodeEditor::PinHadAnyLinks(PinId pinId)
{
return s_Editor->PinHadAnyLinks(pinId);
@@ -635,3 +722,13 @@ ImVec2 ax::NodeEditor::CanvasToScreen(const ImVec2& pos)
{
return s_Editor->ToScreen(pos);
}
+
+int ax::NodeEditor::GetNodeCount()
+{
+ return s_Editor->CountLiveNodes();
+}
+
+int ax::NodeEditor::GetOrderedNodeIds(NodeId* nodes, int size)
+{
+ return s_Editor->GetNodeIds(nodes, size);
+}
diff --git a/3rdparty/imgui-node-editor/imgui_node_editor_internal.h b/3rdparty/imgui-node-editor/imgui_node_editor_internal.h
index cef1bd5..90fc885 100644
--- a/3rdparty/imgui-node-editor/imgui_node_editor_internal.h
+++ b/3rdparty/imgui-node-editor/imgui_node_editor_internal.h
@@ -1,4 +1,6 @@
//------------------------------------------------------------------------------
+// VERSION 0.9.1
+//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
@@ -372,6 +374,7 @@ struct Node final: Object
NodeId m_ID;
NodeType m_Type;
ImRect m_Bounds;
+ float m_ZPosition;
int m_Channel;
Pin* m_LastPin;
ImVec2 m_DragStart;
@@ -395,6 +398,7 @@ struct Node final: Object
, m_ID(id)
, m_Type(NodeType::Node)
, m_Bounds()
+ , m_ZPosition(0.0f)
, m_Channel(0)
, m_LastPin(nullptr)
, m_DragStart()
@@ -515,17 +519,20 @@ struct Settings
vector<ObjectId> m_Selection;
ImVec2 m_ViewScroll;
float m_ViewZoom;
+ ImRect m_VisibleRect;
Settings()
: m_IsDirty(false)
, m_DirtyReason(SaveReasonFlags::None)
, m_ViewScroll(0, 0)
, m_ViewZoom(1.0f)
+ , m_VisibleRect()
{
}
NodeSettings* AddNode(NodeId id);
NodeSettings* FindNode(NodeId id);
+ void RemoveNode(NodeId id);
void ClearDirty(Node* node = nullptr);
void MakeDirty(SaveReasonFlags reason, Node* node = nullptr);
@@ -558,6 +565,11 @@ struct Control
bool BackgroundClicked;
bool BackgroundDoubleClicked;
+ Control()
+ : Control(nullptr, nullptr, nullptr, nullptr, false, false, false, false)
+ {
+ }
+
Control(Object* hotObject, Object* activeObject, Object* clickedObject, Object* doubleClickedObject,
bool backgroundHot, bool backgroundActive, bool backgroundClicked, bool backgroundDoubleClicked)
: HotObject(hotObject)
@@ -706,7 +718,7 @@ private:
void UpdatePath();
void ClearPath();
- ImVec2 SamplePath(float distance);
+ ImVec2 SamplePath(float distance) const;
void OnUpdate(float progress) override final;
void OnStop() override final;
@@ -736,7 +748,7 @@ struct FlowAnimationController final : AnimationController
FlowAnimationController(EditorContext* editor);
virtual ~FlowAnimationController();
- void Flow(Link* link);
+ void Flow(Link* link, FlowDirection direction = FlowDirection::Forward);
virtual void Draw(ImDrawList* drawList) override final;
@@ -786,6 +798,13 @@ struct EditorAction
struct NavigateAction final: EditorAction
{
+ enum class ZoomMode
+ {
+ None,
+ Exact,
+ WithMargin
+ };
+
enum class NavigationReason
{
Unknown,
@@ -798,6 +817,7 @@ struct NavigateAction final: EditorAction
bool m_IsActive;
float m_Zoom;
+ ImRect m_VisibleRect;
ImVec2 m_Scroll;
ImVec2 m_ScrollStart;
ImVec2 m_ScrollDelta;
@@ -813,16 +833,18 @@ struct NavigateAction final: EditorAction
virtual NavigateAction* AsNavigate() override final { return this; }
- void NavigateTo(const ImRect& bounds, bool zoomIn, float duration = -1.0f, NavigationReason reason = NavigationReason::Unknown);
+ void NavigateTo(const ImRect& bounds, ZoomMode zoomMode, float duration = -1.0f, NavigationReason reason = NavigationReason::Unknown);
void StopNavigation();
void FinishNavigation();
- bool MoveOverEdge();
+ bool MoveOverEdge(const ImVec2& canvasSize);
void StopMoveOverEdge();
bool IsMovingOverEdge() const { return m_MovingOverEdge; }
- ImVec2 GetMoveOffset() const { return m_MoveOffset; }
+ ImVec2 GetMoveScreenOffset() const { return m_MoveScreenOffset; }
void SetWindow(ImVec2 position, ImVec2 size);
+ ImVec2 GetWindowScreenPos() const { return m_WindowScreenPos; };
+ ImVec2 GetWindowScreenSize() const { return m_WindowScreenSize; };
ImGuiEx::CanvasView GetView() const;
ImVec2 GetViewOrigin() const;
@@ -833,15 +855,15 @@ struct NavigateAction final: EditorAction
private:
ImGuiEx::Canvas& m_Canvas;
- ImVec2 m_WindowScreenPos;
- ImVec2 m_WindowScreenSize;
+ ImVec2 m_WindowScreenPos;
+ ImVec2 m_WindowScreenSize;
NavigateAnimation m_Animation;
NavigationReason m_Reason;
uint64_t m_LastSelectionId;
Object* m_LastObject;
bool m_MovingOverEdge;
- ImVec2 m_MoveOffset;
+ ImVec2 m_MoveScreenOffset;
bool HandleZoom(const Control& control);
@@ -1107,15 +1129,17 @@ struct DeleteItemsAction final: EditorAction
bool QueryLink(LinkId* linkId, PinId* startId = nullptr, PinId* endId = nullptr);
bool QueryNode(NodeId* nodeId);
- bool AcceptItem();
+ bool AcceptItem(bool deleteDependencies);
void RejectItem();
private:
enum IteratorType { Unknown, Link, Node };
enum UserAction { Undetermined, Accepted, Rejected };
+ void DeleteDeadLinks(NodeId nodeId);
+
bool QueryItem(ObjectId* itemId, IteratorType itemType);
- void RemoveItem();
+ void RemoveItem(bool deleteDependencies);
vector<Object*> m_ManuallyDeletedObjects;
@@ -1249,6 +1273,8 @@ struct EditorContext
EditorContext(const ax::NodeEditor::Config* config = nullptr);
~EditorContext();
+ const Config& GetConfig() const { return m_Config; }
+
Style& GetStyle() { return m_Style; }
void Begin(const char* id, const ImVec2& size = ImVec2(0, 0));
@@ -1272,11 +1298,17 @@ struct EditorContext
const ImRect& GetRect() const { return m_Canvas.Rect(); }
void SetNodePosition(NodeId nodeId, const ImVec2& screenPosition);
+ void SetGroupSize(NodeId nodeId, const ImVec2& size);
ImVec2 GetNodePosition(NodeId nodeId);
ImVec2 GetNodeSize(NodeId nodeId);
+ void SetNodeZPosition(NodeId nodeId, float z);
+ float GetNodeZPosition(NodeId nodeId);
+
void MarkNodeToRestoreState(Node* node);
- void RestoreNodeState(Node* node);
+ void UpdateNodeState(Node* node);
+
+ void RemoveSettings(Object* object);
void ClearSelection();
void SelectObject(Object* object);
@@ -1294,6 +1326,12 @@ struct EditorContext
void FindNodesInRect(const ImRect& r, vector<Node*>& result, bool append = false, bool includeIntersecting = true);
void FindLinksInRect(const ImRect& r, vector<Link*>& result, bool append = false);
+ bool HasAnyLinks(NodeId nodeId) const;
+ bool HasAnyLinks(PinId pinId) const;
+
+ int BreakLinks(NodeId nodeId);
+ int BreakLinks(PinId pinId);
+
void FindLinksForNode(NodeId nodeId, vector<Link*>& result, bool add = false);
bool PinHadAnyLinks(PinId pinId);
@@ -1307,11 +1345,18 @@ struct EditorContext
void Resume(SuspendFlags flags = SuspendFlags::None);
bool IsSuspended();
- bool IsActive();
+ bool IsFocused();
+ bool IsHovered() const;
+ bool IsHoveredWithoutOverlapp() const;
+ bool CanAcceptUserInput() const;
void MakeDirty(SaveReasonFlags reason);
void MakeDirty(SaveReasonFlags reason, Node* node);
+ int CountLiveNodes() const;
+ int CountLivePins() const;
+ int CountLiveLinks() const;
+
Pin* CreatePin(PinId id, PinKind kind);
Node* CreateNode(NodeId id);
Link* CreateLink(LinkId id);
@@ -1363,18 +1408,27 @@ struct EditorContext
ImU32 GetColor(StyleColor colorIndex) const;
ImU32 GetColor(StyleColor colorIndex, float alpha) const;
- void NavigateTo(const ImRect& bounds, bool zoomIn = false, float duration = -1) { m_NavigateAction.NavigateTo(bounds, zoomIn, duration); }
+ int GetNodeIds(NodeId* nodes, int size) const;
+
+ void NavigateTo(const ImRect& bounds, bool zoomIn = false, float duration = -1)
+ {
+ auto zoomMode = zoomIn ? NavigateAction::ZoomMode::WithMargin : NavigateAction::ZoomMode::None;
+ m_NavigateAction.NavigateTo(bounds, zoomMode, duration);
+ }
void RegisterAnimation(Animation* animation);
void UnregisterAnimation(Animation* animation);
- void Flow(Link* link);
+ void Flow(Link* link, FlowDirection direction);
void SetUserContext(bool globalSpace = false);
void EnableShortcuts(bool enable);
bool AreShortcutsEnabled();
+ NodeId GetHoveredNode() const { return m_HoveredNode; }
+ PinId GetHoveredPin() const { return m_HoveredPin; }
+ LinkId GetHoveredLink() const { return m_HoveredLink; }
NodeId GetDoubleClickedNode() const { return m_DoubleClickedNode; }
PinId GetDoubleClickedPin() const { return m_DoubleClickedPin; }
LinkId GetDoubleClickedLink() const { return m_DoubleClickedLink; }
@@ -1394,6 +1448,8 @@ struct EditorContext
return ImVec2(AlignPointToGrid(p.x), AlignPointToGrid(p.y));
}
+ ImDrawList* GetDrawList() { return m_DrawList; }
+
private:
void LoadSettings();
void SaveSettings();
@@ -1405,7 +1461,9 @@ private:
void UpdateAnimations();
bool m_IsFirstFrame;
- bool m_IsWindowActive;
+ bool m_IsFocused;
+ bool m_IsHovered;
+ bool m_IsHoveredWithoutOverlapp;
bool m_ShortcutsEnabled;
@@ -1444,6 +1502,9 @@ private:
vector<AnimationController*> m_AnimationControllers;
FlowAnimationController m_FlowAnimationController;
+ NodeId m_HoveredNode;
+ PinId m_HoveredPin;
+ LinkId m_HoveredLink;
NodeId m_DoubleClickedNode;
PinId m_DoubleClickedPin;
LinkId m_DoubleClickedLink;
@@ -1455,6 +1516,7 @@ private:
Config m_Config;
+ ImDrawList* m_DrawList;
int m_ExternalChannel;
ImDrawListSplitter m_Splitter;
};
diff --git a/3rdparty/imgui-node-editor/imgui_node_editor_internal.inl b/3rdparty/imgui-node-editor/imgui_node_editor_internal.inl
index 0d16231..7280fa1 100644
--- a/3rdparty/imgui-node-editor/imgui_node_editor_internal.inl
+++ b/3rdparty/imgui-node-editor/imgui_node_editor_internal.inl
@@ -1,4 +1,6 @@
//------------------------------------------------------------------------------
+// VERSION 0.9.1
+//
// LICENSE
// This software is dual-licensed to the public domain and under the following
// license: you are granted a perpetual, irrevocable license to copy, modify,
diff --git a/3rdparty/imgui-node-editor/thedmd-imgui-node-editor.stamp b/3rdparty/imgui-node-editor/thedmd-imgui-node-editor.stamp
index 8c5bdc7..c47e4cc 100644
--- a/3rdparty/imgui-node-editor/thedmd-imgui-node-editor.stamp
+++ b/3rdparty/imgui-node-editor/thedmd-imgui-node-editor.stamp
@@ -1,2 +1,2 @@
# This file labels the commit which our source is from
-687a72f940c76cf5064e13fe55fa0408c18fcbe4
+612eaea68977c0dda2b334c46784136e6f4f819a