push missing files
pie menu test
This commit is contained in:
@@ -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
356
src/external/imguipiemenu/PieMenu.cpp
vendored
Normal 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
12
src/external/imguipiemenu/PieMenu.hpp
vendored
Normal 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
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
154
src/noggit/Red/PresetEditor/Nodes/BaseNode.cpp
Normal file
154
src/noggit/Red/PresetEditor/Nodes/BaseNode.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
128
src/noggit/Red/PresetEditor/Nodes/BaseNode.hpp
Normal file
128
src/noggit/Red/PresetEditor/Nodes/BaseNode.hpp
Normal 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
|
||||
25
src/noggit/Red/PresetEditor/Nodes/BaseNode.inl
Normal file
25
src/noggit/Red/PresetEditor/Nodes/BaseNode.inl
Normal 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
|
||||
42
src/noggit/Red/PresetEditor/Nodes/Data/DecimalData.hpp
Normal file
42
src/noggit/Red/PresetEditor/Nodes/Data/DecimalData.hpp
Normal 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
|
||||
40
src/noggit/Red/PresetEditor/Nodes/Data/IntegerData.hpp
Normal file
40
src/noggit/Red/PresetEditor/Nodes/Data/IntegerData.hpp
Normal 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
|
||||
104
src/noggit/Red/PresetEditor/Nodes/MathNode.cpp
Normal file
104
src/noggit/Red/PresetEditor/Nodes/MathNode.cpp
Normal 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());
|
||||
}
|
||||
|
||||
43
src/noggit/Red/PresetEditor/Nodes/MathNode.hpp
Normal file
43
src/noggit/Red/PresetEditor/Nodes/MathNode.hpp
Normal 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
|
||||
Reference in New Issue
Block a user