Merge pull request #147 from madebr/add-sdl3-support

Update imgui and add SDL3 support
This commit is contained in:
aap
2025-09-09 12:10:09 +02:00
committed by GitHub
27 changed files with 12190 additions and 6146 deletions
+8 -6
View File
@@ -16,13 +16,15 @@ jobs:
case: case:
- { name: 'Windows (DirectX 9)', os: 'windows-latest', platform: 'D3D9' } - { name: 'Windows (DirectX 9)', os: 'windows-latest', platform: 'D3D9' }
- { name: 'Windows (GL3, glfw)', os: 'windows-latest', platform: 'GL3', gl3_gfxlib: 'GLFW' } - { name: 'Windows (GL3, glfw)', os: 'windows-latest', platform: 'GL3', gl3_gfxlib: 'GLFW' }
- { name: 'Windows (GL3, SDL2)', os: 'windows-latest', platform: 'GL3', gl3_gfxlib: 'SDL2' } - { name: 'Windows (GL3, SDL2)', os: 'windows-latest', platform: 'GL3', gl3_gfxlib: 'SDL2', sdl-version: '2-latest' }
- { name: 'Windows (GL3, SDL3)', os: 'windows-latest', platform: 'GL3', gl3_gfxlib: 'SDL3', sdl-version: '3-latest' }
- { name: 'Windows (null)', os: 'windows-latest', platform: 'NULL' } - { name: 'Windows (null)', os: 'windows-latest', platform: 'NULL' }
- { name: 'macOS (GL3, glfw)', os: 'macos-latest', platform: 'GL3', gl3_gfxlib: 'GLFW' } - { name: 'macOS (GL3, glfw)', os: 'macos-latest', platform: 'GL3', gl3_gfxlib: 'GLFW' }
- { name: 'macOS (GL3, SDL2)', os: 'macos-latest', platform: 'GL3', gl3_gfxlib: 'SDL2' } - { name: 'macOS (GL3, SDL2)', os: 'macos-latest', platform: 'GL3', gl3_gfxlib: 'SDL2', sdl-version: '2-latest' }
- { name: 'macOS (GL3, SDL3)', os: 'macos-latest', platform: 'GL3', gl3_gfxlib: 'SDL3', sdl-version: '3-latest' }
- { name: 'macOS (null)', os: 'macos-latest', platform: 'NULL' } - { name: 'macOS (null)', os: 'macos-latest', platform: 'NULL' }
- { name: 'Ubuntu (GL3, glfw)', os: 'ubuntu-latest', platform: 'GL3', gl3_gfxlib: 'GLFW' } - { name: 'Ubuntu (GL3, glfw)', os: 'ubuntu-latest', platform: 'GL3', gl3_gfxlib: 'GLFW' }
- { name: 'Ubuntu (GL3, SDL2)', os: 'ubuntu-latest', platform: 'GL3', gl3_gfxlib: 'SDL2' } - { name: 'Ubuntu (GL3, SDL2)', os: 'ubuntu-latest', platform: 'GL3', gl3_gfxlib: 'SDL2', sdl-version: '2-latest' }
- { name: 'Ubuntu (null)', os: 'ubuntu-latest', platform: 'NULL' } - { name: 'Ubuntu (null)', os: 'ubuntu-latest', platform: 'NULL' }
- { name: 'PlayStation 2', os: 'ubuntu-latest', platform: 'PS2', ps2: true, container: 'ps2dev/ps2dev:latest', cmake-toolchain-file: 'cmake/ps2/cmaketoolchain/toolchain_ps2_ee.cmake' } - { name: 'PlayStation 2', os: 'ubuntu-latest', platform: 'PS2', ps2: true, container: 'ps2dev/ps2dev:latest', cmake-toolchain-file: 'cmake/ps2/cmaketoolchain/toolchain_ps2_ee.cmake' }
- { name: 'Nintendo Switch', os: 'ubuntu-latest', platform: 'GL3', gl3_gfxlib: 'GLFW', glfw-nobuild: true, container: 'devkitpro/devkita64:latest', cmake-toolchain-file: '/opt/devkitpro/cmake/Switch.cmake' } - { name: 'Nintendo Switch', os: 'ubuntu-latest', platform: 'GL3', gl3_gfxlib: 'GLFW', glfw-nobuild: true, container: 'devkitpro/devkita64:latest', cmake-toolchain-file: '/opt/devkitpro/cmake/Switch.cmake' }
@@ -36,11 +38,11 @@ jobs:
if: ${{ matrix.case.ps2 }} if: ${{ matrix.case.ps2 }}
run: | run: |
apk add cmake gmp mpc1 mpfr4 make pkgconf git apk add cmake gmp mpc1 mpfr4 make pkgconf git
- name: 'Setup SDL2' - name: 'Setup SDL'
if: ${{ matrix.case.gl3_gfxlib == 'SDL2' }} if: ${{ !!matrix.case.sdl-version }}
uses: libsdl-org/setup-sdl@main uses: libsdl-org/setup-sdl@main
with: with:
version: 2-latest version: ${{ matrix.case.sdl-version }}
install-linux-dependencies: true install-linux-dependencies: true
cmake-toolchain-file: ${{ matrix.case.cmake-toolchain-file }} cmake-toolchain-file: ${{ matrix.case.cmake-toolchain-file }}
- name: 'Setup GLFW' - name: 'Setup GLFW'
+5 -2
View File
@@ -39,7 +39,7 @@ if(NOT LIBRW_PLATFORM IN_LIST LIBRW_PLATFORMS)
endif() endif()
if(LIBRW_PLATFORM_GL3) if(LIBRW_PLATFORM_GL3)
set(LIBRW_GL3_GFXLIBS "GLFW" "SDL2") set(LIBRW_GL3_GFXLIBS "GLFW" "SDL2" "SDL3")
set(LIBRW_GL3_GFXLIB "GLFW" CACHE STRING "gfxlib for gl3 (choices=${LIBRW_GL3_GFXLIBS})") set(LIBRW_GL3_GFXLIB "GLFW" CACHE STRING "gfxlib for gl3 (choices=${LIBRW_GL3_GFXLIBS})")
set_property(CACHE LIBRW_GL3_GFXLIB PROPERTY STRINGS ${LIBRW_GL3_GFXLIBS}) set_property(CACHE LIBRW_GL3_GFXLIB PROPERTY STRINGS ${LIBRW_GL3_GFXLIBS})
if(LIBRW_PLATFORM_GL3) if(LIBRW_PLATFORM_GL3)
@@ -91,6 +91,7 @@ if(LIBRW_INSTALL)
EXPORT librw-targets NAMESPACE librw:: EXPORT librw-targets NAMESPACE librw::
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
) )
export(TARGETS librw NAMESPACE librw:: FILE librw-targets.cmake)
if(LIBRW_GL3_GFXLIB STREQUAL "SDL2") if(LIBRW_GL3_GFXLIB STREQUAL "SDL2")
install( install(
@@ -126,8 +127,10 @@ if(LIBRW_INSTALL)
elseif(LIBRW_PLATFORM_GL3) elseif(LIBRW_PLATFORM_GL3)
if(LIBRW_GL3_GFXLIB STREQUAL "GLFW") if(LIBRW_GL3_GFXLIB STREQUAL "GLFW")
set(platform "-gl3-glfw") set(platform "-gl3-glfw")
else() elseif(LIBRW_GL3_GFXLIB STREQUAL "SDL2")
set(platform "-gl3-sdl2") set(platform "-gl3-sdl2")
elseif(LIBRW_GL3_GFXLIB STREQUAL "SDL3")
set(platform "-gl3-sdl3")
endif() endif()
elseif(LIBRW_PLATFORM_D3D9) elseif(LIBRW_PLATFORM_D3D9)
set(platform "-d3d9") set(platform "-d3d9")
+8 -3
View File
@@ -14,16 +14,21 @@ if(LIBRW_PLATFORM_GL3)
message(FATAL_ERROR "find_package(OpenGL) failed: no target was created") message(FATAL_ERROR "find_package(OpenGL) failed: no target was created")
endif() endif()
include(CMakeFindDependencyMacro)
if(LIBRW_GL3_GFXLIB STREQUAL "GLFW") if(LIBRW_GL3_GFXLIB STREQUAL "GLFW")
if (NOT TARGET glfw) if (NOT TARGET glfw)
find_package(glfw3 REQUIRED) find_dependency(glfw3 REQUIRED)
endif() endif()
elseif(LIBRW_GL3_GFXLIB STREQUAL "SDL2") elseif(LIBRW_GL3_GFXLIB STREQUAL "SDL2")
if (NOT TARGET SDL2::SDL2) if (NOT TARGET SDL2::SDL2)
find_package(SDL2 CONFIG) find_dependency(SDL2 CONFIG)
if (NOT TARGET SDL2::SDL2) if (NOT TARGET SDL2::SDL2)
find_package(SDL2 MODULE REQUIRED) find_dependency(SDL2 MODULE REQUIRED)
endif() endif()
endif() endif()
elseif(LIBRW_GL3_GFXLIB STREQUAL "SDL3")
if (NOT TARGET SDL3::SDL3)
find_dependency(SDL3 CONFIG REQUIRED)
endif()
endif() endif()
endif() endif()
+21 -3
View File
@@ -6,6 +6,7 @@ newoption {
allowed = { allowed = {
{ "glfw", "GLFW" }, { "glfw", "GLFW" },
{ "sdl2", "SDL2" }, { "sdl2", "SDL2" },
{ "sdl3", "SDL3" },
}, },
} }
@@ -27,7 +28,14 @@ newoption {
trigger = "sdl2dir", trigger = "sdl2dir",
value = "PATH", value = "PATH",
description = "Directory of sdl2", description = "Directory of sdl2",
default = "../SDL2-2.0.14", default = "../SDL2-2.32.10",
}
newoption {
trigger = "sdl3dir",
value = "PATH",
description = "Directory of sdl3",
default = "../SDL3-3.2.22",
} }
workspace "librw" workspace "librw"
@@ -64,6 +72,10 @@ workspace "librw"
defines { "RW_GL3" } defines { "RW_GL3" }
if _OPTIONS["gfxlib"] == "sdl2" then if _OPTIONS["gfxlib"] == "sdl2" then
defines { "LIBRW_SDL2" } defines { "LIBRW_SDL2" }
elseif _OPTIONS["gfxlib"] == "sdl3" then
defines { "LIBRW_SDL3" }
elseif _OPTIONS["gfxlib"] == "glfw" then
defines { "LIBRW_GLFW" }
end end
filter { "platforms:*d3d9" } filter { "platforms:*d3d9" }
defines { "RW_D3D9" } defines { "RW_D3D9" }
@@ -131,21 +143,27 @@ function findlibs()
links { "GL" } links { "GL" }
if _OPTIONS["gfxlib"] == "glfw" then if _OPTIONS["gfxlib"] == "glfw" then
links { "glfw" } links { "glfw" }
else elseif _OPTIONS["gfxlib"] == "sdl2" then
links { "SDL2" } links { "SDL2" }
elseif _OPTIONS["gfxlib"] == "sdl3" then
links { "SDL3" }
end end
filter { "platforms:win-amd64-gl3" } filter { "platforms:win-amd64-gl3" }
libdirs { path.join(_OPTIONS["glfwdir64"], "lib-vc2015") } libdirs { path.join(_OPTIONS["glfwdir64"], "lib-vc2015") }
libdirs { path.join(_OPTIONS["sdl2dir"], "lib/x64") } libdirs { path.join(_OPTIONS["sdl2dir"], "lib/x64") }
libdirs { path.join(_OPTIONS["sdl3dir"], "lib/x64") }
filter { "platforms:win-x86-gl3" } filter { "platforms:win-x86-gl3" }
libdirs { path.join(_OPTIONS["glfwdir32"], "lib-vc2015") } libdirs { path.join(_OPTIONS["glfwdir32"], "lib-vc2015") }
libdirs { path.join(_OPTIONS["sdl2dir"], "lib/x86") } libdirs { path.join(_OPTIONS["sdl2dir"], "lib/x86") }
libdirs { path.join(_OPTIONS["sdl3dir"], "lib/x86") }
filter { "platforms:win*gl3" } filter { "platforms:win*gl3" }
links { "opengl32" } links { "opengl32" }
if _OPTIONS["gfxlib"] == "glfw" then if _OPTIONS["gfxlib"] == "glfw" then
links { "glfw3" } links { "glfw3" }
else elseif _OPTIONS["gfxlib"] == "sdl2" then
links { "SDL2" } links { "SDL2" }
elseif _OPTIONS["gfxlib"] == "sdl3" then
links { "SDL3" }
end end
filter { "platforms:*d3d9" } filter { "platforms:*d3d9" }
links { "gdi32", "d3d9" } links { "gdi32", "d3d9" }
+2 -1
View File
@@ -1,6 +1,7 @@
add_library(librw_skeleton add_library(librw_skeleton
glfw.cpp glfw.cpp
sdl2.cpp sdl2.cpp
sdl3.cpp
skeleton.cpp skeleton.cpp
skeleton.h skeleton.h
win.cpp win.cpp
@@ -38,7 +39,7 @@ target_link_libraries(librw_skeleton
librw librw
) )
if (LIBRW_GL3_GFXLIB STREQUAL "SDL2") if (LIBRW_GL3_GFXLIB MATCHES "SDL[23]")
target_compile_definitions(librw_skeleton PRIVATE SDL_MAIN_HANDLED) target_compile_definitions(librw_skeleton PRIVATE SDL_MAIN_HANDLED)
endif() endif()
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef LIBRW_SDL2 #ifdef LIBRW_GLFW
#include <rw.h> #include <rw.h>
#include "skeleton.h" #include "skeleton.h"
+199 -43
View File
@@ -1,5 +1,5 @@
// https://github.com/CedricGuillemet/ImGuizmo // https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP // v1.91.3 WIP
// //
// The MIT License(MIT) // The MIT License(MIT)
// //
@@ -666,7 +666,7 @@ namespace IMGUIZMO_NAMESPACE
struct Context struct Context
{ {
Context() : mbUsing(false), mbEnable(true), mbUsingBounds(false) Context() : mbUsing(false), mbUsingViewManipulate(false), mbEnable(true), mIsViewManipulatorHovered(false), mbUsingBounds(false)
{ {
} }
@@ -702,9 +702,11 @@ namespace IMGUIZMO_NAMESPACE
vec_t mRelativeOrigin; vec_t mRelativeOrigin;
bool mbUsing; bool mbUsing;
bool mbUsingViewManipulate;
bool mbEnable; bool mbEnable;
bool mbMouseOver; bool mbMouseOver;
bool mReversed; // reversed projection matrix bool mReversed; // reversed projection matrix
bool mIsViewManipulatorHovered;
// translation // translation
vec_t mTranslationPlan; vec_t mTranslationPlan;
@@ -726,6 +728,7 @@ namespace IMGUIZMO_NAMESPACE
// save axis factor when using gizmo // save axis factor when using gizmo
bool mBelowAxisLimit[3]; bool mBelowAxisLimit[3];
int mAxisMask = 0;
bool mBelowPlaneLimit[3]; bool mBelowPlaneLimit[3];
float mAxisFactor[3]; float mAxisFactor[3];
@@ -754,13 +757,25 @@ namespace IMGUIZMO_NAMESPACE
float mDisplayRatio = 1.f; float mDisplayRatio = 1.f;
bool mIsOrthographic = false; bool mIsOrthographic = false;
// check to not have multiple gizmo highlighted at the same time
bool mbOverGizmoHotspot = false;
int mActualID = -1; ImGuiWindow* mAlternativeWindow = nullptr;
int mEditingID = -1; ImVector<ImGuiID> mIDStack;
ImGuiID mEditingID = -1;
OPERATION mOperation = OPERATION(-1); OPERATION mOperation = OPERATION(-1);
bool mAllowAxisFlip = true; bool mAllowAxisFlip = true;
float mGizmoSizeClipSpace = 0.1f; float mGizmoSizeClipSpace = 0.1f;
inline ImGuiID GetCurrentID()
{
if (mIDStack.empty())
{
mIDStack.push_back(-1);
}
return mIDStack.back();
}
}; };
static Context gContext; static Context gContext;
@@ -929,6 +944,8 @@ namespace IMGUIZMO_NAMESPACE
ImGuiWindow* window = ImGui::FindWindowByName(gContext.mDrawList->_OwnerName); ImGuiWindow* window = ImGui::FindWindowByName(gContext.mDrawList->_OwnerName);
if (g.HoveredWindow == window) // Mouse hovering drawlist window if (g.HoveredWindow == window) // Mouse hovering drawlist window
return true; return true;
if (gContext.mAlternativeWindow != nullptr && g.HoveredWindow == gContext.mAlternativeWindow)
return true;
if (g.HoveredWindow != NULL) // Any other window is hovered if (g.HoveredWindow != NULL) // Any other window is hovered
return false; return false;
if (ImGui::IsMouseHoveringRect(window->InnerRect.Min, window->InnerRect.Max, false)) // Hovering drawlist window rect, while no other window is hovered (for _NoInputs windows) if (ImGui::IsMouseHoveringRect(window->InnerRect.Min, window->InnerRect.Max, false)) // Hovering drawlist window rect, while no other window is hovered (for _NoInputs windows)
@@ -981,6 +998,7 @@ namespace IMGUIZMO_NAMESPACE
ImGui::Begin("gizmo", NULL, flags); ImGui::Begin("gizmo", NULL, flags);
gContext.mDrawList = ImGui::GetWindowDrawList(); gContext.mDrawList = ImGui::GetWindowDrawList();
gContext.mbOverGizmoHotspot = false;
ImGui::End(); ImGui::End();
ImGui::PopStyleVar(); ImGui::PopStyleVar();
ImGui::PopStyleColor(2); ImGui::PopStyleColor(2);
@@ -988,7 +1006,17 @@ namespace IMGUIZMO_NAMESPACE
bool IsUsing() bool IsUsing()
{ {
return (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) || gContext.mbUsingBounds; return (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID)) || gContext.mbUsingBounds;
}
bool IsUsingViewManipulate()
{
return gContext.mbUsingViewManipulate;
}
bool IsViewManipulateHovered()
{
return gContext.mIsViewManipulatorHovered;
} }
bool IsUsingAny() bool IsUsingAny()
@@ -1078,7 +1106,6 @@ namespace IMGUIZMO_NAMESPACE
// compute scale from the size of camera right vector projected on screen at the matrix position // compute scale from the size of camera right vector projected on screen at the matrix position
vec_t pointRight = viewInverse.v.right; vec_t pointRight = viewInverse.v.right;
pointRight.TransformPoint(gContext.mViewProjection); pointRight.TransformPoint(gContext.mViewProjection);
gContext.mScreenFactor = gContext.mGizmoSizeClipSpace / (pointRight.x / pointRight.w - gContext.mMVP.v.position.x / gContext.mMVP.v.position.w);
vec_t rightViewInverse = viewInverse.v.right; vec_t rightViewInverse = viewInverse.v.right;
rightViewInverse.TransformVector(gContext.mModelInverse); rightViewInverse.TransformVector(gContext.mModelInverse);
@@ -1146,11 +1173,13 @@ namespace IMGUIZMO_NAMESPACE
dirPlaneX = directionUnary[(axisIndex + 1) % 3]; dirPlaneX = directionUnary[(axisIndex + 1) % 3];
dirPlaneY = directionUnary[(axisIndex + 2) % 3]; dirPlaneY = directionUnary[(axisIndex + 2) % 3];
if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID))
{ {
// when using, use stored factors so the gizmo doesn't flip when we translate // when using, use stored factors so the gizmo doesn't flip when we translate
belowAxisLimit = gContext.mBelowAxisLimit[axisIndex];
belowPlaneLimit = gContext.mBelowPlaneLimit[axisIndex]; // Apply axis mask to axes and planes
belowAxisLimit = gContext.mBelowAxisLimit[axisIndex] && ((1<<axisIndex)&gContext.mAxisMask);
belowPlaneLimit = gContext.mBelowPlaneLimit[axisIndex] && (((1<<axisIndex) == gContext.mAxisMask) || !gContext.mAxisMask);
dirAxis *= gContext.mAxisFactor[axisIndex]; dirAxis *= gContext.mAxisFactor[axisIndex];
dirPlaneX *= gContext.mAxisFactor[(axisIndex + 1) % 3]; dirPlaneX *= gContext.mAxisFactor[(axisIndex + 1) % 3];
@@ -1181,8 +1210,9 @@ namespace IMGUIZMO_NAMESPACE
float axisLengthInClipSpace = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirAxis * gContext.mScreenFactor, localCoordinates); float axisLengthInClipSpace = GetSegmentLengthClipSpace(makeVect(0.f, 0.f, 0.f), dirAxis * gContext.mScreenFactor, localCoordinates);
float paraSurf = GetParallelogram(makeVect(0.f, 0.f, 0.f), dirPlaneX * gContext.mScreenFactor, dirPlaneY * gContext.mScreenFactor); float paraSurf = GetParallelogram(makeVect(0.f, 0.f, 0.f), dirPlaneX * gContext.mScreenFactor, dirPlaneY * gContext.mScreenFactor);
belowPlaneLimit = (paraSurf > gContext.mAxisLimit); // Apply axis mask to axes and planes
belowAxisLimit = (axisLengthInClipSpace > gContext.mPlaneLimit); belowPlaneLimit = (paraSurf > gContext.mAxisLimit) && (((1<<axisIndex) == gContext.mAxisMask) || !gContext.mAxisMask);
belowAxisLimit = (axisLengthInClipSpace > gContext.mPlaneLimit) && !((1<<axisIndex)&gContext.mAxisMask);
// and store values // and store values
gContext.mAxisFactor[axisIndex] = mulAxis; gContext.mAxisFactor[axisIndex] = mulAxis;
@@ -1241,6 +1271,9 @@ namespace IMGUIZMO_NAMESPACE
} }
ImDrawList* drawList = gContext.mDrawList; ImDrawList* drawList = gContext.mDrawList;
bool isMultipleAxesMasked = (gContext.mAxisMask & (gContext.mAxisMask - 1)) != 0;
bool isNoAxesMasked = !gContext.mAxisMask;
// colors // colors
ImU32 colors[7]; ImU32 colors[7];
ComputeColors(colors, type, ROTATE); ComputeColors(colors, type, ROTATE);
@@ -1268,8 +1301,15 @@ namespace IMGUIZMO_NAMESPACE
{ {
continue; continue;
} }
bool isAxisMasked = ((1 << (2 - axis)) & gContext.mAxisMask) != 0;
if ((!isAxisMasked || isMultipleAxesMasked) && !isNoAxesMasked)
{
continue;
}
const bool usingAxis = (gContext.mbUsing && type == MT_ROTATE_Z - axis); const bool usingAxis = (gContext.mbUsing && type == MT_ROTATE_Z - axis);
const int circleMul = (hasRSC && !usingAxis ) ? 1 : 2; const int circleMul = (hasRSC && !usingAxis) ? 1 : 2;
ImVec2* circlePos = (ImVec2*)alloca(sizeof(ImVec2) * (circleMul * halfCircleSegmentCount + 1)); ImVec2* circlePos = (ImVec2*)alloca(sizeof(ImVec2) * (circleMul * halfCircleSegmentCount + 1));
@@ -1293,12 +1333,12 @@ namespace IMGUIZMO_NAMESPACE
gContext.mRadiusSquareCenter = radiusAxis; gContext.mRadiusSquareCenter = radiusAxis;
} }
} }
if(hasRSC && (!gContext.mbUsing || type == MT_ROTATE_SCREEN)) if(hasRSC && (!gContext.mbUsing || type == MT_ROTATE_SCREEN) && (!isMultipleAxesMasked && isNoAxesMasked))
{ {
drawList->AddCircle(worldToPos(gContext.mModel.v.position, gContext.mViewProjection), gContext.mRadiusSquareCenter, colors[0], 64, gContext.mStyle.RotationOuterLineThickness); drawList->AddCircle(worldToPos(gContext.mModel.v.position, gContext.mViewProjection), gContext.mRadiusSquareCenter, colors[0], 64, gContext.mStyle.RotationOuterLineThickness);
} }
if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsRotateType(type)) if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID) && IsRotateType(type))
{ {
ImVec2 circlePos[halfCircleSegmentCount + 1]; ImVec2 circlePos[halfCircleSegmentCount + 1];
@@ -1355,7 +1395,7 @@ namespace IMGUIZMO_NAMESPACE
// draw // draw
vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f }; vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f };
if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID))
{ {
scaleDisplay = gContext.mScale; scaleDisplay = gContext.mScale;
} }
@@ -1382,7 +1422,7 @@ namespace IMGUIZMO_NAMESPACE
ImVec2 worldDirSSpaceNoScale = worldToPos(dirAxis * markerScale * gContext.mScreenFactor, gContext.mMVP); ImVec2 worldDirSSpaceNoScale = worldToPos(dirAxis * markerScale * gContext.mScreenFactor, gContext.mMVP);
ImVec2 worldDirSSpace = worldToPos((dirAxis * markerScale * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVP); ImVec2 worldDirSSpace = worldToPos((dirAxis * markerScale * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVP);
if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID))
{ {
ImU32 scaleLineColor = GetColorU32(SCALE_LINE); ImU32 scaleLineColor = GetColorU32(SCALE_LINE);
drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, scaleLineColor, gContext.mStyle.ScaleLineThickness); drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, scaleLineColor, gContext.mStyle.ScaleLineThickness);
@@ -1406,7 +1446,7 @@ namespace IMGUIZMO_NAMESPACE
// draw screen cirle // draw screen cirle
drawList->AddCircleFilled(gContext.mScreenSquareCenter, gContext.mStyle.CenterCircleSize, colors[0], 32); drawList->AddCircleFilled(gContext.mScreenSquareCenter, gContext.mStyle.CenterCircleSize, colors[0], 32);
if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsScaleType(type)) if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID) && IsScaleType(type))
{ {
//ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection); //ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection);
ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
@@ -1443,7 +1483,7 @@ namespace IMGUIZMO_NAMESPACE
// draw // draw
vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f }; vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f };
if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID))
{ {
scaleDisplay = gContext.mScale; scaleDisplay = gContext.mScale;
} }
@@ -1471,7 +1511,7 @@ namespace IMGUIZMO_NAMESPACE
ImVec2 worldDirSSpace = worldToPos((dirAxis * markerScale * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVPLocal); ImVec2 worldDirSSpace = worldToPos((dirAxis * markerScale * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVPLocal);
#if 0 #if 0
if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID))
{ {
drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, IM_COL32(0x40, 0x40, 0x40, 0xFF), 3.f); drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, IM_COL32(0x40, 0x40, 0x40, 0xFF), 3.f);
drawList->AddCircleFilled(worldDirSSpaceNoScale, 6.f, IM_COL32(0x40, 0x40, 0x40, 0xFF)); drawList->AddCircleFilled(worldDirSSpaceNoScale, 6.f, IM_COL32(0x40, 0x40, 0x40, 0xFF));
@@ -1491,7 +1531,7 @@ namespace IMGUIZMO_NAMESPACE
// draw screen cirle // draw screen cirle
drawList->AddCircle(gContext.mScreenSquareCenter, 20.f, colors[0], 32, gContext.mStyle.CenterCircleSize); drawList->AddCircle(gContext.mScreenSquareCenter, 20.f, colors[0], 32, gContext.mStyle.CenterCircleSize);
if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsScaleType(type)) if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID) && IsScaleType(type))
{ {
//ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection); //ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection);
ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection); ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
@@ -1585,7 +1625,7 @@ namespace IMGUIZMO_NAMESPACE
drawList->AddCircleFilled(gContext.mScreenSquareCenter, gContext.mStyle.CenterCircleSize, colors[0], 32); drawList->AddCircleFilled(gContext.mScreenSquareCenter, gContext.mStyle.CenterCircleSize, colors[0], 32);
if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsTranslateType(type)) if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID) && IsTranslateType(type))
{ {
ImU32 translationLineColor = GetColorU32(TRANSLATION_LINE); ImU32 translationLineColor = GetColorU32(TRANSLATION_LINE);
@@ -1779,7 +1819,7 @@ namespace IMGUIZMO_NAMESPACE
gContext.mBoundsLocalPivot[thirdAxis] = aabb[oppositeIndex][thirdAxis]; gContext.mBoundsLocalPivot[thirdAxis] = aabb[oppositeIndex][thirdAxis];
gContext.mbUsingBounds = true; gContext.mbUsingBounds = true;
gContext.mEditingID = gContext.mActualID; gContext.mEditingID = gContext.GetCurrentID();
gContext.mBoundsMatrix = gContext.mModelSource; gContext.mBoundsMatrix = gContext.mModelSource;
} }
// small anchor on middle of segment // small anchor on middle of segment
@@ -1798,12 +1838,12 @@ namespace IMGUIZMO_NAMESPACE
gContext.mBoundsLocalPivot[gContext.mBoundsAxis[0]] = aabb[oppositeIndex][indices[i % 2]];// bounds[gContext.mBoundsAxis[0]] * (((i + 1) & 2) ? 1.f : -1.f); gContext.mBoundsLocalPivot[gContext.mBoundsAxis[0]] = aabb[oppositeIndex][indices[i % 2]];// bounds[gContext.mBoundsAxis[0]] * (((i + 1) & 2) ? 1.f : -1.f);
gContext.mbUsingBounds = true; gContext.mbUsingBounds = true;
gContext.mEditingID = gContext.mActualID; gContext.mEditingID = gContext.GetCurrentID();
gContext.mBoundsMatrix = gContext.mModelSource; gContext.mBoundsMatrix = gContext.mModelSource;
} }
} }
if (gContext.mbUsingBounds && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID)) if (gContext.mbUsingBounds && (gContext.GetCurrentID() == gContext.mEditingID))
{ {
matrix_t scale; matrix_t scale;
scale.SetToIdentity(); scale.SetToIdentity();
@@ -1904,6 +1944,8 @@ namespace IMGUIZMO_NAMESPACE
{ {
continue; continue;
} }
bool isAxisMasked = ((1 << i) & gContext.mAxisMask) != 0;
vec_t dirPlaneX, dirPlaneY, dirAxis; vec_t dirPlaneX, dirPlaneY, dirAxis;
bool belowAxisLimit, belowPlaneLimit; bool belowAxisLimit, belowPlaneLimit;
ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit, true); ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit, true);
@@ -1924,7 +1966,8 @@ namespace IMGUIZMO_NAMESPACE
if ((closestPointOnAxis - makeVect(posOnPlanScreen)).Length() < 12.f) // pixel size if ((closestPointOnAxis - makeVect(posOnPlanScreen)).Length() < 12.f) // pixel size
{ {
type = MT_SCALE_X + i; if (!isAxisMasked)
type = MT_SCALE_X + i;
} }
} }
@@ -1973,6 +2016,10 @@ namespace IMGUIZMO_NAMESPACE
{ {
return MT_NONE; return MT_NONE;
} }
bool isNoAxesMasked = !gContext.mAxisMask;
bool isMultipleAxesMasked = (gContext.mAxisMask & (gContext.mAxisMask - 1)) != 0;
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
int type = MT_NONE; int type = MT_NONE;
@@ -1980,6 +2027,8 @@ namespace IMGUIZMO_NAMESPACE
float dist = deltaScreen.Length(); float dist = deltaScreen.Length();
if (Intersects(op, ROTATE_SCREEN) && dist >= (gContext.mRadiusSquareCenter - 4.0f) && dist < (gContext.mRadiusSquareCenter + 4.0f)) if (Intersects(op, ROTATE_SCREEN) && dist >= (gContext.mRadiusSquareCenter - 4.0f) && dist < (gContext.mRadiusSquareCenter + 4.0f))
{ {
if (!isNoAxesMasked)
return MT_NONE;
type = MT_ROTATE_SCREEN; type = MT_ROTATE_SCREEN;
} }
@@ -1994,6 +2043,7 @@ namespace IMGUIZMO_NAMESPACE
{ {
continue; continue;
} }
bool isAxisMasked = ((1 << i) & gContext.mAxisMask) != 0;
// pickup plan // pickup plan
vec_t pickupPlan = BuildPlan(gContext.mModel.v.position, planNormals[i]); vec_t pickupPlan = BuildPlan(gContext.mModel.v.position, planNormals[i]);
@@ -2018,6 +2068,8 @@ namespace IMGUIZMO_NAMESPACE
const float distance = makeVect(distanceOnScreen).Length(); const float distance = makeVect(distanceOnScreen).Length();
if (distance < 8.f) // pixel size if (distance < 8.f) // pixel size
{ {
if ((!isAxisMasked || isMultipleAxesMasked) && !isNoAxesMasked)
break;
type = MT_ROTATE_X + i; type = MT_ROTATE_X + i;
} }
} }
@@ -2031,6 +2083,10 @@ namespace IMGUIZMO_NAMESPACE
{ {
return MT_NONE; return MT_NONE;
} }
bool isNoAxesMasked = !gContext.mAxisMask;
bool isMultipleAxesMasked = (gContext.mAxisMask & (gContext.mAxisMask - 1)) != 0;
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
int type = MT_NONE; int type = MT_NONE;
@@ -2047,6 +2103,7 @@ namespace IMGUIZMO_NAMESPACE
// compute // compute
for (int i = 0; i < 3 && type == MT_NONE; i++) for (int i = 0; i < 3 && type == MT_NONE; i++)
{ {
bool isAxisMasked = ((1 << i) & gContext.mAxisMask) != 0;
vec_t dirPlaneX, dirPlaneY, dirAxis; vec_t dirPlaneX, dirPlaneY, dirAxis;
bool belowAxisLimit, belowPlaneLimit; bool belowAxisLimit, belowPlaneLimit;
ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit); ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
@@ -2063,6 +2120,8 @@ namespace IMGUIZMO_NAMESPACE
vec_t closestPointOnAxis = PointOnSegment(screenCoord, makeVect(axisStartOnScreen), makeVect(axisEndOnScreen)); vec_t closestPointOnAxis = PointOnSegment(screenCoord, makeVect(axisStartOnScreen), makeVect(axisEndOnScreen));
if ((closestPointOnAxis - screenCoord).Length() < 12.f && Intersects(op, static_cast<OPERATION>(TRANSLATE_X << i))) // pixel size if ((closestPointOnAxis - screenCoord).Length() < 12.f && Intersects(op, static_cast<OPERATION>(TRANSLATE_X << i))) // pixel size
{ {
if (isAxisMasked)
break;
type = MT_MOVE_X + i; type = MT_MOVE_X + i;
} }
@@ -2070,6 +2129,8 @@ namespace IMGUIZMO_NAMESPACE
const float dy = dirPlaneY.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor)); const float dy = dirPlaneY.Dot3((posOnPlan - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor));
if (belowPlaneLimit && dx >= quadUV[0] && dx <= quadUV[4] && dy >= quadUV[1] && dy <= quadUV[3] && Contains(op, TRANSLATE_PLANS[i])) if (belowPlaneLimit && dx >= quadUV[0] && dx <= quadUV[4] && dy >= quadUV[1] && dy <= quadUV[3] && Contains(op, TRANSLATE_PLANS[i]))
{ {
if ((!isAxisMasked || isMultipleAxesMasked) && !isNoAxesMasked)
break;
type = MT_MOVE_YZ + i; type = MT_MOVE_YZ + i;
} }
@@ -2092,7 +2153,7 @@ namespace IMGUIZMO_NAMESPACE
bool modified = false; bool modified = false;
// move // move
if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsTranslateType(gContext.mCurrentOperation)) if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID) && IsTranslateType(gContext.mCurrentOperation))
{ {
#if IMGUI_VERSION_NUM >= 18723 #if IMGUI_VERSION_NUM >= 18723
ImGui::SetNextFrameWantCaptureMouse(true); ImGui::SetNextFrameWantCaptureMouse(true);
@@ -2166,7 +2227,8 @@ namespace IMGUIZMO_NAMESPACE
{ {
// find new possible way to move // find new possible way to move
vec_t gizmoHitProportion; vec_t gizmoHitProportion;
type = GetMoveType(op, &gizmoHitProportion); type = gContext.mbOverGizmoHotspot ? MT_NONE : GetMoveType(op, &gizmoHitProportion);
gContext.mbOverGizmoHotspot |= type != MT_NONE;
if (type != MT_NONE) if (type != MT_NONE)
{ {
#if IMGUI_VERSION_NUM >= 18723 #if IMGUI_VERSION_NUM >= 18723
@@ -2178,7 +2240,7 @@ namespace IMGUIZMO_NAMESPACE
if (CanActivate() && type != MT_NONE) if (CanActivate() && type != MT_NONE)
{ {
gContext.mbUsing = true; gContext.mbUsing = true;
gContext.mEditingID = gContext.mActualID; gContext.mEditingID = gContext.GetCurrentID();
gContext.mCurrentOperation = type; gContext.mCurrentOperation = type;
vec_t movePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, vec_t movePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir,
gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir,
@@ -2215,7 +2277,9 @@ namespace IMGUIZMO_NAMESPACE
if (!gContext.mbUsing) if (!gContext.mbUsing)
{ {
// find new possible way to scale // find new possible way to scale
type = GetScaleType(op); type = gContext.mbOverGizmoHotspot ? MT_NONE : GetScaleType(op);
gContext.mbOverGizmoHotspot |= type != MT_NONE;
if (type != MT_NONE) if (type != MT_NONE)
{ {
#if IMGUI_VERSION_NUM >= 18723 #if IMGUI_VERSION_NUM >= 18723
@@ -2227,23 +2291,23 @@ namespace IMGUIZMO_NAMESPACE
if (CanActivate() && type != MT_NONE) if (CanActivate() && type != MT_NONE)
{ {
gContext.mbUsing = true; gContext.mbUsing = true;
gContext.mEditingID = gContext.mActualID; gContext.mEditingID = gContext.GetCurrentID();
gContext.mCurrentOperation = type; gContext.mCurrentOperation = type;
const vec_t movePlanNormal[] = { gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.dir, gContext.mModel.v.up, gContext.mModel.v.right, -gContext.mCameraDir }; const vec_t movePlanNormal[] = { gContext.mModelLocal.v.up, gContext.mModelLocal.v.dir, gContext.mModelLocal.v.right, gContext.mModelLocal.v.dir, gContext.mModelLocal.v.up, gContext.mModelLocal.v.right, -gContext.mCameraDir };
// pickup plan // pickup plan
gContext.mTranslationPlan = BuildPlan(gContext.mModel.v.position, movePlanNormal[type - MT_SCALE_X]); gContext.mTranslationPlan = BuildPlan(gContext.mModelLocal.v.position, movePlanNormal[type - MT_SCALE_X]);
const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan); const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
gContext.mTranslationPlanOrigin = gContext.mRayOrigin + gContext.mRayVector * len; gContext.mTranslationPlanOrigin = gContext.mRayOrigin + gContext.mRayVector * len;
gContext.mMatrixOrigin = gContext.mModel.v.position; gContext.mMatrixOrigin = gContext.mModelLocal.v.position;
gContext.mScale.Set(1.f, 1.f, 1.f); gContext.mScale.Set(1.f, 1.f, 1.f);
gContext.mRelativeOrigin = (gContext.mTranslationPlanOrigin - gContext.mModel.v.position) * (1.f / gContext.mScreenFactor); gContext.mRelativeOrigin = (gContext.mTranslationPlanOrigin - gContext.mModelLocal.v.position) * (1.f / gContext.mScreenFactor);
gContext.mScaleValueOrigin = makeVect(gContext.mModelSource.v.right.Length(), gContext.mModelSource.v.up.Length(), gContext.mModelSource.v.dir.Length()); gContext.mScaleValueOrigin = makeVect(gContext.mModelSource.v.right.Length(), gContext.mModelSource.v.up.Length(), gContext.mModelSource.v.dir.Length());
gContext.mSaveMousePosx = io.MousePos.x; gContext.mSaveMousePosx = io.MousePos.x;
} }
} }
// scale // scale
if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsScaleType(gContext.mCurrentOperation)) if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID) && IsScaleType(gContext.mCurrentOperation))
{ {
#if IMGUI_VERSION_NUM >= 18723 #if IMGUI_VERSION_NUM >= 18723
ImGui::SetNextFrameWantCaptureMouse(true); ImGui::SetNextFrameWantCaptureMouse(true);
@@ -2336,7 +2400,8 @@ namespace IMGUIZMO_NAMESPACE
if (!gContext.mbUsing) if (!gContext.mbUsing)
{ {
type = GetRotateType(op); type = gContext.mbOverGizmoHotspot ? MT_NONE : GetRotateType(op);
gContext.mbOverGizmoHotspot |= type != MT_NONE;
if (type != MT_NONE) if (type != MT_NONE)
{ {
@@ -2355,7 +2420,7 @@ namespace IMGUIZMO_NAMESPACE
if (CanActivate() && type != MT_NONE) if (CanActivate() && type != MT_NONE)
{ {
gContext.mbUsing = true; gContext.mbUsing = true;
gContext.mEditingID = gContext.mActualID; gContext.mEditingID = gContext.GetCurrentID();
gContext.mCurrentOperation = type; gContext.mCurrentOperation = type;
const vec_t rotatePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, -gContext.mCameraDir }; const vec_t rotatePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, -gContext.mCameraDir };
// pickup plan // pickup plan
@@ -2376,7 +2441,7 @@ namespace IMGUIZMO_NAMESPACE
} }
// rotation // rotation
if (gContext.mbUsing && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID) && IsRotateType(gContext.mCurrentOperation)) if (gContext.mbUsing && (gContext.GetCurrentID() == gContext.mEditingID) && IsRotateType(gContext.mCurrentOperation))
{ {
#if IMGUI_VERSION_NUM >= 18723 #if IMGUI_VERSION_NUM >= 18723
ImGui::SetNextFrameWantCaptureMouse(true); ImGui::SetNextFrameWantCaptureMouse(true);
@@ -2482,9 +2547,74 @@ namespace IMGUIZMO_NAMESPACE
mat.v.position.Set(translation[0], translation[1], translation[2], 1.f); mat.v.position.Set(translation[0], translation[1], translation[2], 1.f);
} }
void SetAlternativeWindow(ImGuiWindow* window)
{
gContext.mAlternativeWindow = window;
}
void SetID(int id) void SetID(int id)
{ {
gContext.mActualID = id; if (gContext.mIDStack.empty())
{
gContext.mIDStack.push_back(-1);
}
gContext.mIDStack.back() = id;
}
ImGuiID GetID(const char* str, const char* str_end)
{
ImGuiID seed = gContext.GetCurrentID();
ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
return id;
}
ImGuiID GetID(const char* str)
{
return GetID(str, nullptr);
}
ImGuiID GetID(const void* ptr)
{
ImGuiID seed = gContext.GetCurrentID();
ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
return id;
}
ImGuiID GetID(int n)
{
ImGuiID seed = gContext.GetCurrentID();
ImGuiID id = ImHashData(&n, sizeof(n), seed);
return id;
}
void PushID(const char* str_id)
{
ImGuiID id = GetID(str_id);
gContext.mIDStack.push_back(id);
}
void PushID(const char* str_id_begin, const char* str_id_end)
{
ImGuiID id = GetID(str_id_begin, str_id_end);
gContext.mIDStack.push_back(id);
}
void PushID(const void* ptr_id)
{
ImGuiID id = GetID(ptr_id);
gContext.mIDStack.push_back(id);
}
void PushID(int int_id)
{
ImGuiID id = GetID(int_id);
gContext.mIDStack.push_back(id);
}
void PopID()
{
IM_ASSERT(gContext.mIDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window?
gContext.mIDStack.pop_back();
} }
void AllowAxisFlip(bool value) void AllowAxisFlip(bool value)
@@ -2497,13 +2627,28 @@ namespace IMGUIZMO_NAMESPACE
gContext.mAxisLimit=value; gContext.mAxisLimit=value;
} }
void SetAxisMask(bool x, bool y, bool z)
{
gContext.mAxisMask = (x ? 1 : 0) + (y ? 2 : 0) + (z ? 4 : 0);
}
void SetPlaneLimit(float value) void SetPlaneLimit(float value)
{ {
gContext.mPlaneLimit = value; gContext.mPlaneLimit = value;
} }
bool IsOver(float* position, float pixelRadius)
{
const ImGuiIO& io = ImGui::GetIO();
float radius = sqrtf((ImLengthSqr(worldToPos({ position[0], position[1], position[2], 0.0f }, gContext.mViewProjection) - io.MousePos)));
return radius < pixelRadius;
}
bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix, const float* snap, const float* localBounds, const float* boundsSnap) bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix, const float* snap, const float* localBounds, const float* boundsSnap)
{ {
gContext.mDrawList->PushClipRect (ImVec2 (gContext.mX, gContext.mY), ImVec2 (gContext.mX + gContext.mWidth, gContext.mY + gContext.mHeight), false);
// Scale is always local or matrix will be skewed when applying world scale or oriented matrix // Scale is always local or matrix will be skewed when applying world scale or oriented matrix
ComputeContext(view, projection, matrix, (operation & SCALE) ? LOCAL : mode); ComputeContext(view, projection, matrix, (operation & SCALE) ? LOCAL : mode);
@@ -2547,6 +2692,8 @@ namespace IMGUIZMO_NAMESPACE
DrawScaleGizmo(operation, type); DrawScaleGizmo(operation, type);
DrawScaleUniveralGizmo(operation, type); DrawScaleUniveralGizmo(operation, type);
} }
gContext.mDrawList->PopClipRect ();
return manipulated; return manipulated;
} }
@@ -2775,7 +2922,6 @@ namespace IMGUIZMO_NAMESPACE
{ {
static bool isDraging = false; static bool isDraging = false;
static bool isClicking = false; static bool isClicking = false;
static bool isInside = false;
static vec_t interpolationUp; static vec_t interpolationUp;
static vec_t interpolationDir; static vec_t interpolationDir;
static int interpolationFrames = 0; static int interpolationFrames = 0;
@@ -2888,10 +3034,11 @@ namespace IMGUIZMO_NAMESPACE
if (iPass) if (iPass)
{ {
ImU32 directionColor = GetColorU32(DIRECTION_X + normalIndex); ImU32 directionColor = GetColorU32(DIRECTION_X + normalIndex);
gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, (directionColor | IM_COL32(0x80, 0x80, 0x80, 0x80)) | (isInside ? IM_COL32(0x08, 0x08, 0x08, 0) : 0)); gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, (directionColor | IM_COL32(0x80, 0x80, 0x80, 0x80)) | (gContext.mIsViewManipulatorHovered ? IM_COL32(0x08, 0x08, 0x08, 0) : 0));
if (boxes[boxCoordInt]) if (boxes[boxCoordInt])
{ {
gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, IM_COL32(0xF0, 0xA0, 0x60, 0x80)); ImU32 selectionColor = GetColorU32(SELECTION);
gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, selectionColor);
if (io.MouseDown[0] && !isClicking && !isDraging && GImGui->ActiveId == 0) { if (io.MouseDown[0] && !isClicking && !isDraging && GImGui->ActiveId == 0) {
overBox = boxCoordInt; overBox = boxCoordInt;
@@ -2917,7 +3064,7 @@ namespace IMGUIZMO_NAMESPACE
vec_t newEye = camTarget + newDir * length; vec_t newEye = camTarget + newDir * length;
LookAt(&newEye.x, &camTarget.x, &newUp.x, view); LookAt(&newEye.x, &camTarget.x, &newUp.x, view);
} }
isInside = gContext.mbMouseOver && ImRect(position, position + size).Contains(io.MousePos); gContext.mIsViewManipulatorHovered = gContext.mbMouseOver && ImRect(position, position + size).Contains(io.MousePos);
if (io.MouseDown[0] && (fabsf(io.MouseDelta[0]) || fabsf(io.MouseDelta[1])) && isClicking) if (io.MouseDown[0] && (fabsf(io.MouseDelta[0]) || fabsf(io.MouseDelta[1])) && isClicking)
{ {
@@ -2990,6 +3137,15 @@ namespace IMGUIZMO_NAMESPACE
LookAt(&newEye.x, &camTarget.x, &referenceUp.x, view); LookAt(&newEye.x, &camTarget.x, &referenceUp.x, view);
} }
gContext.mbUsingViewManipulate = (interpolationFrames != 0) || isDraging;
if (isClicking || gContext.mbUsingViewManipulate || gContext.mIsViewManipulatorHovered) {
#if IMGUI_VERSION_NUM >= 18723
ImGui::SetNextFrameWantCaptureMouse(true);
#else
ImGui::CaptureMouseFromApp();
#endif
}
// restore view/projection because it was used to compute ray // restore view/projection because it was used to compute ray
ComputeContext(svgView.m16, svgProjection.m16, gContext.mModelSource.m16, gContext.mMode); ComputeContext(svgView.m16, svgProjection.m16, gContext.mModelSource.m16, gContext.mMode);
} }
+38 -4
View File
@@ -1,5 +1,5 @@
// https://github.com/CedricGuillemet/ImGuizmo // https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP // v1.91.3 WIP
// //
// The MIT License(MIT) // The MIT License(MIT)
// //
@@ -39,9 +39,9 @@
// - display rotation/translation/scale infos in local/world space and not only local // - display rotation/translation/scale infos in local/world space and not only local
// - finish local/world matrix application // - finish local/world matrix application
// - OPERATION as bitmask // - OPERATION as bitmask
// //
// ------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------
// Example // Example
#if 0 #if 0
void EditTransform(const Camera& camera, matrix_t& matrix) void EditTransform(const Camera& camera, matrix_t& matrix)
{ {
@@ -115,6 +115,8 @@ void EditTransform(const Camera& camera, matrix_t& matrix)
#define IMGUIZMO_NAMESPACE ImGuizmo #define IMGUIZMO_NAMESPACE ImGuizmo
#endif #endif
struct ImGuiWindow;
namespace IMGUIZMO_NAMESPACE namespace IMGUIZMO_NAMESPACE
{ {
// call inside your own window and before Manipulate() in order to draw gizmo to that window. // call inside your own window and before Manipulate() in order to draw gizmo to that window.
@@ -136,6 +138,11 @@ namespace IMGUIZMO_NAMESPACE
// return true if mouse IsOver or if the gizmo is in moving state // return true if mouse IsOver or if the gizmo is in moving state
IMGUI_API bool IsUsing(); IMGUI_API bool IsUsing();
// return true if the view gizmo is in moving state
IMGUI_API bool IsUsingViewManipulate();
// only check if your mouse is over the view manipulator - no matter whether it's active or not
IMGUI_API bool IsViewManipulateHovered();
// return true if any gizmo is in moving state // return true if any gizmo is in moving state
IMGUI_API bool IsUsingAny(); IMGUI_API bool IsUsingAny();
@@ -167,7 +174,7 @@ namespace IMGUIZMO_NAMESPACE
IMGUI_API void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize); IMGUI_API void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize);
// call it when you want a gizmo // call it when you want a gizmo
// Needs view and projection matrices. // Needs view and projection matrices.
// matrix parameter is the source matrix (where will be gizmo be drawn) and might be transformed by the function. Return deltaMatrix is optional // matrix parameter is the source matrix (where will be gizmo be drawn) and might be transformed by the function. Return deltaMatrix is optional
// translation is applied in world space // translation is applied in world space
enum OPERATION enum OPERATION
@@ -216,8 +223,31 @@ namespace IMGUIZMO_NAMESPACE
// use this version if you did not call Manipulate before and you are just using ViewManipulate // use this version if you did not call Manipulate before and you are just using ViewManipulate
IMGUI_API void ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor); IMGUI_API void ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
IMGUI_API void SetAlternativeWindow(ImGuiWindow* window);
[[deprecated("Use PushID/PopID instead.")]]
IMGUI_API void SetID(int id); IMGUI_API void SetID(int id);
// ID stack/scopes
// Read the FAQ (docs/FAQ.md or http://dearimgui.org/faq) for more details about how ID are handled in dear imgui.
// - Those questions are answered and impacted by understanding of the ID stack system:
// - "Q: Why is my widget not reacting when I click on it?"
// - "Q: How can I have widgets with an empty label?"
// - "Q: How can I have multiple widgets with the same label?"
// - Short version: ID are hashes of the entire ID stack. If you are creating widgets in a loop you most likely
// want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them.
// - You can also use the "Label##foobar" syntax within widget label to distinguish them from each others.
// - In this header file we use the "label"/"name" terminology to denote a string that will be displayed + used as an ID,
// whereas "str_id" denote a string that is only used as an ID and not normally displayed.
IMGUI_API void PushID(const char* str_id); // push string into the ID stack (will hash string).
IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); // push string into the ID stack (will hash string).
IMGUI_API void PushID(const void* ptr_id); // push pointer into the ID stack (will hash pointer).
IMGUI_API void PushID(int int_id); // push integer into the ID stack (will hash integer).
IMGUI_API void PopID(); // pop from the ID stack.
IMGUI_API ImGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself
IMGUI_API ImGuiID GetID(const char* str_id_begin, const char* str_id_end);
IMGUI_API ImGuiID GetID(const void* ptr_id);
// return true if the cursor is over the operation's gizmo // return true if the cursor is over the operation's gizmo
IMGUI_API bool IsOver(OPERATION op); IMGUI_API bool IsOver(OPERATION op);
IMGUI_API void SetGizmoSizeClipSpace(float value); IMGUI_API void SetGizmoSizeClipSpace(float value);
@@ -229,8 +259,12 @@ namespace IMGUIZMO_NAMESPACE
// Configure the limit where axis are hidden // Configure the limit where axis are hidden
IMGUI_API void SetAxisLimit(float value); IMGUI_API void SetAxisLimit(float value);
// Set an axis mask to permanently hide a given axis (true -> hidden, false -> shown)
IMGUI_API void SetAxisMask(bool x, bool y, bool z);
// Configure the limit where planes are hiden // Configure the limit where planes are hiden
IMGUI_API void SetPlaneLimit(float value); IMGUI_API void SetPlaneLimit(float value);
// from a x,y,z point in space and using Manipulation view/projection matrix, check if mouse is in pixel radius distance of that projected point
IMGUI_API bool IsOver(float* position, float pixelRadius);
enum COLOR enum COLOR
{ {
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2014-2021 Omar Cornut Copyright (c) 2014-2025 Omar Cornut
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+15 -6
View File
@@ -29,7 +29,6 @@
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names. //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
//---- Disable all of Dear ImGui or don't implement standard windows/tools. //---- Disable all of Dear ImGui or don't implement standard windows/tools.
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp. // It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
@@ -43,12 +42,13 @@
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME). //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default io.PlatformOpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")). //#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded font (ProggyClean.ttf), remove ~9.5 KB from output binary. AddFontDefault() will assert.
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available //#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
//---- Enable Test Engine / Automation features. //---- Enable Test Engine / Automation features.
@@ -59,9 +59,12 @@
//#define IMGUI_INCLUDE_IMGUI_USER_H //#define IMGUI_INCLUDE_IMGUI_USER_H
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h" //#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another) //---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support.
//#define IMGUI_USE_BGRA_PACKED_COLOR //#define IMGUI_USE_BGRA_PACKED_COLOR
//---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate.
//#define IMGUI_USE_LEGACY_CRC32_ADLER
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...) //---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
//#define IMGUI_USE_WCHAR32 //#define IMGUI_USE_WCHAR32
@@ -83,10 +86,12 @@
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. // On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
//#define IMGUI_ENABLE_FREETYPE //#define IMGUI_ENABLE_FREETYPE
//---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT) //---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
// Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided).
// Only works in combination with IMGUI_ENABLE_FREETYPE. // Only works in combination with IMGUI_ENABLE_FREETYPE.
// (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement) // - plutosvg is currently easier to install, as e.g. it is part of vcpkg. It will support more fonts and may load them faster. See misc/freetype/README for instructions.
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
//#define IMGUI_ENABLE_FREETYPE_LUNASVG //#define IMGUI_ENABLE_FREETYPE_LUNASVG
//---- Use stb_truetype to build and rasterize the font atlas (default) //---- Use stb_truetype to build and rasterize the font atlas (default)
@@ -124,6 +129,10 @@
//#define IM_DEBUG_BREAK IM_ASSERT(0) //#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak() //#define IM_DEBUG_BREAK __debugbreak()
//---- Debug Tools: Enable highlight ID conflicts _before_ hovering items. When io.ConfigDebugHighlightIdConflicts is set.
// (THIS WILL SLOW DOWN DEAR IMGUI. Only use occasionally and disable after use)
//#define IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS
//---- Debug Tools: Enable slower asserts //---- Debug Tools: Enable slower asserts
//#define IMGUI_DEBUG_PARANOID //#define IMGUI_DEBUG_PARANOID
+3115 -1501
View File
File diff suppressed because it is too large Load Diff
+924 -432
View File
File diff suppressed because it is too large Load Diff
+2246 -1725
View File
File diff suppressed because it is too large Load Diff
+2723 -1051
View File
File diff suppressed because it is too large Load Diff
+100 -30
View File
@@ -80,7 +80,7 @@ ImGui_ImplRW_RenderDrawLists(ImDrawData* draw_data)
if(pcmd->UserCallback) if(pcmd->UserCallback)
pcmd->UserCallback(cmd_list, pcmd); pcmd->UserCallback(cmd_list, pcmd);
else{ else{
rw::Texture *tex = (rw::Texture*)pcmd->TextureId; rw::Texture *tex = (rw::Texture*)pcmd->GetTexID();
if(tex && tex->raster){ if(tex && tex->raster){
rw::SetRenderStatePtr(rw::TEXTURERASTER, tex->raster); rw::SetRenderStatePtr(rw::TEXTURERASTER, tex->raster);
rw::SetRenderState(rw::TEXTUREADDRESSU, tex->getAddressU()); rw::SetRenderState(rw::TEXTUREADDRESSU, tex->getAddressU());
@@ -116,26 +116,6 @@ ImGui_ImplRW_Init(void)
ImGui::CreateContext(); ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO(); ImGuiIO &io = ImGui::GetIO();
io.KeyMap[ImGuiKey_Tab] = KEY_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = KEY_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = KEY_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = KEY_UP;
io.KeyMap[ImGuiKey_DownArrow] = KEY_DOWN;
io.KeyMap[ImGuiKey_PageUp] = KEY_PGUP;
io.KeyMap[ImGuiKey_PageDown] = KEY_PGDN;
io.KeyMap[ImGuiKey_Home] = KEY_HOME;
io.KeyMap[ImGuiKey_End] = KEY_END;
io.KeyMap[ImGuiKey_Delete] = KEY_DEL;
io.KeyMap[ImGuiKey_Backspace] = KEY_BACKSP;
io.KeyMap[ImGuiKey_Enter] = KEY_ENTER;
io.KeyMap[ImGuiKey_Escape] = KEY_ESC;
io.KeyMap[ImGuiKey_A] = 'A';
io.KeyMap[ImGuiKey_C] = 'C';
io.KeyMap[ImGuiKey_V] = 'V';
io.KeyMap[ImGuiKey_X] = 'X';
io.KeyMap[ImGuiKey_Y] = 'Y';
io.KeyMap[ImGuiKey_Z] = 'Z';
return true; return true;
} }
@@ -189,9 +169,9 @@ ImGui_ImplRW_NewFrame(float timeDelta)
io.DisplaySize = ImVec2(sk::globals.width, sk::globals.height); io.DisplaySize = ImVec2(sk::globals.width, sk::globals.height);
io.DeltaTime = timeDelta; io.DeltaTime = timeDelta;
io.KeyCtrl = io.KeysDown[sk::KEY_LCTRL] || io.KeysDown[sk::KEY_RCTRL]; io.KeyCtrl = ImGui::IsKeyPressed(ImGuiKey_LeftCtrl) || ImGui::IsKeyPressed(ImGuiKey_RightCtrl);
io.KeyShift = io.KeysDown[sk::KEY_LSHIFT] || io.KeysDown[sk::KEY_RSHIFT]; io.KeyShift = ImGui::IsKeyPressed(ImGuiKey_LeftShift) || ImGui::IsKeyPressed(ImGuiKey_RightShift);
io.KeyAlt = io.KeysDown[sk::KEY_LALT] || io.KeysDown[sk::KEY_RALT]; io.KeyAlt = ImGui::IsKeyPressed(ImGuiKey_LeftAlt) || ImGui::IsKeyPressed(ImGuiKey_RightAlt);
io.KeySuper = false; io.KeySuper = false;
if(io.WantSetMousePos) if(io.WantSetMousePos)
@@ -200,6 +180,100 @@ ImGui_ImplRW_NewFrame(float timeDelta)
ImGui::NewFrame(); ImGui::NewFrame();
} }
static ImGuiKey SkKeyToImGuiKey(int keycode)
{
switch (keycode) {
case ' ': return ImGuiKey_Space;
case '\'': return ImGuiKey_Apostrophe;
case '-': return ImGuiKey_Minus;
case '.': return ImGuiKey_Period;
case '/': return ImGuiKey_Slash;
case '0': return ImGuiKey_0;
case '1': return ImGuiKey_1;
case '2': return ImGuiKey_2;
case '3': return ImGuiKey_3;
case '4': return ImGuiKey_4;
case '5': return ImGuiKey_5;
case '6': return ImGuiKey_6;
case '7': return ImGuiKey_7;
case '8': return ImGuiKey_8;
case '9': return ImGuiKey_9;
case ';': return ImGuiKey_Semicolon;
case '=': return ImGuiKey_Equal;
case 'A': return ImGuiKey_A;
case 'B': return ImGuiKey_B;
case 'C': return ImGuiKey_C;
case 'D': return ImGuiKey_D;
case 'E': return ImGuiKey_E;
case 'F': return ImGuiKey_F;
case 'G': return ImGuiKey_G;
case 'H': return ImGuiKey_H;
case 'I': return ImGuiKey_I;
case 'J': return ImGuiKey_J;
case 'K': return ImGuiKey_K;
case 'L': return ImGuiKey_L;
case 'M': return ImGuiKey_M;
case 'N': return ImGuiKey_N;
case 'O': return ImGuiKey_O;
case 'P': return ImGuiKey_P;
case 'Q': return ImGuiKey_Q;
case 'R': return ImGuiKey_R;
case 'S': return ImGuiKey_S;
case 'T': return ImGuiKey_T;
case 'U': return ImGuiKey_U;
case 'V': return ImGuiKey_V;
case 'W': return ImGuiKey_W;
case 'X': return ImGuiKey_X;
case 'Y': return ImGuiKey_Y;
case 'Z': return ImGuiKey_Z;
case '[': return ImGuiKey_LeftBracket;
case '\\': return ImGuiKey_Backslash;
case ']': return ImGuiKey_RightBracket;
case '`': return ImGuiKey_GraveAccent;
case sk::KEY_ESC: return ImGuiKey_Escape;
case sk::KEY_ENTER: return ImGuiKey_Enter;
case sk::KEY_TAB: return ImGuiKey_Tab;
case sk::KEY_BACKSP: return ImGuiKey_Backspace;
case sk::KEY_INS: return ImGuiKey_Insert;
case sk::KEY_DEL: return ImGuiKey_Delete;
case sk::KEY_RIGHT: return ImGuiKey_RightArrow;
case sk::KEY_LEFT: return ImGuiKey_LeftArrow;
case sk::KEY_DOWN: return ImGuiKey_DownArrow;
case sk::KEY_UP: return ImGuiKey_UpArrow;
case sk::KEY_PGUP: return ImGuiKey_PageUp;
case sk::KEY_PGDN: return ImGuiKey_PageDown;
case sk::KEY_HOME: return ImGuiKey_Home;
case sk::KEY_END: return ImGuiKey_End;
case sk::KEY_CAPSLK: return ImGuiKey_CapsLock;
case sk::KEY_F1: return ImGuiKey_F1;
case sk::KEY_F2: return ImGuiKey_F2;
case sk::KEY_F3: return ImGuiKey_F3;
case sk::KEY_F4: return ImGuiKey_F4;
case sk::KEY_F5: return ImGuiKey_F5;
case sk::KEY_F6: return ImGuiKey_F6;
case sk::KEY_F7: return ImGuiKey_F7;
case sk::KEY_F8: return ImGuiKey_F8;
case sk::KEY_F9: return ImGuiKey_F9;
case sk::KEY_F10: return ImGuiKey_F10;
case sk::KEY_F11: return ImGuiKey_F11;
case sk::KEY_F12: return ImGuiKey_F12;
case sk::KEY_LSHIFT: return ImGuiKey_LeftShift;
case sk::KEY_LCTRL: return ImGuiKey_LeftCtrl;
case sk::KEY_LALT: return ImGuiKey_LeftAlt;
case sk::KEY_RSHIFT: return ImGuiKey_RightShift;
case sk::KEY_RCTRL: return ImGuiKey_RightCtrl;
case sk::KEY_RALT: return ImGuiKey_RightAlt;
case sk::KEY_NULL: return ImGuiKey_None;
}
return ImGuiKey_None;
}
sk::EventStatus sk::EventStatus
ImGuiEventHandler(sk::Event e, void *param) ImGuiEventHandler(sk::Event e, void *param)
{ {
@@ -211,14 +285,10 @@ ImGuiEventHandler(sk::Event e, void *param)
switch(e){ switch(e){
case KEYDOWN: case KEYDOWN:
c = *(int*)param; io.AddKeyEvent(SkKeyToImGuiKey(*(int*)param), true);
if(c < 256)
io.KeysDown[c] = 1;
return EVENTPROCESSED; return EVENTPROCESSED;
case KEYUP: case KEYUP:
c = *(int*)param; io.AddKeyEvent(SkKeyToImGuiKey(*(int*)param), false);
if(c < 256)
io.KeysDown[c] = 0;
return EVENTPROCESSED; return EVENTPROCESSED;
case CHARINPUT: case CHARINPUT:
c = (uint)(uintptr)param; c = (uint)(uintptr)param;
File diff suppressed because it is too large Load Diff
+196 -85
View File
@@ -1,4 +1,4 @@
// dear imgui, v1.91.0 // dear imgui, v1.92.2b
// (tables and columns code) // (tables and columns code)
/* /*
@@ -221,6 +221,7 @@ Index of this file:
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok. #pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
#pragma clang diagnostic ignored "-Wformat" // warning: format specifies type 'int' but the argument has type 'unsigned int'
#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. #pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
@@ -229,9 +230,15 @@ Index of this file:
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated #pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
#pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
#pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label
#elif defined(__GNUC__) #elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
#pragma GCC diagnostic ignored "-Wstrict-overflow"
#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
#endif #endif
@@ -328,13 +335,14 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[]. // - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[].
const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0; const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0;
const ImVec2 avail_size = GetContentRegionAvail(); const ImVec2 avail_size = GetContentRegionAvail();
const ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f); const ImVec2 actual_outer_size = ImTrunc(CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f));
const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size); const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size);
const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows! const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows!
if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size) if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size)
{ {
ItemSize(outer_rect); ItemSize(outer_rect);
ItemAdd(outer_rect, id); ItemAdd(outer_rect, id);
g.NextWindowData.ClearFlags();
return false; return false;
} }
@@ -369,6 +377,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->ColumnsCount = columns_count; table->ColumnsCount = columns_count;
table->IsLayoutLocked = false; table->IsLayoutLocked = false;
table->InnerWidth = inner_width; table->InnerWidth = inner_width;
table->NavLayer = (ImS8)outer_window->DC.NavLayerCurrent;
temp_data->UserOuterSize = outer_size; temp_data->UserOuterSize = outer_size;
// Instance data (for instance 0, TableID == TableInstanceID) // Instance data (for instance 0, TableID == TableInstanceID)
@@ -409,11 +418,15 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// Reset scroll if we are reactivating it // Reset scroll if we are reactivating it
if ((previous_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0) if ((previous_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0)
SetNextWindowScroll(ImVec2(0.0f, 0.0f)); if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasScroll) == 0)
SetNextWindowScroll(ImVec2(0.0f, 0.0f));
// Create scrolling region (without border and zero window padding) // Create scrolling region (without border and zero window padding)
ImGuiWindowFlags child_window_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None; ImGuiChildFlags child_child_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : ImGuiChildFlags_None;
BeginChildEx(name, instance_id, outer_rect.GetSize(), ImGuiChildFlags_None, child_window_flags); ImGuiWindowFlags child_window_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasWindowFlags) ? g.NextWindowData.WindowFlags : ImGuiWindowFlags_None;
if (flags & ImGuiTableFlags_ScrollX)
child_window_flags |= ImGuiWindowFlags_HorizontalScrollbar;
BeginChildEx(name, instance_id, outer_rect.GetSize(), child_child_flags, child_window_flags);
table->InnerWindow = g.CurrentWindow; table->InnerWindow = g.CurrentWindow;
table->WorkRect = table->InnerWindow->WorkRect; table->WorkRect = table->InnerWindow->WorkRect;
table->OuterRect = table->InnerWindow->Rect(); table->OuterRect = table->InnerWindow->Rect();
@@ -438,6 +451,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable(). // But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable().
table->WorkRect = table->OuterRect = table->InnerRect = outer_rect; table->WorkRect = table->OuterRect = table->InnerRect = outer_rect;
table->HasScrollbarYPrev = table->HasScrollbarYCurr = false; table->HasScrollbarYPrev = table->HasScrollbarYCurr = false;
table->InnerWindow->DC.TreeDepth++; // This is designed to always linking ImGuiTreeNodeFlags_DrawLines linking accross a table
} }
// Push a standardized ID for both child-using and not-child-using tables // Push a standardized ID for both child-using and not-child-using tables
@@ -460,16 +474,27 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size;
inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
// Make left and top borders not overlap our contents by offsetting HostClipRect (#6765) // Make borders not overlap our contents by offsetting HostClipRect (#6765, #7428, #3752)
// (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the // (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the
// limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap // limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap
// problem only affect scrolling tables in this case we can get away with doing it without extra cost). // problem only affect scrolling tables in this case we can get away with doing it without extra cost).
if (inner_window != outer_window) if (inner_window != outer_window)
{ {
// FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window->WindowBorderSize,
// it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with
// different x/y values to BeginChild().
if (flags & ImGuiTableFlags_BordersOuterV) if (flags & ImGuiTableFlags_BordersOuterV)
{
table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x); table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x);
if (inner_window->DecoOuterSizeX2 == 0.0f)
table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - TABLE_BORDER_SIZE, table->HostClipRect.Min.x);
}
if (flags & ImGuiTableFlags_BordersOuterH) if (flags & ImGuiTableFlags_BordersOuterH)
{
table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y); table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y);
if (inner_window->DecoOuterSizeY2 == 0.0f)
table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - TABLE_BORDER_SIZE, table->HostClipRect.Min.y);
}
} }
// Padding and Spacing // Padding and Spacing
@@ -497,7 +522,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect; table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect;
table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width
table->InnerClipRect.ClipWithFull(table->HostClipRect); table->InnerClipRect.ClipWithFull(table->HostClipRect);
table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : inner_window->ClipRect.Max.y; table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : table->HostClipRect.Max.y;
table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow
table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow() table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow()
@@ -517,7 +542,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// Make table current // Make table current
g.CurrentTable = table; g.CurrentTable = table;
outer_window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX(); inner_window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX();
outer_window->DC.CurrentTableIdx = table_idx; outer_window->DC.CurrentTableIdx = table_idx;
if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly. if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly.
inner_window->DC.CurrentTableIdx = table_idx; inner_window->DC.CurrentTableIdx = table_idx;
@@ -555,6 +580,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// Initialize // Initialize
table->SettingsOffset = -1; table->SettingsOffset = -1;
table->IsSortSpecsDirty = true; table->IsSortSpecsDirty = true;
table->IsSettingsDirty = true; // Records itself into .ini file even when in default state (#7934)
table->InstanceInteracted = -1; table->InstanceInteracted = -1;
table->ContextPopupColumn = -1; table->ContextPopupColumn = -1;
table->ReorderColumn = table->ResizedColumn = table->LastResizedColumn = -1; table->ReorderColumn = table->ResizedColumn = table->LastResizedColumn = -1;
@@ -855,7 +881,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping) // Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping)
// Combine width from regular rows + width from headers unless requested not to. // Combine width from regular rows + width from headers unless requested not to.
if (!column->IsPreserveWidthAuto) if (!column->IsPreserveWidthAuto && table->InstanceCurrent == 0)
column->WidthAuto = TableGetColumnWidthAuto(table, column); column->WidthAuto = TableGetColumnWidthAuto(table, column);
// Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto) // Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto)
@@ -954,7 +980,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// [Part 4] Apply final widths based on requested widths // [Part 4] Apply final widths based on requested widths
const ImRect work_rect = table->WorkRect; const ImRect work_rect = table->WorkRect;
const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1); const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1);
const float width_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synched tables with mismatching scrollbar state (#5920) const float width_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synced tables with mismatching scrollbar state (#5920)
const float width_avail = ImMax(1.0f, (((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth()) - width_removed); const float width_avail = ImMax(1.0f, (((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth()) - width_removed);
const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests; const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests;
float width_remaining_for_stretched_columns = width_avail_for_stretched_columns; float width_remaining_for_stretched_columns = width_avail_for_stretched_columns;
@@ -1033,7 +1059,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
const int column_n = table->DisplayOrderToIndex[order_n]; const int column_n = table->DisplayOrderToIndex[order_n];
ImGuiTableColumn* column = &table->Columns[column_n]; ImGuiTableColumn* column = &table->Columns[column_n];
column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); // Use Count NOT request so Header line changes layer when frozen // Initial nav layer: using FreezeRowsCount, NOT FreezeRowsRequest, so Header line changes layer when frozen
column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : (ImGuiNavLayer)table->NavLayer);
if (offset_x_frozen && table->FreezeColumnsCount == visible_n) if (offset_x_frozen && table->FreezeColumnsCount == visible_n)
{ {
@@ -1059,16 +1086,12 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
continue; continue;
} }
// Detect hovered column
if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)
table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
// Lock start position // Lock start position
column->MinX = offset_x; column->MinX = offset_x;
// Lock width based on start position and minimum/maximum width for this position // Lock width based on start position and minimum/maximum width for this position
float max_width = TableGetMaxColumnWidth(table, column_n); column->WidthMax = TableCalcMaxColumnWidth(table, column_n);
column->WidthGiven = ImMin(column->WidthGiven, max_width); column->WidthGiven = ImMin(column->WidthGiven, column->WidthMax);
column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth)); column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth));
column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f; column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f;
@@ -1117,8 +1140,13 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
column->Flags |= ImGuiTableColumnFlags_IsVisible; column->Flags |= ImGuiTableColumnFlags_IsVisible;
if (column->SortOrder != -1) if (column->SortOrder != -1)
column->Flags |= ImGuiTableColumnFlags_IsSorted; column->Flags |= ImGuiTableColumnFlags_IsSorted;
if (table->HoveredColumnBody == column_n)
// Detect hovered column
if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)
{
column->Flags |= ImGuiTableColumnFlags_IsHovered; column->Flags |= ImGuiTableColumnFlags_IsHovered;
table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
}
// Alignment // Alignment
// FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in // FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in
@@ -1148,7 +1176,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
} }
// Don't decrement auto-fit counters until container window got a chance to submit its items // Don't decrement auto-fit counters until container window got a chance to submit its items
if (table->HostSkipItems == false) if (table->HostSkipItems == false && table->InstanceCurrent == 0)
{ {
column->AutoFitQueue >>= 1; column->AutoFitQueue >>= 1;
column->CannotSkipItemsQueue >>= 1; column->CannotSkipItemsQueue >>= 1;
@@ -1223,7 +1251,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// [Part 11] Default context menu // [Part 11] Default context menu
// - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup(). // - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup().
// - To modify or replace this: set table->IsContextPopupNoDefaultContents = true, then call TableBeginContextMenuPopup()/.../EndPopup(). // - To modify or replace this: set table->DisableDefaultContextMenu = true, then call TableBeginContextMenuPopup()/.../EndPopup().
// - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu, // - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu,
// e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options. // e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options.
if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table)) if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table))
@@ -1249,7 +1277,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
if (table->Flags & ImGuiTableFlags_NoClip) if (table->Flags & ImGuiTableFlags_NoClip)
table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);
else else
inner_window->DrawList->PushClipRect(inner_window->ClipRect.Min, inner_window->ClipRect.Max, false); inner_window->DrawList->PushClipRect(inner_window->InnerClipRect.Min, inner_window->InnerClipRect.Max, false); // FIXME: use table->InnerClipRect?
} }
// Process hit-testing on resizing borders. Actual size change will be applied in EndTable() // Process hit-testing on resizing borders. Actual size change will be applied in EndTable()
@@ -1319,7 +1347,11 @@ void ImGui::EndTable()
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable; ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Only call EndTable() if BeginTable() returns true!"); if (table == NULL)
{
IM_ASSERT_USER_ERROR(table != NULL, "EndTable() call should only be done while in BeginTable() scope!");
return;
}
// This assert would be very useful to catch a common error... unfortunately it would probably trigger in some // This assert would be very useful to catch a common error... unfortunately it would probably trigger in some
// cases, and for consistency user may sometimes output empty tables (and still benefit from e.g. outer border) // cases, and for consistency user may sometimes output empty tables (and still benefit from e.g. outer border)
@@ -1361,7 +1393,7 @@ void ImGui::EndTable()
// Setup inner scrolling range // Setup inner scrolling range
// FIXME: This ideally should be done earlier, in BeginTable() SetNextWindowContentSize call, just like writing to inner_window->DC.CursorMaxPos.y, // FIXME: This ideally should be done earlier, in BeginTable() SetNextWindowContentSize call, just like writing to inner_window->DC.CursorMaxPos.y,
// but since the later is likely to be impossible to do we'd rather update both axises together. // but since the later is likely to be impossible to do we'd rather update both axes together.
if (table->Flags & ImGuiTableFlags_ScrollX) if (table->Flags & ImGuiTableFlags_ScrollX)
{ {
const float outer_padding_for_border = (table->Flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f; const float outer_padding_for_border = (table->Flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f;
@@ -1471,12 +1503,15 @@ void ImGui::EndTable()
if (inner_window != outer_window) if (inner_window != outer_window)
{ {
short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask; short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask;
inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently. inner_window->DC.NavLayersActiveMask |= 1 << table->NavLayer; // So empty table don't appear to navigate differently.
g.CurrentTable = NULL; // To avoid error recovery recursing
EndChild(); EndChild();
g.CurrentTable = table;
inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask; inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask;
} }
else else
{ {
table->InnerWindow->DC.TreeDepth--;
ItemSize(table->OuterRect.GetSize()); ItemSize(table->OuterRect.GetSize());
ItemAdd(table->OuterRect, 0); ItemAdd(table->OuterRect, 0);
} }
@@ -1534,14 +1569,43 @@ void ImGui::EndTable()
NavUpdateCurrentWindowIsScrollPushableX(); NavUpdateCurrentWindowIsScrollPushableX();
} }
// Called in TableSetupColumn() when initializing and in TableLoadSettings() for defaults before applying stored settings.
// 'init_mask' specify which fields to initialize.
static void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask)
{
ImGuiTableColumnFlags flags = column->Flags;
if (init_mask & ImGuiTableFlags_Resizable)
{
float init_width_or_weight = column->InitStretchWeightOrWidth;
column->WidthRequest = ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f) ? init_width_or_weight : -1.0f;
column->StretchWeight = (init_width_or_weight > 0.0f && (flags & ImGuiTableColumnFlags_WidthStretch)) ? init_width_or_weight : -1.0f;
if (init_width_or_weight > 0.0f) // Disable auto-fit if an explicit width/weight has been specified
column->AutoFitQueue = 0x00;
}
if (init_mask & ImGuiTableFlags_Reorderable)
column->DisplayOrder = (ImGuiTableColumnIdx)table->Columns.index_from_ptr(column);
if (init_mask & ImGuiTableFlags_Hideable)
column->IsUserEnabled = column->IsUserEnabledNextFrame = (flags & ImGuiTableColumnFlags_DefaultHide) ? 0 : 1;
if (init_mask & ImGuiTableFlags_Sortable)
{
// Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs.
column->SortOrder = (flags & ImGuiTableColumnFlags_DefaultSort) ? 0 : -1;
column->SortDirection = (flags & ImGuiTableColumnFlags_DefaultSort) ? ((flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending)) : (ImS8)ImGuiSortDirection_None;
}
}
// See "COLUMNS SIZING POLICIES" comments at the top of this file // See "COLUMNS SIZING POLICIES" comments at the top of this file
// If (init_width_or_weight <= 0.0f) it is ignored // If (init_width_or_weight <= 0.0f) it is ignored
void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id) void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable; ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!"); if (table == NULL)
IM_ASSERT(table->IsLayoutLocked == false && "Need to call call TableSetupColumn() before first row!"); {
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
return;
}
IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!");
IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()"); IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()");
if (table->DeclColumnsCount >= table->ColumnsCount) if (table->DeclColumnsCount >= table->ColumnsCount)
{ {
@@ -1558,7 +1622,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
IM_ASSERT(init_width_or_weight <= 0.0f && "Can only specify width/weight if sizing policy is set explicitly in either Table or Column."); IM_ASSERT(init_width_or_weight <= 0.0f && "Can only specify width/weight if sizing policy is set explicitly in either Table or Column.");
// When passing a width automatically enforce WidthFixed policy // When passing a width automatically enforce WidthFixed policy
// (whereas TableSetupColumnFlags would default to WidthAuto if table is not Resizable) // (whereas TableSetupColumnFlags would default to WidthAuto if table is not resizable)
if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f) if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f)
if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame) if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame)
flags |= ImGuiTableColumnFlags_WidthFixed; flags |= ImGuiTableColumnFlags_WidthFixed;
@@ -1576,27 +1640,10 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
column->InitStretchWeightOrWidth = init_width_or_weight; column->InitStretchWeightOrWidth = init_width_or_weight;
if (table->IsInitializing) if (table->IsInitializing)
{ {
// Init width or weight ImGuiTableFlags init_flags = ~table->SettingsLoadedFlags;
if (column->WidthRequest < 0.0f && column->StretchWeight < 0.0f) if (column->WidthRequest < 0.0f && column->StretchWeight < 0.0f)
{ init_flags |= ImGuiTableFlags_Resizable;
if ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f) TableInitColumnDefaults(table, column, init_flags);
column->WidthRequest = init_width_or_weight;
if (flags & ImGuiTableColumnFlags_WidthStretch)
column->StretchWeight = (init_width_or_weight > 0.0f) ? init_width_or_weight : -1.0f;
// Disable auto-fit if an explicit width/weight has been specified
if (init_width_or_weight > 0.0f)
column->AutoFitQueue = 0x00;
}
// Init default visibility/sort state
if ((flags & ImGuiTableColumnFlags_DefaultHide) && (table->SettingsLoadedFlags & ImGuiTableFlags_Hideable) == 0)
column->IsUserEnabled = column->IsUserEnabledNextFrame = false;
if (flags & ImGuiTableColumnFlags_DefaultSort && (table->SettingsLoadedFlags & ImGuiTableFlags_Sortable) == 0)
{
column->SortOrder = 0; // Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs.
column->SortDirection = (column->Flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending);
}
} }
// Store name (append with zero-terminator in contiguous buffer) // Store name (append with zero-terminator in contiguous buffer)
@@ -1605,7 +1652,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
if (label != NULL && label[0] != 0) if (label != NULL && label[0] != 0)
{ {
column->NameOffset = (ImS16)table->ColumnsNames.size(); column->NameOffset = (ImS16)table->ColumnsNames.size();
table->ColumnsNames.append(label, label + strlen(label) + 1); table->ColumnsNames.append(label, label + ImStrlen(label) + 1);
} }
} }
@@ -1614,7 +1661,11 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable; ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!"); if (table == NULL)
{
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
return;
}
IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!"); IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!");
IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS); IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS);
IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit
@@ -1691,9 +1742,11 @@ void ImGui::TableSetColumnEnabled(int column_n, bool enabled)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable; ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL); if (table == NULL)
if (!table) {
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
return; return;
}
IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above
if (column_n < 0) if (column_n < 0)
column_n = table->CurrentColumn; column_n = table->CurrentColumn;
@@ -1772,6 +1825,11 @@ void ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable; ImGuiTable* table = g.CurrentTable;
IM_ASSERT(target != ImGuiTableBgTarget_None); IM_ASSERT(target != ImGuiTableBgTarget_None);
if (table == NULL)
{
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
return;
}
if (color == IM_COL32_DISABLE) if (color == IM_COL32_DISABLE)
color = 0; color = 0;
@@ -1900,7 +1958,10 @@ void ImGui::TableEndRow(ImGuiTable* table)
IM_ASSERT(table->IsInsideRow); IM_ASSERT(table->IsInsideRow);
if (table->CurrentColumn != -1) if (table->CurrentColumn != -1)
{
TableEndCell(table); TableEndCell(table);
table->CurrentColumn = -1;
}
// Logging // Logging
if (g.LogEnabled) if (g.LogEnabled)
@@ -1998,8 +2059,8 @@ void ImGui::TableEndRow(ImGuiTable* table)
if (unfreeze_rows_request) if (unfreeze_rows_request)
{ {
for (int column_n = 0; column_n < table->ColumnsCount; column_n++) for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main; table->Columns[column_n].NavLayerCurrent = table->NavLayer;
const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y); const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y);
table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y; table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;
if (unfreeze_rows_actual) if (unfreeze_rows_actual)
@@ -2008,8 +2069,8 @@ void ImGui::TableEndRow(ImGuiTable* table)
table->IsUnfrozenRows = true; table->IsUnfrozenRows = true;
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y); table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, table->InnerClipRect.Max.y);
table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y; table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = table->InnerClipRect.Max.y;
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen; table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y); IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
@@ -2065,7 +2126,11 @@ bool ImGui::TableSetColumnIndex(int column_n)
{ {
if (table->CurrentColumn != -1) if (table->CurrentColumn != -1)
TableEndCell(table); TableEndCell(table);
IM_ASSERT(column_n >= 0 && table->ColumnsCount); if ((column_n >= 0 && column_n < table->ColumnsCount) == false)
{
IM_ASSERT_USER_ERROR(column_n >= 0 && column_n < table->ColumnsCount, "TableSetColumnIndex() invalid column index!");
return false;
}
TableBeginCell(table, column_n); TableBeginCell(table, column_n);
} }
@@ -2136,6 +2201,7 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n)
g.LastItemData.StatusFlags = 0; g.LastItemData.StatusFlags = 0;
} }
// Also see TablePushColumnChannel()
if (table->Flags & ImGuiTableFlags_NoClip) if (table->Flags & ImGuiTableFlags_NoClip)
{ {
// FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed. // FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed.
@@ -2195,8 +2261,8 @@ void ImGui::TableEndCell(ImGuiTable* table)
// Note that actual columns widths are computed in TableUpdateLayout(). // Note that actual columns widths are computed in TableUpdateLayout().
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// Maximum column content width given current layout. Use column->MinX so this value on a per-column basis. // Maximum column content width given current layout. Use column->MinX so this value differs on a per-column basis.
float ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n) float ImGui::TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n)
{ {
const ImGuiTableColumn* column = &table->Columns[column_n]; const ImGuiTableColumn* column = &table->Columns[column_n];
float max_width = FLT_MAX; float max_width = FLT_MAX;
@@ -2258,7 +2324,7 @@ void ImGui::TableSetColumnWidth(int column_n, float width)
// Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded) // Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)
IM_ASSERT(table->MinColumnWidth > 0.0f); IM_ASSERT(table->MinColumnWidth > 0.0f);
const float min_width = table->MinColumnWidth; const float min_width = table->MinColumnWidth;
const float max_width = ImMax(min_width, TableGetMaxColumnWidth(table, column_n)); const float max_width = ImMax(min_width, column_0->WidthMax); // Don't use TableCalcMaxColumnWidth() here as it would rely on MinX from last instance (#7933)
column_0_width = ImClamp(column_0_width, min_width, max_width); column_0_width = ImClamp(column_0_width, min_width, max_width);
if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width) if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width)
return; return;
@@ -2409,10 +2475,38 @@ void ImGui::TablePopBackgroundChannel()
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* window = g.CurrentWindow;
ImGuiTable* table = g.CurrentTable; ImGuiTable* table = g.CurrentTable;
ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
// Optimization: avoid PopClipRect() + SetCurrentChannel() // Optimization: avoid PopClipRect() + SetCurrentChannel()
SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect); SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[table->CurrentColumn].DrawChannelCurrent);
}
// Also see TableBeginCell()
void ImGui::TablePushColumnChannel(int column_n)
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
// Optimization: avoid SetCurrentChannel() + PushClipRect()
if (table->Flags & ImGuiTableFlags_NoClip)
return;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiTableColumn* column = &table->Columns[column_n];
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
}
void ImGui::TablePopColumnChannel()
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
// Optimization: avoid PopClipRect() + SetCurrentChannel()
if ((table->Flags & ImGuiTableFlags_NoClip) || (table->CurrentColumn == -1)) // Calling TreePop() after TableNextRow() is supported.
return;
ImGuiWindow* window = g.CurrentWindow;
const ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
} }
@@ -2743,7 +2837,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
const ImU32 outer_col = table->BorderColorStrong; const ImU32 outer_col = table->BorderColorStrong;
if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter) if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter)
{ {
inner_drawlist->AddRect(outer_border.Min, outer_border.Max + ImVec2(1, 1), outer_col, 0.0f, 0, border_size); inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size);
} }
else if (table->Flags & ImGuiTableFlags_BordersOuterV) else if (table->Flags & ImGuiTableFlags_BordersOuterV)
{ {
@@ -2787,9 +2881,7 @@ ImGuiTableSortSpecs* ImGui::TableGetSortSpecs()
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable; ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL); if (table == NULL || !(table->Flags & ImGuiTableFlags_Sortable))
if (!(table->Flags & ImGuiTableFlags_Sortable))
return NULL; return NULL;
// Require layout (in case TableHeadersRow() hasn't been called) as it may alter IsSortSpecsDirty in some paths. // Require layout (in case TableHeadersRow() hasn't been called) as it may alter IsSortSpecsDirty in some paths.
@@ -3007,15 +3099,21 @@ float ImGui::TableGetHeaderAngledMaxLabelWidth()
// The intent is that advanced users willing to create customized headers would not need to use this helper // The intent is that advanced users willing to create customized headers would not need to use this helper
// and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets. // and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets.
// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this. // See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.
// This code is constructed to not make much use of internal functions, as it is intended to be a template to copy. // This code is intentionally written to not make much use of internal functions, to give you better direction
// if you need to write your own.
// FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public. // FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public.
void ImGui::TableHeadersRow() void ImGui::TableHeadersRow()
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable; ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!"); if (table == NULL)
{
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
return;
}
// Layout if not already done (this is automatically done by TableNextRow, we do it here solely to facilitate stepping in debugger as it is frequent to step in TableUpdateLayout) // Call layout if not already done. This is automatically done by TableNextRow: we do it here _only_ to make
// it easier to debug-step in TableUpdateLayout(). Your own version of this function doesn't need this.
if (!table->IsLayoutLocked) if (!table->IsLayoutLocked)
TableUpdateLayout(table); TableUpdateLayout(table);
@@ -3032,8 +3130,7 @@ void ImGui::TableHeadersRow()
if (!TableSetColumnIndex(column_n)) if (!TableSetColumnIndex(column_n))
continue; continue;
// Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them) // Push an id to allow empty/unnamed headers. This is also idiomatic as it ensure there is a consistent ID path to access columns (for e.g. automation)
// In your own code you may omit the PushID/PopID all-together, provided you know they won't collide.
const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n); const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n);
PushID(column_n); PushID(column_n);
TableHeader(name); TableHeader(name);
@@ -3058,7 +3155,12 @@ void ImGui::TableHeader(const char* label)
return; return;
ImGuiTable* table = g.CurrentTable; ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Need to call TableHeader() after BeginTable()!"); if (table == NULL)
{
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
return;
}
IM_ASSERT(table->CurrentColumn != -1); IM_ASSERT(table->CurrentColumn != -1);
const int column_n = table->CurrentColumn; const int column_n = table->CurrentColumn;
ImGuiTableColumn* column = &table->Columns[column_n]; ImGuiTableColumn* column = &table->Columns[column_n];
@@ -3124,7 +3226,7 @@ void ImGui::TableHeader(const char* label)
if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0) if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0)
TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn); TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn);
} }
RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact | ImGuiNavHighlightFlags_NoRounding); RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding);
if (held) if (held)
table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n; table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n;
window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f; window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;
@@ -3179,7 +3281,7 @@ void ImGui::TableHeader(const char* label)
// Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will // Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
// be merged into a single draw call. // be merged into a single draw call.
//window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE); //window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size); RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, bb.Max.y), ellipsis_max, label, label_end, &label_size);
const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x); const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
if (text_clipped && hovered && g.ActiveId == 0) if (text_clipped && hovered && g.ActiveId == 0)
@@ -3233,7 +3335,11 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
ImGuiTable* table = g.CurrentTable; ImGuiTable* table = g.CurrentTable;
ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* window = g.CurrentWindow;
ImDrawList* draw_list = window->DrawList; ImDrawList* draw_list = window->DrawList;
IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!"); if (table == NULL)
{
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
return;
}
IM_ASSERT(table->CurrentRow == -1 && "Must be first row"); IM_ASSERT(table->CurrentRow == -1 && "Must be first row");
if (max_label_width == 0.0f) if (max_label_width == 0.0f)
@@ -3272,13 +3378,13 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
ButtonBehavior(row_r, row_id, NULL, NULL); ButtonBehavior(row_r, row_id, NULL, NULL);
KeepAliveID(row_id); KeepAliveID(row_id);
const float ascent_scaled = g.Font->Ascent * g.FontScale; // FIXME: Standardize those scaling factors better const float ascent_scaled = g.FontBaked->Ascent * g.FontBakedScale; // FIXME: Standardize those scaling factors better
const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f); const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f);
const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
const ImVec2 align = g.Style.TableAngledHeadersTextAlign; const ImVec2 align = g.Style.TableAngledHeadersTextAlign;
// Draw background and labels in first pass, then all borders. // Draw background and labels in first pass, then all borders.
float max_x = 0.0f; float max_x = -FLT_MAX;
for (int pass = 0; pass < 2; pass++) for (int pass = 0; pass < 2; pass++)
for (int order_n = 0; order_n < data_count; order_n++) for (int order_n = 0; order_n < data_count; order_n++)
{ {
@@ -3327,7 +3433,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height)); ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height));
int vtx_idx_begin = draw_list->_VtxCurrentIdx; int vtx_idx_begin = draw_list->_VtxCurrentIdx;
PushStyleColor(ImGuiCol_Text, request->TextColor); PushStyleColor(ImGuiCol_Text, request->TextColor);
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, clip_r.Max.x, label_name, label_name_eol, &label_size); RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, label_name, label_name_eol, &label_size);
PopStyleColor(); PopStyleColor();
int vtx_idx_end = draw_list->_VtxCurrentIdx; int vtx_idx_end = draw_list->_VtxCurrentIdx;
@@ -3664,6 +3770,14 @@ void ImGui::TableLoadSettings(ImGuiTable* table)
table->SettingsLoadedFlags = settings->SaveFlags; table->SettingsLoadedFlags = settings->SaveFlags;
table->RefScale = settings->RefScale; table->RefScale = settings->RefScale;
// Initialize default columns settings
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
{
ImGuiTableColumn* column = &table->Columns[column_n];
TableInitColumnDefaults(table, column, ~0);
column->AutoFitQueue = 0x00;
}
// Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn // Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn
ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings(); ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();
ImU64 display_order_mask = 0; ImU64 display_order_mask = 0;
@@ -3680,14 +3794,12 @@ void ImGui::TableLoadSettings(ImGuiTable* table)
column->StretchWeight = column_settings->WidthOrWeight; column->StretchWeight = column_settings->WidthOrWeight;
else else
column->WidthRequest = column_settings->WidthOrWeight; column->WidthRequest = column_settings->WidthOrWeight;
column->AutoFitQueue = 0x00;
} }
if (settings->SaveFlags & ImGuiTableFlags_Reorderable) if (settings->SaveFlags & ImGuiTableFlags_Reorderable)
column->DisplayOrder = column_settings->DisplayOrder; column->DisplayOrder = column_settings->DisplayOrder;
else
column->DisplayOrder = (ImGuiTableColumnIdx)column_n;
display_order_mask |= (ImU64)1 << column->DisplayOrder; display_order_mask |= (ImU64)1 << column->DisplayOrder;
column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled; if ((settings->SaveFlags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1)
column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled == 1;
column->SortOrder = column_settings->SortOrder; column->SortOrder = column_settings->SortOrder;
column->SortDirection = column_settings->SortDirection; column->SortDirection = column_settings->SortDirection;
} }
@@ -3783,8 +3895,7 @@ static void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandle
const bool save_visible = (settings->SaveFlags & ImGuiTableFlags_Hideable) != 0; const bool save_visible = (settings->SaveFlags & ImGuiTableFlags_Hideable) != 0;
const bool save_order = (settings->SaveFlags & ImGuiTableFlags_Reorderable) != 0; const bool save_order = (settings->SaveFlags & ImGuiTableFlags_Reorderable) != 0;
const bool save_sort = (settings->SaveFlags & ImGuiTableFlags_Sortable) != 0; const bool save_sort = (settings->SaveFlags & ImGuiTableFlags_Sortable) != 0;
if (!save_size && !save_visible && !save_order && !save_sort) // We need to save the [Table] entry even if all the bools are false, since this records a table with "default settings".
continue;
buf->reserve(buf->size() + 30 + settings->ColumnsCount * 50); // ballpark reserve buf->reserve(buf->size() + 30 + settings->ColumnsCount * 50); // ballpark reserve
buf->appendf("[%s][0x%08X,%d]\n", handler->TypeName, settings->ID, settings->ColumnsCount); buf->appendf("[%s][0x%08X,%d]\n", handler->TypeName, settings->ID, settings->ColumnsCount);
@@ -4396,7 +4507,7 @@ void ImGui::EndColumns()
{ {
ButtonBehavior(column_hit_rect, column_id, &hovered, &held); ButtonBehavior(column_hit_rect, column_id, &hovered, &held);
if (hovered || held) if (hovered || held)
g.MouseCursor = ImGuiMouseCursor_ResizeEW; SetMouseCursor(ImGuiMouseCursor_ResizeEW);
if (held && !(column->Flags & ImGuiOldColumnFlags_NoResize)) if (held && !(column->Flags & ImGuiOldColumnFlags_NoResize))
dragging_column = n; dragging_column = n;
} }
@@ -4428,12 +4539,12 @@ void ImGui::EndColumns()
NavUpdateCurrentWindowIsScrollPushableX(); NavUpdateCurrentWindowIsScrollPushableX();
} }
void ImGui::Columns(int columns_count, const char* id, bool border) void ImGui::Columns(int columns_count, const char* id, bool borders)
{ {
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(columns_count >= 1); IM_ASSERT(columns_count >= 1);
ImGuiOldColumnFlags flags = (border ? 0 : ImGuiOldColumnFlags_NoBorder); ImGuiOldColumnFlags flags = (borders ? 0 : ImGuiOldColumnFlags_NoBorder);
//flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior //flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior
ImGuiOldColumns* columns = window->DC.CurrentColumns; ImGuiOldColumns* columns = window->DC.CurrentColumns;
if (columns != NULL && columns->Count == columns_count && columns->Flags == flags) if (columns != NULL && columns->Count == columns_count && columns->Flags == flags)
File diff suppressed because it is too large Load Diff
+96 -49
View File
@@ -3,6 +3,8 @@
// Those changes would need to be pushed into nothings/stb: // Those changes would need to be pushed into nothings/stb:
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) // - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783) // - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
// - Added name to struct or it may be forward declared in our code.
// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
// Grep for [DEAR IMGUI] to find the changes. // Grep for [DEAR IMGUI] to find the changes.
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_* // - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
@@ -139,6 +141,7 @@
// with previous char) // with previous char)
// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character // STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character
// (return type is int, -1 means not valid to insert) // (return type is int, -1 means not valid to insert)
// (not supported if you want to use UTF-8, see below)
// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based // STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based
// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize // STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize
// as manually wordwrapping for end-of-line positioning // as manually wordwrapping for end-of-line positioning
@@ -176,6 +179,13 @@
// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text // STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text
// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text // STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text
// //
// To support UTF-8:
//
// STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character
// STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character
// Do NOT define STB_TEXTEDIT_KEYTOTEXT.
// Instead, call stb_textedit_text() directly for text contents.
//
// Keyboard input must be encoded as a single integer value; e.g. a character code // Keyboard input must be encoded as a single integer value; e.g. a character code
// and some bitflags that represent shift states. to simplify the interface, SHIFT must // and some bitflags that represent shift states. to simplify the interface, SHIFT must
// be a bitflag, so we can test the shifted state of cursor movements to allow selection, // be a bitflag, so we can test the shifted state of cursor movements to allow selection,
@@ -209,6 +219,7 @@
// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) // int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) // int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key) // void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key)
// void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len)
// //
// Each of these functions potentially updates the string and updates the // Each of these functions potentially updates the string and updates the
// state. // state.
@@ -243,7 +254,14 @@
// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit // various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit
// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is // set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is
// clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to // clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to
// anything other type you wante before including. // anything other type you want before including.
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
// transformed into text and stb_textedit_text() is automatically called.
//
// text: (added 2025)
// call this to directly send text input the textfield, which is required
// for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()
// cannot infer text length.
// //
// //
// When rendering, you can read the cursor position and selection state from // When rendering, you can read the cursor position and selection state from
@@ -318,7 +336,7 @@ typedef struct
int undo_char_point, redo_char_point; int undo_char_point, redo_char_point;
} StbUndoState; } StbUndoState;
typedef struct typedef struct STB_TexteditState
{ {
///////////////////// /////////////////////
// //
@@ -392,6 +410,16 @@ typedef struct
#define IMSTB_TEXTEDIT_memmove memmove #define IMSTB_TEXTEDIT_memmove memmove
#endif #endif
// [DEAR IMGUI]
// Functions must be implemented for UTF8 support
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(OBJ, IDX) ((IDX) - 1)
#endif
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(OBJ, IDX) ((IDX) + 1)
#endif
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// //
@@ -438,13 +466,13 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
if (x < r.x1) { if (x < r.x1) {
// search characters in row for one that straddles 'x' // search characters in row for one that straddles 'x'
prev_x = r.x0; prev_x = r.x0;
for (k=0; k < r.num_chars; ++k) { for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
float w = STB_TEXTEDIT_GETWIDTH(str, i, k); float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
if (x < prev_x+w) { if (x < prev_x+w) {
if (x < prev_x+w/2) if (x < prev_x+w/2)
return k+i; return k+i;
else else
return k+i+1; return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k);
} }
prev_x += w; prev_x += w;
} }
@@ -563,7 +591,7 @@ static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING
// now scan to find xpos // now scan to find xpos
find->x = r.x0; find->x = r.x0;
for (i=0; first+i < n; ++i) for (i=0; first+i < n; i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i) - first)
find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); find->x += STB_TEXTEDIT_GETWIDTH(str, first, i);
} }
@@ -649,9 +677,9 @@ static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
#ifndef STB_TEXTEDIT_MOVEWORDLEFT #ifndef STB_TEXTEDIT_MOVEWORDLEFT
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c ) static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
{ {
--c; // always move at least one character c = IMSTB_TEXTEDIT_GETPREVCHARINDEX( str, c ); // always move at least one character
while( c >= 0 && !is_word_boundary( str, c ) ) while (c >= 0 && !is_word_boundary(str, c))
--c; c = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, c);
if( c < 0 ) if( c < 0 )
c = 0; c = 0;
@@ -665,9 +693,9 @@ static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c ) static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
{ {
const int len = STB_TEXTEDIT_STRINGLEN(str); const int len = STB_TEXTEDIT_STRINGLEN(str);
++c; // always move at least one character c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c); // always move at least one character
while( c < len && !is_word_boundary( str, c ) ) while( c < len && !is_word_boundary( str, c ) )
++c; c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c);
if( c > len ) if( c > len )
c = len; c = len;
@@ -720,36 +748,45 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
#define STB_TEXTEDIT_KEYTYPE int #define STB_TEXTEDIT_KEYTYPE int
#endif #endif
// API key: process text input
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
{
// can't add newline in single-line mode
if (text[0] == '\n' && state->single_line)
return;
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
state->cursor += text_len;
state->has_preferred_x = 0;
}
} else {
stb_textedit_delete_selection(str, state); // implicitly clamps
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
stb_text_makeundo_insert(state, state->cursor, text_len);
state->cursor += text_len;
state->has_preferred_x = 0;
}
}
}
// API key: process a keyboard input // API key: process a keyboard input
static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key) static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
{ {
retry: retry:
switch (key) { switch (key) {
default: { default: {
#ifdef STB_TEXTEDIT_KEYTOTEXT
// This is not suitable for UTF-8 support.
int c = STB_TEXTEDIT_KEYTOTEXT(key); int c = STB_TEXTEDIT_KEYTOTEXT(key);
if (c > 0) { if (c > 0) {
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE) c; IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
stb_textedit_text(str, state, &ch, 1);
// can't add newline in single-line mode
if (c == '\n' && state->single_line)
break;
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
++state->cursor;
state->has_preferred_x = 0;
}
} else {
stb_textedit_delete_selection(str,state); // implicitly clamps
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
stb_text_makeundo_insert(state, state->cursor, 1);
++state->cursor;
state->has_preferred_x = 0;
}
}
} }
#endif
break; break;
} }
@@ -775,7 +812,7 @@ retry:
stb_textedit_move_to_first(state); stb_textedit_move_to_first(state);
else else
if (state->cursor > 0) if (state->cursor > 0)
--state->cursor; state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->has_preferred_x = 0; state->has_preferred_x = 0;
break; break;
@@ -784,7 +821,7 @@ retry:
if (STB_TEXT_HAS_SELECTION(state)) if (STB_TEXT_HAS_SELECTION(state))
stb_textedit_move_to_last(str, state); stb_textedit_move_to_last(str, state);
else else
++state->cursor; state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
state->has_preferred_x = 0; state->has_preferred_x = 0;
break; break;
@@ -794,7 +831,7 @@ retry:
stb_textedit_prep_selection_at_cursor(state); stb_textedit_prep_selection_at_cursor(state);
// move selection left // move selection left
if (state->select_end > 0) if (state->select_end > 0)
--state->select_end; state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end);
state->cursor = state->select_end; state->cursor = state->select_end;
state->has_preferred_x = 0; state->has_preferred_x = 0;
break; break;
@@ -844,7 +881,7 @@ retry:
case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT: case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:
stb_textedit_prep_selection_at_cursor(state); stb_textedit_prep_selection_at_cursor(state);
// move selection right // move selection right
++state->select_end; state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end);
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
state->cursor = state->select_end; state->cursor = state->select_end;
state->has_preferred_x = 0; state->has_preferred_x = 0;
@@ -891,8 +928,9 @@ retry:
state->cursor = start; state->cursor = start;
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
x = row.x0; x = row.x0;
for (i=0; i < row.num_chars; ++i) { for (i=0; i < row.num_chars; ) {
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE) if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break; break;
@@ -900,7 +938,8 @@ retry:
x += dx; x += dx;
if (x > goal_x) if (x > goal_x)
break; break;
++state->cursor; i += next - state->cursor;
state->cursor = next;
} }
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
@@ -953,8 +992,9 @@ retry:
state->cursor = find.prev_first; state->cursor = find.prev_first;
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
x = row.x0; x = row.x0;
for (i=0; i < row.num_chars; ++i) { for (i=0; i < row.num_chars; ) {
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE) if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break; break;
@@ -962,7 +1002,8 @@ retry:
x += dx; x += dx;
if (x > goal_x) if (x > goal_x)
break; break;
++state->cursor; i += next - state->cursor;
state->cursor = next;
} }
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
@@ -975,8 +1016,13 @@ retry:
// go to previous line // go to previous line
// (we need to scan previous line the hard way. maybe we could expose this as a new API function?) // (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0; prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;
while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE) while (prev_scan > 0)
--prev_scan; {
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, prev_scan);
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
break;
prev_scan = prev;
}
find.first_char = find.prev_first; find.first_char = find.prev_first;
find.prev_first = prev_scan; find.prev_first = prev_scan;
} }
@@ -990,7 +1036,7 @@ retry:
else { else {
int n = STB_TEXTEDIT_STRINGLEN(str); int n = STB_TEXTEDIT_STRINGLEN(str);
if (state->cursor < n) if (state->cursor < n)
stb_textedit_delete(str, state, state->cursor, 1); stb_textedit_delete(str, state, state->cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor) - state->cursor);
} }
state->has_preferred_x = 0; state->has_preferred_x = 0;
break; break;
@@ -1002,8 +1048,9 @@ retry:
else { else {
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
if (state->cursor > 0) { if (state->cursor > 0) {
stb_textedit_delete(str, state, state->cursor-1, 1); int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
--state->cursor; stb_textedit_delete(str, state, prev, state->cursor - prev);
state->cursor = prev;
} }
} }
state->has_preferred_x = 0; state->has_preferred_x = 0;
@@ -1054,7 +1101,7 @@ retry:
if (state->single_line) if (state->single_line)
state->cursor = 0; state->cursor = 0;
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE) else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
--state->cursor; state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->has_preferred_x = 0; state->has_preferred_x = 0;
break; break;
@@ -1066,9 +1113,9 @@ retry:
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
stb_textedit_move_to_first(state); stb_textedit_move_to_first(state);
if (state->single_line) if (state->single_line)
state->cursor = n; state->cursor = n;
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
++state->cursor; state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
state->has_preferred_x = 0; state->has_preferred_x = 0;
break; break;
} }
@@ -1082,7 +1129,7 @@ retry:
if (state->single_line) if (state->single_line)
state->cursor = 0; state->cursor = 0;
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE) else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
--state->cursor; state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->select_end = state->cursor; state->select_end = state->cursor;
state->has_preferred_x = 0; state->has_preferred_x = 0;
break; break;
@@ -1097,7 +1144,7 @@ retry:
if (state->single_line) if (state->single_line)
state->cursor = n; state->cursor = n;
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
++state->cursor; state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
state->select_end = state->cursor; state->select_end = state->cursor;
state->has_preferred_x = 0; state->has_preferred_x = 0;
break; break;
+2 -2
View File
@@ -4516,8 +4516,8 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
q2[0] = (float)x2; q2[0] = (float)x2;
q2[1] = (float)y2; q2[1] = (float)y2;
if (equal(q0,q1) || equal(q1,q2)) { if (equal(q0,q1) || equal(q1,q2)) {
x0 = (int)verts[i-1].x; x0 = (int)verts[i-1].x; //-V1048
y0 = (int)verts[i-1].y; y0 = (int)verts[i-1].y; //-V1048
x1 = (int)verts[i ].x; x1 = (int)verts[i ].x;
y1 = (int)verts[i ].y; y1 = (int)verts[i ].y;
if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
+323
View File
@@ -0,0 +1,323 @@
#ifdef LIBRW_SDL3
#include <rw.h>
#include "skeleton.h"
using namespace sk;
using namespace rw;
#ifdef RW_OPENGL
SDL_Window *window;
static int keyCodeToSkKey(SDL_Keycode keycode) {
switch (keycode) {
case SDLK_SPACE: return ' ';
case SDLK_APOSTROPHE: return '\'';
case SDLK_COMMA: return ',';
case SDLK_MINUS: return '-';
case SDLK_PERIOD: return '.';
case SDLK_SLASH: return '/';
case SDLK_0: return '0';
case SDLK_1: return '1';
case SDLK_2: return '2';
case SDLK_3: return '3';
case SDLK_4: return '4';
case SDLK_5: return '5';
case SDLK_6: return '6';
case SDLK_7: return '7';
case SDLK_8: return '8';
case SDLK_9: return '9';
case SDLK_SEMICOLON: return ';';
case SDLK_EQUALS: return '=';
case SDLK_A: return 'A';
case SDLK_B: return 'B';
case SDLK_C: return 'C';
case SDLK_D: return 'D';
case SDLK_E: return 'E';
case SDLK_F: return 'F';
case SDLK_G: return 'G';
case SDLK_H: return 'H';
case SDLK_I: return 'I';
case SDLK_J: return 'J';
case SDLK_K: return 'K';
case SDLK_L: return 'L';
case SDLK_M: return 'M';
case SDLK_N: return 'N';
case SDLK_O: return 'O';
case SDLK_P: return 'P';
case SDLK_Q: return 'Q';
case SDLK_R: return 'R';
case SDLK_S: return 'S';
case SDLK_T: return 'T';
case SDLK_U: return 'U';
case SDLK_V: return 'V';
case SDLK_W: return 'W';
case SDLK_X: return 'X';
case SDLK_Y: return 'Y';
case SDLK_Z: return 'Z';
case SDLK_LEFTBRACKET: return '[';
case SDLK_BACKSLASH: return '\\';
case SDLK_RIGHTBRACKET: return ']';
case SDLK_GRAVE: return '`';
case SDLK_ESCAPE: return KEY_ESC;
case SDLK_RETURN: return KEY_ENTER;
case SDLK_TAB: return KEY_TAB;
case SDLK_BACKSPACE: return KEY_BACKSP;
case SDLK_INSERT: return KEY_INS;
case SDLK_DELETE: return KEY_DEL;
case SDLK_RIGHT: return KEY_RIGHT;
case SDLK_LEFT: return KEY_LEFT;
case SDLK_DOWN: return KEY_DOWN;
case SDLK_UP: return KEY_UP;
case SDLK_PAGEUP: return KEY_PGUP;
case SDLK_PAGEDOWN: return KEY_PGDN;
case SDLK_HOME: return KEY_HOME;
case SDLK_END: return KEY_END;
case SDLK_CAPSLOCK: return KEY_CAPSLK;
case SDLK_SCROLLLOCK: return KEY_NULL;
case SDLK_NUMLOCKCLEAR: return KEY_NULL;
case SDLK_PRINTSCREEN: return KEY_NULL;
case SDLK_PAUSE: return KEY_NULL;
case SDLK_F1: return KEY_F1;
case SDLK_F2: return KEY_F2;
case SDLK_F3: return KEY_F3;
case SDLK_F4: return KEY_F4;
case SDLK_F5: return KEY_F5;
case SDLK_F6: return KEY_F6;
case SDLK_F7: return KEY_F7;
case SDLK_F8: return KEY_F8;
case SDLK_F9: return KEY_F9;
case SDLK_F10: return KEY_F10;
case SDLK_F11: return KEY_F11;
case SDLK_F12: return KEY_F12;
case SDLK_F13: return KEY_NULL;
case SDLK_F14: return KEY_NULL;
case SDLK_F15: return KEY_NULL;
case SDLK_F16: return KEY_NULL;
case SDLK_F17: return KEY_NULL;
case SDLK_F18: return KEY_NULL;
case SDLK_F19: return KEY_NULL;
case SDLK_F20: return KEY_NULL;
case SDLK_F21: return KEY_NULL;
case SDLK_F22: return KEY_NULL;
case SDLK_F23: return KEY_NULL;
case SDLK_F24: return KEY_NULL;
case SDLK_KP_0: return KEY_NULL;
case SDLK_KP_1: return KEY_NULL;
case SDLK_KP_2: return KEY_NULL;
case SDLK_KP_3: return KEY_NULL;
case SDLK_KP_4: return KEY_NULL;
case SDLK_KP_5: return KEY_NULL;
case SDLK_KP_6: return KEY_NULL;
case SDLK_KP_7: return KEY_NULL;
case SDLK_KP_8: return KEY_NULL;
case SDLK_KP_9: return KEY_NULL;
case SDLK_KP_DECIMAL: return KEY_NULL;
case SDLK_KP_DIVIDE: return KEY_NULL;
case SDLK_KP_MULTIPLY: return KEY_NULL;
case SDLK_KP_MINUS: return KEY_NULL;
case SDLK_KP_PLUS: return KEY_NULL;
case SDLK_KP_ENTER: return KEY_NULL;
case SDLK_KP_EQUALS: return KEY_NULL;
case SDLK_LSHIFT: return KEY_LSHIFT;
case SDLK_LCTRL: return KEY_LCTRL;
case SDLK_LALT: return KEY_LALT;
case SDLK_LGUI: return KEY_NULL;
case SDLK_RSHIFT: return KEY_RSHIFT;
case SDLK_RCTRL: return KEY_RCTRL;
case SDLK_RALT: return KEY_RALT;
case SDLK_RGUI: return KEY_NULL;
case SDLK_MENU: return KEY_NULL;
}
return KEY_NULL;
}
#if 0
static void
keypress(SDL_Window *window, int key, int scancode, int action, int mods)
{
if(key >= 0 && key <= GLFW_KEY_LAST){
if(action == GLFW_RELEASE) KeyUp(keymap[key]);
else if(action == GLFW_PRESS) KeyDown(keymap[key]);
else if(action == GLFW_REPEAT) KeyDown(keymap[key]);
}
}
static void
charinput(GLFWwindow *window, unsigned int c)
{
EventHandler(CHARINPUT, (void*)(uintptr)c);
}
static void
resize(GLFWwindow *window, int w, int h)
{
rw::Rect r;
r.x = 0;
r.y = 0;
r.w = w;
r.h = h;
EventHandler(RESIZE, &r);
}
static void
mousebtn(GLFWwindow *window, int button, int action, int mods)
{
static int buttons = 0;
sk::MouseState ms;
switch(button){
case GLFW_MOUSE_BUTTON_LEFT:
if(action == GLFW_PRESS)
buttons |= 1;
else
buttons &= ~1;
break;
case GLFW_MOUSE_BUTTON_MIDDLE:
if(action == GLFW_PRESS)
buttons |= 2;
else
buttons &= ~2;
break;
case GLFW_MOUSE_BUTTON_RIGHT:
if(action == GLFW_PRESS)
buttons |= 4;
else
buttons &= ~4;
break;
}
sk::MouseState ms;
ms.buttons = buttons;
EventHandler(MOUSEBTN, &ms);
}
#endif
enum mousebutton {
BUTTON_LEFT = 0x1,
BUTTON_MIDDLE = 0x2,
BUTTON_RIGHT = 0x4,
};
int
main(int argc, char *argv[])
{
args.argc = argc;
args.argv = argv;
if(EventHandler(INITIALIZE, nil) == EVENTERROR)
return 0;
engineOpenParams.width = sk::globals.width;
engineOpenParams.height = sk::globals.height;
engineOpenParams.windowtitle = sk::globals.windowtitle;
engineOpenParams.window = &window;
if(EventHandler(RWINITIALIZE, nil) == EVENTERROR)
return 0;
Uint64 lastTicks = SDL_GetPerformanceCounter();
const float tickPeriod = 1.f / SDL_GetPerformanceFrequency();
SDL_Event event;
int mouseButtons = 0;
SDL_StartTextInput(window);
while(!sk::globals.quit){
while(SDL_PollEvent(&event)){
switch(event.type){
case SDL_EVENT_QUIT:
sk::globals.quit = true;
break;
case SDL_EVENT_WINDOW_RESIZED: {
rw::Rect r;
SDL_GetWindowPosition(window, &r.x, &r.y);
r.w = event.window.data1;
r.h = event.window.data2;
EventHandler(RESIZE, &r);
break;
}
case SDL_EVENT_KEY_UP: {
int c = keyCodeToSkKey(event.key.key);
EventHandler(KEYUP, &c);
break;
}
case SDL_EVENT_KEY_DOWN: {
int c = keyCodeToSkKey(event.key.key);
EventHandler(KEYDOWN, &c);
break;
}
case SDL_EVENT_TEXT_INPUT: {
const char *c = event.text.text;
while (int ci = *c) {
EventHandler(CHARINPUT, (void*)(uintptr)ci);
++c;
}
break;
}
case SDL_EVENT_MOUSE_MOTION: {
sk::MouseState ms;
ms.posx = event.motion.x;
ms.posy = event.motion.y;
EventHandler(MOUSEMOVE, &ms);
break;
}
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
switch (event.button.button) {
case SDL_BUTTON_LEFT: mouseButtons |= BUTTON_LEFT; break;
case SDL_BUTTON_MIDDLE: mouseButtons |= BUTTON_MIDDLE; break;
case SDL_BUTTON_RIGHT: mouseButtons |= BUTTON_RIGHT; break;
}
sk::MouseState ms;
ms.buttons = mouseButtons;
EventHandler(MOUSEBTN, &ms);
break;
}
case SDL_EVENT_MOUSE_BUTTON_UP: {
switch (event.button.button) {
case SDL_BUTTON_LEFT: mouseButtons &= ~BUTTON_LEFT; break;
case SDL_BUTTON_MIDDLE: mouseButtons &= ~BUTTON_MIDDLE; break;
case SDL_BUTTON_RIGHT: mouseButtons &= ~BUTTON_RIGHT; break;
}
sk::MouseState ms;
ms.buttons = mouseButtons;
EventHandler(MOUSEBTN, &ms);
break;
}
}
}
Uint64 currTicks = SDL_GetPerformanceCounter();
float timeDelta = (currTicks - lastTicks) * tickPeriod;
EventHandler(IDLE, &timeDelta);
lastTicks = currTicks;
}
SDL_StopTextInput(window);
EventHandler(RWTERMINATE, nil);
return 0;
}
namespace sk {
void
SetMousePosition(int x, int y)
{
SDL_WarpMouseInWindow(*engineOpenParams.window, x, y);
}
}
#endif
#endif
+8
View File
@@ -153,6 +153,7 @@ if(LIBRW_PLATFORM_GL3)
if (NOT TARGET glfw) if (NOT TARGET glfw)
find_package(glfw3 REQUIRED) find_package(glfw3 REQUIRED)
endif() endif()
target_compile_definitions(librw PUBLIC LIBRW_GLFW)
target_link_libraries(librw target_link_libraries(librw
PUBLIC PUBLIC
glfw glfw
@@ -169,6 +170,13 @@ if(LIBRW_PLATFORM_GL3)
PUBLIC PUBLIC
SDL2::SDL2 SDL2::SDL2
) )
elseif (LIBRW_GL3_GFXLIB STREQUAL "SDL3")
find_package(SDL3 CONFIG REQUIRED)
target_compile_definitions(librw PUBLIC LIBRW_SDL3)
target_link_libraries(librw
PUBLIC
SDL3::SDL3
)
endif() endif()
set(OpenGL_GL_PREFERENCE GLVND) set(OpenGL_GL_PREFERENCE GLVND)
+281 -5
View File
@@ -1246,8 +1246,12 @@ getFramebufferRect(Raster *frameBuffer)
if(fb->type == Raster::CAMERA){ if(fb->type == Raster::CAMERA){
#ifdef LIBRW_SDL2 #ifdef LIBRW_SDL2
SDL_GetWindowSize(glGlobals.window, &r.w, &r.h); SDL_GetWindowSize(glGlobals.window, &r.w, &r.h);
#else #elif defined(LIBRW_SDL3)
SDL_GetWindowSize(glGlobals.window, &r.w, &r.h);
#elif defined(LIBRW_GLFW)
glfwGetFramebufferSize(glGlobals.window, &r.w, &r.h); glfwGetFramebufferSize(glGlobals.window, &r.w, &r.h);
#else
missing implementation
#endif #endif
}else{ }else{
r.w = fb->width; r.w = fb->width;
@@ -1412,12 +1416,20 @@ showRaster(Raster *raster, uint32 flags)
else else
SDL_GL_SetSwapInterval(0); SDL_GL_SetSwapInterval(0);
SDL_GL_SwapWindow(glGlobals.window); SDL_GL_SwapWindow(glGlobals.window);
#else #elif defined(LIBRW_SDL3)
if(flags & Raster::FLIPWAITVSYNCH)
SDL_GL_SetSwapInterval(1);
else
SDL_GL_SetSwapInterval(0);
SDL_GL_SwapWindow(glGlobals.window);
#elif defined(LIBRW_GLFW)
if(flags & Raster::FLIPWAITVSYNCH) if(flags & Raster::FLIPWAITVSYNCH)
glfwSwapInterval(1); glfwSwapInterval(1);
else else
glfwSwapInterval(0); glfwSwapInterval(0);
glfwSwapBuffers(glGlobals.window); glfwSwapBuffers(glGlobals.window);
#else
not implemented
#endif #endif
} }
@@ -1603,7 +1615,174 @@ stopSDL2(void)
SDL_DestroyWindow(glGlobals.window); SDL_DestroyWindow(glGlobals.window);
return 1; return 1;
} }
#else
#elif defined(LIBRW_SDL3)
static void
addVideoMode(const SDL_DisplayMode *mode)
{
int i;
for(i = 1; i < glGlobals.numModes; i++){
if(glGlobals.modes[i].mode.w == mode->w &&
glGlobals.modes[i].mode.h == mode->h &&
glGlobals.modes[i].mode.format == mode->format){
// had this mode already, remember highest refresh rate
if(mode->refresh_rate > glGlobals.modes[i].mode.refresh_rate)
glGlobals.modes[i].mode.refresh_rate = mode->refresh_rate;
return;
}
}
// none found, add
glGlobals.modes[glGlobals.numModes].mode = *mode;
glGlobals.modes[glGlobals.numModes].flags = VIDEOMODEEXCLUSIVE;
glGlobals.numModes++;
}
static void
makeVideoModeList(SDL_DisplayID displayIndex, SDL_DisplayID *displays)
{
int i, num, depth;
const SDL_DisplayMode *currentMode;
SDL_DisplayMode **modes;
currentMode = SDL_GetCurrentDisplayMode(displays[displayIndex]);
modes = SDL_GetFullscreenDisplayModes(displayIndex, &num);
rwFree(glGlobals.modes);
glGlobals.modes = rwNewT(DisplayMode, num+(currentMode != NULL ? 1 : 0), ID_DRIVER | MEMDUR_EVENT);
if (currentMode) {
glGlobals.modes[0].mode = *currentMode;
glGlobals.modes[0].flags = 0;
glGlobals.numModes = 1;
}
for(i = 0; i < num; i++)
addVideoMode(modes[i]);
for(i = 0; i < glGlobals.numModes; i++){
depth = SDL_BITSPERPIXEL(glGlobals.modes[i].mode.format);
// set depth to power of two
for(glGlobals.modes[i].depth = 1; glGlobals.modes[i].depth < depth; glGlobals.modes[i].depth <<= 1);
}
SDL_free(modes);
}
static int
openSDL3(EngineOpenParams *openparams)
{
glGlobals.winWidth = openparams->width;
glGlobals.winHeight = openparams->height;
glGlobals.winTitle = openparams->windowtitle;
glGlobals.pWindow = openparams->window;
memset(&gl3Caps, 0, sizeof(gl3Caps));
/* Init SDL */
if (!SDL_InitSubSystem(SDL_INIT_VIDEO)){
RWERROR((ERR_GENERAL, SDL_GetError()));
return 0;
}
glGlobals.currentDisplay = 0;
SDL_DisplayID *displays = SDL_GetDisplays(&glGlobals.numDisplays);
if (glGlobals.currentDisplay >= glGlobals.numDisplays) {
RWERROR((ERR_GENERAL, SDL_GetError()));
return 0;
}
makeVideoModeList(glGlobals.currentDisplay, displays);
SDL_free(displays);
return 1;
}
static int
closeSDL3(void)
{
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return 1;
}
static struct {
int gl;
int major, minor;
} profiles[] = {
{ SDL_GL_CONTEXT_PROFILE_CORE, 3, 3 },
{ SDL_GL_CONTEXT_PROFILE_CORE, 2, 1 },
{ SDL_GL_CONTEXT_PROFILE_ES, 3, 1 },
{ SDL_GL_CONTEXT_PROFILE_ES, 2, 0 },
{ 0, 0, 0 },
};
static int
startSDL3(void)
{
SDL_Window *win;
SDL_GLContext ctx;
DisplayMode *mode;
mode = &glGlobals.modes[glGlobals.currentMode];
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, glGlobals.numSamples);
int i;
for(i = 0; profiles[i].gl; i++){
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profiles[i].gl);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, profiles[i].major);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, profiles[i].minor);
if(mode->flags & VIDEOMODEEXCLUSIVE) {
win = SDL_CreateWindow(glGlobals.winTitle, mode->mode.w, mode->mode.h, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN);
if (win)
SDL_SetWindowFullscreenMode(win, &mode->mode);
} else {
win = SDL_CreateWindow(glGlobals.winTitle, glGlobals.winWidth, glGlobals.winHeight, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
if (win)
SDL_SetWindowFullscreenMode(win, NULL);
}
if(win){
gl3Caps.gles = profiles[i].gl == SDL_GL_CONTEXT_PROFILE_ES;
gl3Caps.glversion = profiles[i].major*10 + profiles[i].minor;
break;
}
}
if(win == nil){
RWERROR((ERR_GENERAL, SDL_GetError()));
return 0;
}
ctx = SDL_GL_CreateContext(win);
if (!((gl3Caps.gles ? gladLoadGLES2Loader : gladLoadGLLoader) ((GLADloadproc) SDL_GL_GetProcAddress, gl3Caps.glversion)) ) {
RWERROR((ERR_GENERAL, "gladLoadGLLoader failed"));
SDL_GL_DestroyContext(ctx);
SDL_DestroyWindow(win);
return 0;
}
printf("OpenGL version: %s\n", glGetString(GL_VERSION));
glGlobals.window = win;
glGlobals.glcontext = ctx;
*glGlobals.pWindow = win;
glGlobals.presentWidth = 0;
glGlobals.presentHeight = 0;
glGlobals.presentOffX = 0;
glGlobals.presentOffY = 0;
return 1;
}
static int
stopSDL3(void)
{
SDL_GL_DestroyContext(glGlobals.glcontext);
SDL_DestroyWindow(glGlobals.window);
return 1;
}
#elif defined(LIBRW_GLFW)
static void static void
addVideoMode(const GLFWvidmode *mode) addVideoMode(const GLFWvidmode *mode)
@@ -1767,6 +1946,8 @@ stopGLFW(void)
glfwDestroyWindow(glGlobals.window); glfwDestroyWindow(glGlobals.window);
return 1; return 1;
} }
#else
not implemented
#endif #endif
static int static int
@@ -2007,7 +2188,94 @@ deviceSystemSDL2(DeviceReq req, void *arg, int32 n)
return 1; return 1;
} }
#else #elif defined(LIBRW_SDL3)
static int
deviceSystemSDL3(DeviceReq req, void *arg, int32 n)
{
VideoMode *rwmode;
switch(req){
case DEVICEOPEN:
return openSDL3((EngineOpenParams*)arg);
case DEVICECLOSE:
return closeSDL3();
case DEVICEINIT:
return startSDL3() && initOpenGL();
case DEVICETERM:
return termOpenGL() && stopSDL3();
case DEVICEFINALIZE:
return finalizeOpenGL();
case DEVICEGETNUMSUBSYSTEMS:
return glGlobals.numDisplays;
case DEVICEGETCURRENTSUBSYSTEM:
return glGlobals.currentDisplay;
case DEVICESETSUBSYSTEM:
if (n >= glGlobals.numDisplays)
return 0;
glGlobals.currentDisplay = n;
return 1;
case DEVICEGETSUBSSYSTEMINFO: {
const char *display_name = SDL_GetDisplayName(n);
if (display_name == nil)
return 0;
strncpy(((SubSystemInfo*)arg)->name, display_name, sizeof(SubSystemInfo::name));
return 1;
}
case DEVICEGETNUMVIDEOMODES:
return glGlobals.numModes;
case DEVICEGETCURRENTVIDEOMODE:
return glGlobals.currentMode;
case DEVICESETVIDEOMODE:
if(n >= glGlobals.numModes)
return 0;
glGlobals.currentMode = n;
return 1;
case DEVICEGETVIDEOMODEINFO:
if (n <= 0)
return 0;
rwmode = (VideoMode*)arg;
rwmode->width = glGlobals.modes[n].mode.w;
rwmode->height = glGlobals.modes[n].mode.h;
rwmode->depth = glGlobals.modes[n].depth;
rwmode->flags = glGlobals.modes[n].flags;
return 1;
case DEVICEGETMAXMULTISAMPLINGLEVELS:
{
GLint maxSamples;
glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
if(maxSamples == 0)
return 1;
return maxSamples;
}
case DEVICEGETMULTISAMPLINGLEVELS:
if(glGlobals.numSamples == 0)
return 1;
return glGlobals.numSamples;
case DEVICESETMULTISAMPLINGLEVELS:
glGlobals.numSamples = (uint32)n;
return 1;
default:
assert(0 && "not implemented");
return 0;
}
return 1;
}
#elif defined(LIBRW_GLFW)
static int static int
deviceSystemGLFW(DeviceReq req, void *arg, int32 n) deviceSystemGLFW(DeviceReq req, void *arg, int32 n)
@@ -2094,6 +2362,10 @@ deviceSystemGLFW(DeviceReq req, void *arg, int32 n)
return 1; return 1;
} }
#else
not implemented
#endif #endif
Device renderdevice = { Device renderdevice = {
@@ -2115,8 +2387,12 @@ Device renderdevice = {
gl3::im3DEnd, gl3::im3DEnd,
#ifdef LIBRW_SDL2 #ifdef LIBRW_SDL2
gl3::deviceSystemSDL2 gl3::deviceSystemSDL2
#else #elif defined(LIBRW_SDL3)
gl3::deviceSystemSDL3
#elif defined(LIBRW_GLFW)
gl3::deviceSystemGLFW gl3::deviceSystemGLFW
#else
not implemented
#endif #endif
}; };
+11 -2
View File
@@ -2,8 +2,12 @@
#include "glad/glad.h" #include "glad/glad.h"
#ifdef LIBRW_SDL2 #ifdef LIBRW_SDL2
#include <SDL.h> #include <SDL.h>
#else #elif defined(LIBRW_SDL3)
#include <SDL3/SDL.h>
#elif defined(LIBRW_GLFW)
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#else
not implemented
#endif #endif
#endif #endif
@@ -15,8 +19,13 @@ struct EngineOpenParams
#ifdef LIBRW_SDL2 #ifdef LIBRW_SDL2
SDL_Window **window; SDL_Window **window;
bool32 fullscreen; bool32 fullscreen;
#else #elif defined(LIBRW_SDL3)
SDL_Window **window;
bool32 fullscreen;
#elif defined(LIBRW_GLFW)
GLFWwindow **window; GLFWwindow **window;
#else
not implemented
#endif #endif
int width, height; int width, height;
const char *windowtitle; const char *windowtitle;
+15 -2
View File
@@ -26,8 +26,12 @@ struct DisplayMode
{ {
#ifdef LIBRW_SDL2 #ifdef LIBRW_SDL2
SDL_DisplayMode mode; SDL_DisplayMode mode;
#else #elif defined(LIBRW_SDL3)
SDL_DisplayMode mode;
#elif defined(LIBRW_GLFW)
GLFWvidmode mode; GLFWvidmode mode;
#else
not implemented
#endif #endif
int32 depth; int32 depth;
uint32 flags; uint32 flags;
@@ -42,13 +46,22 @@ struct GlGlobals
int numDisplays; int numDisplays;
int currentDisplay; int currentDisplay;
#else #elif defined(LIBRW_SDL3)
SDL_Window **pWindow;
SDL_Window *window;
SDL_GLContext glcontext;
int numDisplays;
int currentDisplay;
#elif defined(LIBRW_GLFW)
GLFWwindow **pWindow; GLFWwindow **pWindow;
GLFWwindow *window; GLFWwindow *window;
GLFWmonitor *monitor; GLFWmonitor *monitor;
int numMonitors; int numMonitors;
int currentMonitor; int currentMonitor;
#else
not implemented
#endif #endif
DisplayMode *modes; DisplayMode *modes;
+1 -1
View File
@@ -7,7 +7,7 @@ target_link_libraries(dumprwtree
librw::librw librw::librw
) )
if(LIBRW_GL3_GFXLIB STREQUAL "SDL2") if(LIBRW_GL3_GFXLIB MATCHES "SDL[23]")
target_compile_definitions(dumprwtree PRIVATE SDL_MAIN_HANDLED) target_compile_definitions(dumprwtree PRIVATE SDL_MAIN_HANDLED)
endif() endif()
+1 -1
View File
@@ -7,7 +7,7 @@ target_link_libraries(ska2anm
librw::librw librw::librw
) )
if(LIBRW_GL3_GFXLIB STREQUAL "SDL2") if(LIBRW_GL3_GFXLIB MATCHES "SDL[23]")
target_compile_definitions(ska2anm PRIVATE SDL_MAIN_HANDLED) target_compile_definitions(ska2anm PRIVATE SDL_MAIN_HANDLED)
endif() endif()