push missing files

pie menu test
This commit is contained in:
sshumakov3
2020-11-28 12:45:56 +03:00
parent 18cd05afa2
commit 0d89f12875
11 changed files with 958 additions and 0 deletions

View File

@@ -228,6 +228,7 @@ INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src/external/PNG2BLP/libtxc_dxtn" )
INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src/external/PNG2BLP/pngpp" )
INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src/external/PNG2BLP/zlib" )
INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src/external/imguizmo" )
INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src/external/imguipiemenu" )
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
COLLECT_FILES(false noggit_root_sources src/noggit .cpp)
@@ -237,6 +238,7 @@ COLLECT_FILES(false opengl_sources src/opengl .cpp)
COLLECT_FILES(true red_sources src/noggit/Red .cpp)
COLLECT_FILES(true png_blp_sources src/external/PNG2BLP ".c;.cpp;")
COLLECT_FILES(false imguizmo_sources src/external/imguizmo ".c;.cpp;")
COLLECT_FILES(false imguipiemenu_sources src/external/imguipiemenu ".c;.cpp;")
set ( util_sources
src/util/exception_to_string.cpp
)
@@ -248,6 +250,7 @@ COLLECT_FILES(false shaders src/glsl .glsl)
COLLECT_FILES(true red_headers src/noggit/Red ".hpp;.inl")
COLLECT_FILES(true png_blp_headers src/external/PNG2BLP ".h;.hpp;")
COLLECT_FILES(false imguizmo_headers src/external/imguizmo ".h;.hpp;")
COLLECT_FILES(false imguipiemenu_headers src/external/imguipiemenu ".h;.hpp;")
IF(WIN32)
set ( os_sources
@@ -301,6 +304,7 @@ ADD_EXECUTABLE ( noggit
${red_sources}
${png_blp_sources}
${imguizmo_sources}
${imguipiemenu_sources}
${noggit_root_headers}
${noggit_ui_headers}
${opengl_headers}
@@ -313,6 +317,7 @@ ADD_EXECUTABLE ( noggit
${moced}
${red_headers}
${imguizmo_headers}
${imguipiemenu_headers}
${compiled_resource_files}
${compiled_ui_files}
${shaders}

356
src/external/imguipiemenu/PieMenu.cpp vendored Normal file
View File

@@ -0,0 +1,356 @@
#include "PieMenu.hpp"
#include <external/qtimgui/imgui/imgui.h>
#include <external/qtimgui/imgui/imgui_internal.h>
struct PieMenuContext
{
static const int c_iMaxPieMenuStack = 8;
static const int c_iMaxPieItemCount = 12;
static const int c_iRadiusEmpty = 30;
static const int c_iRadiusMin = 30;
static const int c_iMinItemCount = 3;
static const int c_iMinItemCountPerLevel = 3;
struct PieMenu
{
int m_iCurrentIndex;
float m_fMaxItemSqrDiameter;
float m_fLastMaxItemSqrDiameter;
int m_iHoveredItem;
int m_iLastHoveredItem;
int m_iClickedItem;
bool m_oItemIsSubMenu[c_iMaxPieItemCount];
ImVector<char> m_oItemNames[c_iMaxPieItemCount];
ImVec2 m_oItemSizes[c_iMaxPieItemCount];
};
PieMenuContext()
{
m_iCurrentIndex = -1;
m_iLastFrame = 0;
}
PieMenu m_oPieMenuStack[c_iMaxPieMenuStack];
int m_iCurrentIndex;
int m_iMaxIndex;
int m_iLastFrame;
ImVec2 m_oCenter;
int m_iMouseButton;
bool m_bClose;
};
static PieMenuContext s_oPieMenuContext;
bool IsPopupOpen(const char* pName)
{
ImGuiID iID = ImGui::GetID(pName);
auto& g = *ImGui::GetCurrentContext();
return g.OpenPopupStack.Size && g.OpenPopupStack[g.OpenPopupStack.Size - 1].PopupId == iID;
}
void BeginPieMenuEx()
{
IM_ASSERT(s_oPieMenuContext.m_iCurrentIndex < PieMenuContext::c_iMaxPieMenuStack);
++s_oPieMenuContext.m_iCurrentIndex;
++s_oPieMenuContext.m_iMaxIndex;
PieMenuContext::PieMenu& oPieMenu = s_oPieMenuContext.m_oPieMenuStack[s_oPieMenuContext.m_iCurrentIndex];
oPieMenu.m_iCurrentIndex = 0;
oPieMenu.m_fMaxItemSqrDiameter = 0.f;
if( !ImGui::IsMouseReleased( s_oPieMenuContext.m_iMouseButton ) )
oPieMenu.m_iHoveredItem = -1;
if (s_oPieMenuContext.m_iCurrentIndex > 0)
oPieMenu.m_fMaxItemSqrDiameter = s_oPieMenuContext.m_oPieMenuStack[s_oPieMenuContext.m_iCurrentIndex - 1].m_fMaxItemSqrDiameter;
}
void EndPieMenuEx()
{
IM_ASSERT(s_oPieMenuContext.m_iCurrentIndex >= 0);
PieMenuContext::PieMenu& oPieMenu = s_oPieMenuContext.m_oPieMenuStack[s_oPieMenuContext.m_iCurrentIndex];
--s_oPieMenuContext.m_iCurrentIndex;
}
bool BeginPiePopup(const char* pName, int iMouseButton)
{
if (IsPopupOpen(pName))
{
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 1.0f);
s_oPieMenuContext.m_iMouseButton = iMouseButton;
s_oPieMenuContext.m_bClose = false;
ImGui::SetNextWindowPos( ImVec2( -100.f, -100.f ), ImGuiCond_Appearing );
bool bOpened = ImGui::BeginPopup(pName);
if (bOpened)
{
int iCurrentFrame = ImGui::GetFrameCount();
if (s_oPieMenuContext.m_iLastFrame < (iCurrentFrame - 1))
{
s_oPieMenuContext.m_oCenter = ImGui::GetIO().MousePos;
}
s_oPieMenuContext.m_iLastFrame = iCurrentFrame;
s_oPieMenuContext.m_iMaxIndex = -1;
BeginPieMenuEx();
return true;
}
else
{
ImGui::End();
ImGui::PopStyleColor(2);
ImGui::PopStyleVar(2);
}
}
return false;
}
void EndPiePopup()
{
EndPieMenuEx();
ImGuiStyle& oStyle = ImGui::GetStyle();
ImDrawList* pDrawList = ImGui::GetWindowDrawList();
pDrawList->PushClipRectFullScreen();
const ImVec2 oMousePos = ImGui::GetIO().MousePos;
const ImVec2 oDragDelta = ImVec2(oMousePos.x - s_oPieMenuContext.m_oCenter.x, oMousePos.y - s_oPieMenuContext.m_oCenter.y);
const float fDragDistSqr = oDragDelta.x*oDragDelta.x + oDragDelta.y*oDragDelta.y;
float fCurrentRadius = (float)PieMenuContext::c_iRadiusEmpty;
ImRect oArea = ImRect( s_oPieMenuContext.m_oCenter, s_oPieMenuContext.m_oCenter );
bool bItemHovered = false;
const float c_fDefaultRotate = -IM_PI / 2.f;
float fLastRotate = c_fDefaultRotate;
for (int iIndex = 0; iIndex <= s_oPieMenuContext.m_iMaxIndex; ++iIndex)
{
PieMenuContext::PieMenu& oPieMenu = s_oPieMenuContext.m_oPieMenuStack[iIndex];
float fMenuHeight = sqrt(oPieMenu.m_fMaxItemSqrDiameter);
const float fMinRadius = fCurrentRadius;
const float fMaxRadius = fMinRadius + (fMenuHeight * oPieMenu.m_iCurrentIndex) / ( 2.f );
const float item_arc_span = 2 * IM_PI / ImMax(PieMenuContext::c_iMinItemCount + PieMenuContext::c_iMinItemCountPerLevel * iIndex, oPieMenu.m_iCurrentIndex);
float drag_angle = atan2f(oDragDelta.y, oDragDelta.x);
float fRotate = fLastRotate - item_arc_span * ( oPieMenu.m_iCurrentIndex - 1.f ) / 2.f;
int item_hovered = -1;
for( int item_n = 0; item_n < oPieMenu.m_iCurrentIndex; item_n++ )
{
const char* item_label = oPieMenu.m_oItemNames[ item_n ].Data;
const float inner_spacing = oStyle.ItemInnerSpacing.x / fMinRadius / 2;
const float fMinInnerSpacing = oStyle.ItemInnerSpacing.x / ( fMinRadius * 2.f );
const float fMaxInnerSpacing = oStyle.ItemInnerSpacing.x / ( fMaxRadius * 2.f );
const float item_inner_ang_min = item_arc_span * ( item_n - 0.5f + fMinInnerSpacing ) + fRotate;
const float item_inner_ang_max = item_arc_span * ( item_n + 0.5f - fMinInnerSpacing ) + fRotate;
const float item_outer_ang_min = item_arc_span * ( item_n - 0.5f + fMaxInnerSpacing ) + fRotate;
const float item_outer_ang_max = item_arc_span * ( item_n + 0.5f - fMaxInnerSpacing ) + fRotate;
bool hovered = false;
if( fDragDistSqr >= fMinRadius * fMinRadius && fDragDistSqr < fMaxRadius * fMaxRadius )
{
while( ( drag_angle - item_inner_ang_min ) < 0.f )
drag_angle += 2.f * IM_PI;
while( ( drag_angle - item_inner_ang_min ) > 2.f * IM_PI )
drag_angle -= 2.f * IM_PI;
if( drag_angle >= item_inner_ang_min && drag_angle < item_inner_ang_max )
{
hovered = true;
bItemHovered = !oPieMenu.m_oItemIsSubMenu[ item_n ];
}
}
int arc_segments = (int)( 32 * item_arc_span / ( 2 * IM_PI ) ) + 1;
ImU32 iColor = hovered ? ImColor( 100, 100, 150 ) : ImColor( 70, 70, 70 );
iColor = ImGui::GetColorU32( hovered ? ImGuiCol_HeaderHovered : ImGuiCol_FrameBg );
iColor = ImGui::GetColorU32( hovered ? ImGuiCol_Button : ImGuiCol_ButtonHovered );
//iColor |= 0xFF000000;
const float fAngleStepInner = (item_inner_ang_max - item_inner_ang_min) / arc_segments;
const float fAngleStepOuter = ( item_outer_ang_max - item_outer_ang_min ) / arc_segments;
pDrawList->PrimReserve(arc_segments * 6, (arc_segments + 1) * 2);
for (int iSeg = 0; iSeg <= arc_segments; ++iSeg)
{
float fCosInner = cosf(item_inner_ang_min + fAngleStepInner * iSeg);
float fSinInner = sinf(item_inner_ang_min + fAngleStepInner * iSeg);
float fCosOuter = cosf(item_outer_ang_min + fAngleStepOuter * iSeg);
float fSinOuter = sinf(item_outer_ang_min + fAngleStepOuter * iSeg);
if (iSeg < arc_segments)
{
pDrawList->PrimWriteIdx(pDrawList->_VtxCurrentIdx + 0);
pDrawList->PrimWriteIdx(pDrawList->_VtxCurrentIdx + 2);
pDrawList->PrimWriteIdx(pDrawList->_VtxCurrentIdx + 1);
pDrawList->PrimWriteIdx(pDrawList->_VtxCurrentIdx + 3);
pDrawList->PrimWriteIdx(pDrawList->_VtxCurrentIdx + 2);
pDrawList->PrimWriteIdx(pDrawList->_VtxCurrentIdx + 1);
}
pDrawList->PrimWriteVtx(ImVec2(s_oPieMenuContext.m_oCenter.x + fCosInner * (fMinRadius + oStyle.ItemInnerSpacing.x), s_oPieMenuContext.m_oCenter.y + fSinInner * (fMinRadius + oStyle.ItemInnerSpacing.x)), ImVec2(0.f, 0.f), iColor);
pDrawList->PrimWriteVtx(ImVec2(s_oPieMenuContext.m_oCenter.x + fCosOuter * (fMaxRadius - oStyle.ItemInnerSpacing.x), s_oPieMenuContext.m_oCenter.y + fSinOuter * (fMaxRadius - oStyle.ItemInnerSpacing.x)), ImVec2(0.f, 0.f), iColor);
}
float fRadCenter = ( item_arc_span * item_n ) + fRotate;
ImVec2 oOuterCenter = ImVec2( s_oPieMenuContext.m_oCenter.x + cosf( fRadCenter ) * fMaxRadius, s_oPieMenuContext.m_oCenter.y + sinf( fRadCenter ) * fMaxRadius );
oArea.Add( oOuterCenter );
if (oPieMenu.m_oItemIsSubMenu[item_n])
{
ImVec2 oTrianglePos[3];
float fRadLeft = fRadCenter - 5.f / fMaxRadius;
float fRadRight = fRadCenter + 5.f / fMaxRadius;
oTrianglePos[ 0 ].x = s_oPieMenuContext.m_oCenter.x + cosf( fRadCenter ) * ( fMaxRadius - 5.f );
oTrianglePos[ 0 ].y = s_oPieMenuContext.m_oCenter.y + sinf( fRadCenter ) * ( fMaxRadius - 5.f );
oTrianglePos[ 1 ].x = s_oPieMenuContext.m_oCenter.x + cosf( fRadLeft ) * ( fMaxRadius - 10.f );
oTrianglePos[ 1 ].y = s_oPieMenuContext.m_oCenter.y + sinf( fRadLeft ) * ( fMaxRadius - 10.f );
oTrianglePos[ 2 ].x = s_oPieMenuContext.m_oCenter.x + cosf( fRadRight ) * ( fMaxRadius - 10.f );
oTrianglePos[ 2 ].y = s_oPieMenuContext.m_oCenter.y + sinf( fRadRight ) * ( fMaxRadius - 10.f );
pDrawList->AddTriangleFilled(oTrianglePos[0], oTrianglePos[1], oTrianglePos[2], ImColor(255, 255, 255));
}
ImVec2 text_size = oPieMenu.m_oItemSizes[item_n];
ImVec2 text_pos = ImVec2(
s_oPieMenuContext.m_oCenter.x + cosf((item_inner_ang_min + item_inner_ang_max) * 0.5f) * (fMinRadius + fMaxRadius) * 0.5f - text_size.x * 0.5f,
s_oPieMenuContext.m_oCenter.y + sinf((item_inner_ang_min + item_inner_ang_max) * 0.5f) * (fMinRadius + fMaxRadius) * 0.5f - text_size.y * 0.5f);
pDrawList->AddText(text_pos, ImColor(255, 255, 255), item_label);
if (hovered)
item_hovered = item_n;
}
fCurrentRadius = fMaxRadius;
oPieMenu.m_fLastMaxItemSqrDiameter = oPieMenu.m_fMaxItemSqrDiameter;
oPieMenu.m_iHoveredItem = item_hovered;
if (fDragDistSqr >= fMaxRadius * fMaxRadius)
item_hovered = oPieMenu.m_iLastHoveredItem;
oPieMenu.m_iLastHoveredItem = item_hovered;
fLastRotate = item_arc_span * oPieMenu.m_iLastHoveredItem + fRotate;
if( item_hovered == -1 || !oPieMenu.m_oItemIsSubMenu[item_hovered] )
break;
}
pDrawList->PopClipRect();
if( oArea.Min.x < 0.f )
{
s_oPieMenuContext.m_oCenter.x = ( s_oPieMenuContext.m_oCenter.x - oArea.Min.x );
}
if( oArea.Min.y < 0.f )
{
s_oPieMenuContext.m_oCenter.y = ( s_oPieMenuContext.m_oCenter.y - oArea.Min.y );
}
ImVec2 oDisplaySize = ImGui::GetIO().DisplaySize;
if( oArea.Max.x > oDisplaySize.x )
{
s_oPieMenuContext.m_oCenter.x = ( s_oPieMenuContext.m_oCenter.x - oArea.Max.x ) + oDisplaySize.x;
}
if( oArea.Max.y > oDisplaySize.y )
{
s_oPieMenuContext.m_oCenter.y = ( s_oPieMenuContext.m_oCenter.y - oArea.Max.y ) + oDisplaySize.y;
}
if( s_oPieMenuContext.m_bClose ||
( !bItemHovered && ImGui::IsMouseReleased( s_oPieMenuContext.m_iMouseButton ) ) )
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
ImGui::PopStyleColor(2);
ImGui::PopStyleVar(2);
}
bool BeginPieMenu(const char* pName, bool bEnabled)
{
IM_ASSERT(s_oPieMenuContext.m_iCurrentIndex >= 0 && s_oPieMenuContext.m_iCurrentIndex < PieMenuContext::c_iMaxPieItemCount);
PieMenuContext::PieMenu& oPieMenu = s_oPieMenuContext.m_oPieMenuStack[s_oPieMenuContext.m_iCurrentIndex];
ImVec2 oTextSize = ImGui::CalcTextSize(pName, NULL, true);
oPieMenu.m_oItemSizes[oPieMenu.m_iCurrentIndex] = oTextSize;
float fSqrDiameter = oTextSize.x * oTextSize.x + oTextSize.y * oTextSize.y;
if (fSqrDiameter > oPieMenu.m_fMaxItemSqrDiameter)
{
oPieMenu.m_fMaxItemSqrDiameter = fSqrDiameter;
}
oPieMenu.m_oItemIsSubMenu[oPieMenu.m_iCurrentIndex] = true;
int iLen = strlen(pName);
ImVector<char>& oName = oPieMenu.m_oItemNames[oPieMenu.m_iCurrentIndex];
oName.resize(iLen + 1);
oName[iLen] = '\0';
memcpy(oName.Data, pName, iLen);
if (oPieMenu.m_iLastHoveredItem == oPieMenu.m_iCurrentIndex)
{
++oPieMenu.m_iCurrentIndex;
BeginPieMenuEx();
return true;
}
++oPieMenu.m_iCurrentIndex;
return false;
}
void EndPieMenu()
{
IM_ASSERT(s_oPieMenuContext.m_iCurrentIndex >= 0 && s_oPieMenuContext.m_iCurrentIndex < PieMenuContext::c_iMaxPieItemCount);
--s_oPieMenuContext.m_iCurrentIndex;
}
bool PieMenuItem(const char* pName, bool bEnabled)
{
IM_ASSERT(s_oPieMenuContext.m_iCurrentIndex >= 0 && s_oPieMenuContext.m_iCurrentIndex < PieMenuContext::c_iMaxPieItemCount);
PieMenuContext::PieMenu& oPieMenu = s_oPieMenuContext.m_oPieMenuStack[s_oPieMenuContext.m_iCurrentIndex];
ImVec2 oTextSize = ImGui::CalcTextSize(pName, NULL, true);
oPieMenu.m_oItemSizes[oPieMenu.m_iCurrentIndex] = oTextSize;
float fSqrDiameter = oTextSize.x * oTextSize.x + oTextSize.y * oTextSize.y;
if (fSqrDiameter > oPieMenu.m_fMaxItemSqrDiameter)
{
oPieMenu.m_fMaxItemSqrDiameter = fSqrDiameter;
}
oPieMenu.m_oItemIsSubMenu[oPieMenu.m_iCurrentIndex] = false;
int iLen = strlen(pName);
ImVector<char>& oName = oPieMenu.m_oItemNames[oPieMenu.m_iCurrentIndex];
oName.resize(iLen + 1);
oName[iLen] = '\0';
memcpy(oName.Data, pName, iLen);
bool bActive = oPieMenu.m_iCurrentIndex == oPieMenu.m_iHoveredItem;
++oPieMenu.m_iCurrentIndex;
if( bActive )
s_oPieMenuContext.m_bClose = true;
return bActive;
}

12
src/external/imguipiemenu/PieMenu.hpp vendored Normal file
View File

@@ -0,0 +1,12 @@
#ifndef NOGGIT_PIEMENU_HPP
#define NOGGIT_PIEMENU_HPP
/* Declaration */
bool BeginPiePopup( const char* pName, int iMouseButton = 0 );
void EndPiePopup();
bool PieMenuItem( const char* pName, bool bEnabled = true );
bool BeginPieMenu( const char* pName, bool bEnabled = true );
void EndPieMenu();
#endif //NOGGIT_PIEMENU_HPP

View File

@@ -39,6 +39,7 @@
#include <noggit/Red/AssetBrowser/Ui/AssetBrowser.hpp>
#include <noggit/Red/PresetEditor/Ui/PresetEditor.hpp>
#include <external/QtAdvancedDockingSystem/src/DockManager.h>
#include <external/imguipiemenu/PieMenu.hpp>
#include "revision.h"
@@ -1836,6 +1837,54 @@ void MapView::paintGL()
_world->update_selection_pivot();
ImGui::End();
/* Example */
std::string sText;
if(ImGui::IsMouseClicked( 1 ) )
{
ImGui::OpenPopup( "PieMenu" );
}
if( BeginPiePopup( "PieMenu", 1 ) )
{
if( PieMenuItem( "Test1" ) ) sText = "Test1";
if( PieMenuItem( "Test2" ) )
{
sText = "Test2";
}
if( PieMenuItem( "Test3", false ) ) sText = "Test3";
if( BeginPieMenu( "Sub" ) )
{
if( BeginPieMenu( "Sub sub\nmenu" ) )
{
if( PieMenuItem( "SubSub" ) ) sText = "SubSub";
if( PieMenuItem( "SubSub2" ) ) sText = "SubSub2";
EndPieMenu();
}
if( PieMenuItem( "TestSub" ) ) sText = "TestSub";
if( PieMenuItem( "TestSub2" ) ) sText = "TestSub2";
EndPieMenu();
}
if( BeginPieMenu( "Sub2" ) )
{
if( PieMenuItem( "TestSub" ) ) sText = "TestSub";
if( BeginPieMenu( "Sub sub\nmenu" ) )
{
if( PieMenuItem( "SubSub" ) ) sText = "SubSub";
if( PieMenuItem( "SubSub2" ) ) sText = "SubSub2";
EndPieMenu();
}
if( PieMenuItem( "TestSub2" ) ) sText = "TestSub2";
EndPieMenu();
}
EndPiePopup();
}
ImGui::ShowDemoWindow();
ImGui::ShowStyleEditor();
ImGui::Render();
}

View File

@@ -0,0 +1,154 @@
#include "BaseNode.hpp"
#include <stdexcept>
#include <QHBoxLayout>
using namespace noggit::Red::PresetEditor::Nodes;
InNodePort::InNodePort(QString const& caption_, bool caption_visible_)
: caption(caption_)
, caption_visible(caption_visible_)
{}
OutNodePort::OutNodePort(QString const& caption_, bool caption_visible_)
: caption(caption_)
, caption_visible(caption_visible_)
{}
BaseNode::BaseNode()
: NodeDataModel()
, _embedded_widget(QWidget())
{
_embedded_widget.setAttribute(Qt::WA_TranslucentBackground);
_embedded_widget_layout = new QVBoxLayout(&_embedded_widget);
_embedded_widget_layout->setContentsMargins(5, 5, 5, 5);
}
std::shared_ptr<NodeData> BaseNode::outData(PortIndex port_index)
{
return std::static_pointer_cast<NodeData>(_out_ports[port_index].out_value);
}
void BaseNode::setInData(std::shared_ptr<NodeData> data, PortIndex port_index)
{
_in_ports[port_index].in_value = data;
}
void BaseNode::addWidget(QWidget* widget, PortIndex in_port)
{
auto row_layout = new QHBoxLayout(&_embedded_widget);
row_layout->setSpacing(0);
_embedded_widget_layout->addLayout(row_layout);
auto spacer = new QLabel("", &_embedded_widget);
spacer->setMaximumWidth(0);
row_layout->addWidget(spacer);
row_layout->addWidget(widget);
if (in_port >= 0)
{
_in_ports[in_port].default_widget = widget;
}
}
void BaseNode::addWidget(QWidget* widget, QString const& label_text, PortIndex in_port)
{
auto row_layout = new QHBoxLayout(&_embedded_widget);
_embedded_widget_layout->addLayout(row_layout);
row_layout->addWidget(new QLabel(label_text, &_embedded_widget));
row_layout->addWidget(widget);
if (in_port >= 0)
{
_in_ports[in_port].default_widget = widget;
}
}
bool BaseNode::portCaptionVisible(PortType port_type, PortIndex port_index) const
{
if (port_type == PortType::In)
{
return _in_ports[port_index].caption_visible;
}
else if (port_type == PortType::Out)
{
return _out_ports[port_index].caption_visible;
}
else
{
throw std::logic_error("Incorrect port type or port type None.");
}
}
QString BaseNode::portCaption(PortType port_type, PortIndex port_index) const
{
if (port_type == PortType::In)
{
return _in_ports[port_index].caption;
}
else if (port_type == PortType::Out)
{
return _out_ports[port_index].caption;
}
else
{
throw std::logic_error("Incorrect port type or port type None.");
}
}
NodeDataType BaseNode::dataType(PortType port_type, PortIndex port_index) const
{
if (port_type == PortType::In)
{
return _in_ports[port_index].data_type->type();
}
else if (port_type == PortType::Out)
{
return _out_ports[port_index].data_type->type();
}
else
{
throw std::logic_error("Incorrect port type or port type None.");
}
}
unsigned int BaseNode::nPorts(PortType port_type) const
{
if (port_type == PortType::In)
{
return _in_ports.size();
}
else if (port_type == PortType::Out)
{
return _out_ports.size();
}
else
{
throw std::logic_error("Incorrect port type or port type None.");
}
}
QWidget* BaseNode::portDefaultValueWidget(PortIndex port_index)
{
return _in_ports[port_index].default_widget;
}
void BaseNode::inputConnectionDeleted(const Connection& connection)
{
auto default_widget = _in_ports[connection.getPortIndex(PortType::In)].default_widget;
if (default_widget)
default_widget->setVisible(true);
}
void BaseNode::inputConnectionCreated(const Connection& connection)
{
auto default_widget = _in_ports[connection.getPortIndex(PortType::In)].default_widget;
if (default_widget)
default_widget->setVisible(false);
}

View File

@@ -0,0 +1,128 @@
#ifndef NOGGIT_BASENODE_HPP
#define NOGGIT_BASENODE_HPP
#include <external/NodeEditor/include/nodes/NodeDataModel>
#include <external/NodeEditor/include/nodes/NodeData>
#include <external/NodeEditor/include/nodes/Connection>
#include <boost/variant.hpp>
#include <vector>
#include <memory>
#include <unordered_map>
#include <QObject>
#include <QJsonObject>
#include <QLabel>
#include <QLineEdit>
#include <QDoubleSpinBox>
#include <QSpinBox>
#include <QCheckBox>
#include <QVBoxLayout>
using QtNodes::PortType;
using QtNodes::PortIndex;
using QtNodes::NodeData;
using QtNodes::NodeDataType;
using QtNodes::NodeDataModel;
using QtNodes::NodeValidationState;
using QtNodes::Connection;
namespace noggit
{
namespace Red::PresetEditor::Nodes
{
struct InNodePort
{
InNodePort(QString const& caption_, bool caption_visible_);
QString caption;
bool caption_visible = true;
std::unique_ptr<NodeData> data_type;
std::weak_ptr<NodeData> in_value;
QWidget* default_widget = nullptr;
};
struct OutNodePort
{
OutNodePort(QString const& caption_, bool caption_visible_);
QString caption;
bool caption_visible = true;
std::unique_ptr<NodeData> data_type;
std::shared_ptr<NodeData> out_value;
};
class BaseNode : public NodeDataModel
{
Q_OBJECT
public:
BaseNode();
virtual ~BaseNode() {}
public:
unsigned int nPorts(PortType port_type) const override;
NodeDataType dataType(PortType port_type, PortIndex port_index) const override;
std::shared_ptr<NodeData> outData(PortIndex port) override;
void setInData(std::shared_ptr<NodeData> data, PortIndex port_index) override;
QWidget* embeddedWidget() override { return &_embedded_widget; }
NodeValidationState validationState() const override { return _validation_state; };
QString validationMessage() const override { return _validation_error; };
QString portCaption(PortType port_type, PortIndex port_index) const override;
QString name() const override { return _name; }
QString caption() const override { return _caption; };
bool portCaptionVisible(PortType port_type, PortIndex port_index) const override;
QWidget* portDefaultValueWidget(PortIndex port_index) override;
virtual void compute() = 0;
public Q_SLOTS:
void inputConnectionCreated(Connection const& connection) override;
void inputConnectionDeleted(Connection const& connection) override;
protected:
void setName(QString const& name) {_name = name;};
void setCaption(QString const& caption){_caption = caption;};
void setValidationMessage(QString const& message){_validation_error = message;};
void setValidationState(NodeValidationState state){_validation_state = state;};
void addWidget(QWidget* widget, PortIndex in_port = -1);
void addWidget(QWidget* widget, QString const& label_text, PortIndex in_port = -1);
template<typename T>
void addPort(PortType port_type, QString const& caption, bool caption_visible);
protected:
QString _name;
QString _caption;
std::vector<InNodePort> _in_ports;
std::vector<OutNodePort> _out_ports;
QWidget _embedded_widget;
QVBoxLayout* _embedded_widget_layout;
NodeValidationState _validation_state = NodeValidationState::Warning;
QString _validation_error = QString("Missing or incorrect inputs");
};
}
}
#endif //NOGGIT_BASENODE_HPP

View File

@@ -0,0 +1,25 @@
#ifndef NOGGIT_BASENODE_INL
#define NOGGIT_BASENODE_INL
#include "BaseNode.hpp"
template<typename T>
void noggit::Red::PresetEditor::Nodes::BaseNode::addPort(PortType port_type, const QString &caption, bool caption_visible)
{
if (port_type == PortType::In)
{
auto& port = _in_ports.emplace_back(caption, caption_visible);
port.data_type = std::make_unique<T>();
}
else if (port_type == PortType::Out)
{
auto& port = _out_ports.emplace_back(caption, caption_visible);
port.data_type = std::make_unique<T>();
}
else
{
throw std::logic_error("Incorrect port type or port type None.");
}
}
#endif // NOGGIT_BASENODE_INL

View File

@@ -0,0 +1,42 @@
#ifndef NOGGIT_DECIMALDATA_HPP
#define NOGGIT_DECIMALDATA_HPP
#include <external/NodeEditor/include/nodes/NodeDataModel>
using QtNodes::NodeDataType;
using QtNodes::NodeData;
/// The class can potentially incapsulate any user data which
/// need to be transferred within the Node Editor graph
class DecimalData : public NodeData
{
public:
DecimalData()
: _number(0.0)
{}
DecimalData(double const number)
: _number(number)
{}
NodeDataType type() const override
{
return NodeDataType {"decimal",
"Decimal"};
}
double number() const
{ return _number; }
QString numberAsText() const
{ return QString::number(_number, 'f'); }
private:
double _number;
};
#endif //NOGGIT_DECIMALDATA_HPP

View File

@@ -0,0 +1,40 @@
#ifndef NOGGIT_INTEGERDATA_HPP
#define NOGGIT_INTEGERDATA_HPP
#include <external/NodeEditor/include/nodes/NodeDataModel>
using QtNodes::NodeDataType;
using QtNodes::NodeData;
class IntegerData : public NodeData
{
public:
IntegerData()
: _number(0.0)
{}
IntegerData(int const number)
: _number(number)
{}
NodeDataType type() const override
{
return NodeDataType {"integer",
"Integer"};
}
int number() const
{ return _number; }
QString numberAsText() const
{ return QString::number(_number); }
private:
int _number;
};
#endif //NOGGIT_INTEGERDATA_HPP

View File

@@ -0,0 +1,104 @@
#include "MathNode.hpp"
#include "BaseNode.inl"
#include "Data/DecimalData.hpp"
#include <cmath>
using namespace noggit::Red::PresetEditor::Nodes;
MathNode::MathNode()
: BaseNode()
{
_validation_state = NodeValidationState::Valid;
_operation = new QComboBox(&_embedded_widget);
_operation->addItems({"Add",
"Subtract",
"Multiply",
"Divide",
"Modulo"});
addWidget(_operation);
setName("MathNode");
setCaption("Add");
QComboBox::connect(_operation, qOverload<int>(&QComboBox::currentIndexChanged)
,[this](int index)
{
setCaption(_operation->currentText());
}
);
addPort<DecimalData>(PortType::In, "Value", true);
_first = new QDoubleSpinBox(&_embedded_widget);
addWidget(_first, 0);
addPort<DecimalData>(PortType::In, "Value", true);
_second = new QDoubleSpinBox(&_embedded_widget);
addWidget(_second, 1);
addPort<DecimalData>(PortType::Out, "Value", true);
}
void MathNode::compute()
{
auto first_shared = _in_ports[0].in_value.lock();
auto second_shared = _in_ports[1].in_value.lock();
auto first = dynamic_cast<DecimalData*>(first_shared.get());
auto second = dynamic_cast<DecimalData*>(second_shared.get());
// handle defaults
double first_number = first ? first->number() : _first->value();
double second_number = second ? second->number() : _second->value();
setValidationState(NodeValidationState::Warning);
double result = 0.0;
switch (_operation->currentIndex())
{
case 0:
result = first_number + second_number;
break;
case 1:
result = first_number - second_number;
break;
case 2:
result = first_number * second_number;
break;
case 3:
result = first_number / second_number;
break;
case 4:
result = std::fmod(first_number, second_number);
break;
}
_out_ports[0].out_value = std::make_shared<DecimalData>(result);
setValidationMessage(("Debug: " + std::to_string(result)).c_str());
Q_EMIT dataUpdated(0);
}
QJsonObject MathNode::save() const
{
QJsonObject modelJson;
modelJson["name"] = name();
modelJson["caption"] = caption();
modelJson["default_first"] = _first->value();
modelJson["default_second"] = _second->value();
modelJson["operation"] = _operation->currentIndex();
return modelJson;
}
void MathNode::restore(const QJsonObject& json_obj)
{
setName(json_obj["name"].toString());
setCaption(json_obj["caption"].toString());
_first->setValue(json_obj["default_first"].toDouble());
_second->setValue(json_obj["default_second"].toDouble());
_operation->setCurrentIndex(json_obj["operation"].toInt());
}

View File

@@ -0,0 +1,43 @@
#ifndef NOGGIT_MATHNODE_HPP
#define NOGGIT_MATHNODE_HPP
#include "BaseNode.hpp"
#include <QDoubleSpinBox>
#include <QComboBox>
using QtNodes::PortType;
using QtNodes::PortIndex;
using QtNodes::NodeData;
using QtNodes::NodeDataType;
using QtNodes::NodeDataModel;
using QtNodes::NodeValidationState;
namespace noggit
{
namespace Red::PresetEditor::Nodes
{
class MathNode : public BaseNode
{
Q_OBJECT
public:
MathNode();
void compute() override;
QJsonObject save() const override;
void restore(QJsonObject const& json_obj) override;
private:
QDoubleSpinBox* _first;
QDoubleSpinBox* _second;
QComboBox* _operation;
};
}
}
#endif //NOGGIT_MATHNODE_HPP