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
commit 1499b9660c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 12190 additions and 6146 deletions

View File

@ -16,13 +16,15 @@ jobs:
case:
- { name: 'Windows (DirectX 9)', os: 'windows-latest', platform: 'D3D9' }
- { 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: '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: '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: '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' }
@ -36,11 +38,11 @@ jobs:
if: ${{ matrix.case.ps2 }}
run: |
apk add cmake gmp mpc1 mpfr4 make pkgconf git
- name: 'Setup SDL2'
if: ${{ matrix.case.gl3_gfxlib == 'SDL2' }}
- name: 'Setup SDL'
if: ${{ !!matrix.case.sdl-version }}
uses: libsdl-org/setup-sdl@main
with:
version: 2-latest
version: ${{ matrix.case.sdl-version }}
install-linux-dependencies: true
cmake-toolchain-file: ${{ matrix.case.cmake-toolchain-file }}
- name: 'Setup GLFW'

View File

@ -39,7 +39,7 @@ if(NOT LIBRW_PLATFORM IN_LIST LIBRW_PLATFORMS)
endif()
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_property(CACHE LIBRW_GL3_GFXLIB PROPERTY STRINGS ${LIBRW_GL3_GFXLIBS})
if(LIBRW_PLATFORM_GL3)
@ -91,6 +91,7 @@ if(LIBRW_INSTALL)
EXPORT librw-targets NAMESPACE librw::
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
)
export(TARGETS librw NAMESPACE librw:: FILE librw-targets.cmake)
if(LIBRW_GL3_GFXLIB STREQUAL "SDL2")
install(
@ -126,8 +127,10 @@ if(LIBRW_INSTALL)
elseif(LIBRW_PLATFORM_GL3)
if(LIBRW_GL3_GFXLIB STREQUAL "GLFW")
set(platform "-gl3-glfw")
else()
elseif(LIBRW_GL3_GFXLIB STREQUAL "SDL2")
set(platform "-gl3-sdl2")
elseif(LIBRW_GL3_GFXLIB STREQUAL "SDL3")
set(platform "-gl3-sdl3")
endif()
elseif(LIBRW_PLATFORM_D3D9)
set(platform "-d3d9")

View File

@ -14,16 +14,21 @@ if(LIBRW_PLATFORM_GL3)
message(FATAL_ERROR "find_package(OpenGL) failed: no target was created")
endif()
include(CMakeFindDependencyMacro)
if(LIBRW_GL3_GFXLIB STREQUAL "GLFW")
if (NOT TARGET glfw)
find_package(glfw3 REQUIRED)
find_dependency(glfw3 REQUIRED)
endif()
elseif(LIBRW_GL3_GFXLIB STREQUAL "SDL2")
if (NOT TARGET SDL2::SDL2)
find_package(SDL2 CONFIG)
find_dependency(SDL2 CONFIG)
if (NOT TARGET SDL2::SDL2)
find_package(SDL2 MODULE REQUIRED)
find_dependency(SDL2 MODULE REQUIRED)
endif()
endif()
elseif(LIBRW_GL3_GFXLIB STREQUAL "SDL3")
if (NOT TARGET SDL3::SDL3)
find_dependency(SDL3 CONFIG REQUIRED)
endif()
endif()
endif()

View File

@ -6,6 +6,7 @@ newoption {
allowed = {
{ "glfw", "GLFW" },
{ "sdl2", "SDL2" },
{ "sdl3", "SDL3" },
},
}
@ -27,7 +28,14 @@ newoption {
trigger = "sdl2dir",
value = "PATH",
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"
@ -64,6 +72,10 @@ workspace "librw"
defines { "RW_GL3" }
if _OPTIONS["gfxlib"] == "sdl2" then
defines { "LIBRW_SDL2" }
elseif _OPTIONS["gfxlib"] == "sdl3" then
defines { "LIBRW_SDL3" }
elseif _OPTIONS["gfxlib"] == "glfw" then
defines { "LIBRW_GLFW" }
end
filter { "platforms:*d3d9" }
defines { "RW_D3D9" }
@ -131,21 +143,27 @@ function findlibs()
links { "GL" }
if _OPTIONS["gfxlib"] == "glfw" then
links { "glfw" }
else
elseif _OPTIONS["gfxlib"] == "sdl2" then
links { "SDL2" }
elseif _OPTIONS["gfxlib"] == "sdl3" then
links { "SDL3" }
end
filter { "platforms:win-amd64-gl3" }
libdirs { path.join(_OPTIONS["glfwdir64"], "lib-vc2015") }
libdirs { path.join(_OPTIONS["sdl2dir"], "lib/x64") }
libdirs { path.join(_OPTIONS["sdl3dir"], "lib/x64") }
filter { "platforms:win-x86-gl3" }
libdirs { path.join(_OPTIONS["glfwdir32"], "lib-vc2015") }
libdirs { path.join(_OPTIONS["sdl2dir"], "lib/x86") }
libdirs { path.join(_OPTIONS["sdl3dir"], "lib/x86") }
filter { "platforms:win*gl3" }
links { "opengl32" }
if _OPTIONS["gfxlib"] == "glfw" then
links { "glfw3" }
else
elseif _OPTIONS["gfxlib"] == "sdl2" then
links { "SDL2" }
elseif _OPTIONS["gfxlib"] == "sdl3" then
links { "SDL3" }
end
filter { "platforms:*d3d9" }
links { "gdi32", "d3d9" }

View File

@ -1,6 +1,7 @@
add_library(librw_skeleton
glfw.cpp
sdl2.cpp
sdl3.cpp
skeleton.cpp
skeleton.h
win.cpp
@ -38,7 +39,7 @@ target_link_libraries(librw_skeleton
librw
)
if (LIBRW_GL3_GFXLIB STREQUAL "SDL2")
if (LIBRW_GL3_GFXLIB MATCHES "SDL[23]")
target_compile_definitions(librw_skeleton PRIVATE SDL_MAIN_HANDLED)
endif()

View File

@ -1,4 +1,4 @@
#ifndef LIBRW_SDL2
#ifdef LIBRW_GLFW
#include <rw.h>
#include "skeleton.h"

View File

@ -1,5 +1,5 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
// v1.91.3 WIP
//
// The MIT License(MIT)
//
@ -666,7 +666,7 @@ namespace IMGUIZMO_NAMESPACE
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;
bool mbUsing;
bool mbUsingViewManipulate;
bool mbEnable;
bool mbMouseOver;
bool mReversed; // reversed projection matrix
bool mIsViewManipulatorHovered;
// translation
vec_t mTranslationPlan;
@ -726,6 +728,7 @@ namespace IMGUIZMO_NAMESPACE
// save axis factor when using gizmo
bool mBelowAxisLimit[3];
int mAxisMask = 0;
bool mBelowPlaneLimit[3];
float mAxisFactor[3];
@ -754,13 +757,25 @@ namespace IMGUIZMO_NAMESPACE
float mDisplayRatio = 1.f;
bool mIsOrthographic = false;
// check to not have multiple gizmo highlighted at the same time
bool mbOverGizmoHotspot = false;
int mActualID = -1;
int mEditingID = -1;
ImGuiWindow* mAlternativeWindow = nullptr;
ImVector<ImGuiID> mIDStack;
ImGuiID mEditingID = -1;
OPERATION mOperation = OPERATION(-1);
bool mAllowAxisFlip = true;
float mGizmoSizeClipSpace = 0.1f;
inline ImGuiID GetCurrentID()
{
if (mIDStack.empty())
{
mIDStack.push_back(-1);
}
return mIDStack.back();
}
};
static Context gContext;
@ -929,6 +944,8 @@ namespace IMGUIZMO_NAMESPACE
ImGuiWindow* window = ImGui::FindWindowByName(gContext.mDrawList->_OwnerName);
if (g.HoveredWindow == window) // Mouse hovering drawlist window
return true;
if (gContext.mAlternativeWindow != nullptr && g.HoveredWindow == gContext.mAlternativeWindow)
return true;
if (g.HoveredWindow != NULL) // Any other window is hovered
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)
@ -981,6 +998,7 @@ namespace IMGUIZMO_NAMESPACE
ImGui::Begin("gizmo", NULL, flags);
gContext.mDrawList = ImGui::GetWindowDrawList();
gContext.mbOverGizmoHotspot = false;
ImGui::End();
ImGui::PopStyleVar();
ImGui::PopStyleColor(2);
@ -988,7 +1006,17 @@ namespace IMGUIZMO_NAMESPACE
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()
@ -1078,7 +1106,6 @@ namespace IMGUIZMO_NAMESPACE
// compute scale from the size of camera right vector projected on screen at the matrix position
vec_t pointRight = viewInverse.v.right;
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;
rightViewInverse.TransformVector(gContext.mModelInverse);
@ -1146,11 +1173,13 @@ namespace IMGUIZMO_NAMESPACE
dirPlaneX = directionUnary[(axisIndex + 1) % 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
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];
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 paraSurf = GetParallelogram(makeVect(0.f, 0.f, 0.f), dirPlaneX * gContext.mScreenFactor, dirPlaneY * gContext.mScreenFactor);
belowPlaneLimit = (paraSurf > gContext.mAxisLimit);
belowAxisLimit = (axisLengthInClipSpace > gContext.mPlaneLimit);
// Apply axis mask to axes and planes
belowPlaneLimit = (paraSurf > gContext.mAxisLimit) && (((1<<axisIndex) == gContext.mAxisMask) || !gContext.mAxisMask);
belowAxisLimit = (axisLengthInClipSpace > gContext.mPlaneLimit) && !((1<<axisIndex)&gContext.mAxisMask);
// and store values
gContext.mAxisFactor[axisIndex] = mulAxis;
@ -1241,6 +1271,9 @@ namespace IMGUIZMO_NAMESPACE
}
ImDrawList* drawList = gContext.mDrawList;
bool isMultipleAxesMasked = (gContext.mAxisMask & (gContext.mAxisMask - 1)) != 0;
bool isNoAxesMasked = !gContext.mAxisMask;
// colors
ImU32 colors[7];
ComputeColors(colors, type, ROTATE);
@ -1268,8 +1301,15 @@ namespace IMGUIZMO_NAMESPACE
{
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 int circleMul = (hasRSC && !usingAxis ) ? 1 : 2;
const int circleMul = (hasRSC && !usingAxis) ? 1 : 2;
ImVec2* circlePos = (ImVec2*)alloca(sizeof(ImVec2) * (circleMul * halfCircleSegmentCount + 1));
@ -1293,12 +1333,12 @@ namespace IMGUIZMO_NAMESPACE
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);
}
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];
@ -1355,7 +1395,7 @@ namespace IMGUIZMO_NAMESPACE
// draw
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;
}
@ -1382,7 +1422,7 @@ namespace IMGUIZMO_NAMESPACE
ImVec2 worldDirSSpaceNoScale = worldToPos(dirAxis * markerScale * 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);
drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, scaleLineColor, gContext.mStyle.ScaleLineThickness);
@ -1406,7 +1446,7 @@ namespace IMGUIZMO_NAMESPACE
// draw screen cirle
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 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
@ -1443,7 +1483,7 @@ namespace IMGUIZMO_NAMESPACE
// draw
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;
}
@ -1471,7 +1511,7 @@ namespace IMGUIZMO_NAMESPACE
ImVec2 worldDirSSpace = worldToPos((dirAxis * markerScale * scaleDisplay[i]) * gContext.mScreenFactor, gContext.mMVPLocal);
#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->AddCircleFilled(worldDirSSpaceNoScale, 6.f, IM_COL32(0x40, 0x40, 0x40, 0xFF));
@ -1491,7 +1531,7 @@ namespace IMGUIZMO_NAMESPACE
// draw screen cirle
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 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);
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);
@ -1779,7 +1819,7 @@ namespace IMGUIZMO_NAMESPACE
gContext.mBoundsLocalPivot[thirdAxis] = aabb[oppositeIndex][thirdAxis];
gContext.mbUsingBounds = true;
gContext.mEditingID = gContext.mActualID;
gContext.mEditingID = gContext.GetCurrentID();
gContext.mBoundsMatrix = gContext.mModelSource;
}
// 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.mbUsingBounds = true;
gContext.mEditingID = gContext.mActualID;
gContext.mEditingID = gContext.GetCurrentID();
gContext.mBoundsMatrix = gContext.mModelSource;
}
}
if (gContext.mbUsingBounds && (gContext.mActualID == -1 || gContext.mActualID == gContext.mEditingID))
if (gContext.mbUsingBounds && (gContext.GetCurrentID() == gContext.mEditingID))
{
matrix_t scale;
scale.SetToIdentity();
@ -1904,6 +1944,8 @@ namespace IMGUIZMO_NAMESPACE
{
continue;
}
bool isAxisMasked = ((1 << i) & gContext.mAxisMask) != 0;
vec_t dirPlaneX, dirPlaneY, dirAxis;
bool belowAxisLimit, belowPlaneLimit;
ComputeTripodAxisAndVisibility(i, dirAxis, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit, true);
@ -1924,7 +1966,8 @@ namespace IMGUIZMO_NAMESPACE
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;
}
bool isNoAxesMasked = !gContext.mAxisMask;
bool isMultipleAxesMasked = (gContext.mAxisMask & (gContext.mAxisMask - 1)) != 0;
ImGuiIO& io = ImGui::GetIO();
int type = MT_NONE;
@ -1980,6 +2027,8 @@ namespace IMGUIZMO_NAMESPACE
float dist = deltaScreen.Length();
if (Intersects(op, ROTATE_SCREEN) && dist >= (gContext.mRadiusSquareCenter - 4.0f) && dist < (gContext.mRadiusSquareCenter + 4.0f))
{
if (!isNoAxesMasked)
return MT_NONE;
type = MT_ROTATE_SCREEN;
}
@ -1994,6 +2043,7 @@ namespace IMGUIZMO_NAMESPACE
{
continue;
}
bool isAxisMasked = ((1 << i) & gContext.mAxisMask) != 0;
// pickup plan
vec_t pickupPlan = BuildPlan(gContext.mModel.v.position, planNormals[i]);
@ -2018,6 +2068,8 @@ namespace IMGUIZMO_NAMESPACE
const float distance = makeVect(distanceOnScreen).Length();
if (distance < 8.f) // pixel size
{
if ((!isAxisMasked || isMultipleAxesMasked) && !isNoAxesMasked)
break;
type = MT_ROTATE_X + i;
}
}
@ -2031,6 +2083,10 @@ namespace IMGUIZMO_NAMESPACE
{
return MT_NONE;
}
bool isNoAxesMasked = !gContext.mAxisMask;
bool isMultipleAxesMasked = (gContext.mAxisMask & (gContext.mAxisMask - 1)) != 0;
ImGuiIO& io = ImGui::GetIO();
int type = MT_NONE;
@ -2047,6 +2103,7 @@ namespace IMGUIZMO_NAMESPACE
// compute
for (int i = 0; i < 3 && type == MT_NONE; i++)
{
bool isAxisMasked = ((1 << i) & gContext.mAxisMask) != 0;
vec_t dirPlaneX, dirPlaneY, dirAxis;
bool 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));
if ((closestPointOnAxis - screenCoord).Length() < 12.f && Intersects(op, static_cast<OPERATION>(TRANSLATE_X << i))) // pixel size
{
if (isAxisMasked)
break;
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));
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;
}
@ -2092,7 +2153,7 @@ namespace IMGUIZMO_NAMESPACE
bool modified = false;
// 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
ImGui::SetNextFrameWantCaptureMouse(true);
@ -2166,7 +2227,8 @@ namespace IMGUIZMO_NAMESPACE
{
// find new possible way to move
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 IMGUI_VERSION_NUM >= 18723
@ -2178,7 +2240,7 @@ namespace IMGUIZMO_NAMESPACE
if (CanActivate() && type != MT_NONE)
{
gContext.mbUsing = true;
gContext.mEditingID = gContext.mActualID;
gContext.mEditingID = gContext.GetCurrentID();
gContext.mCurrentOperation = type;
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,
@ -2215,7 +2277,9 @@ namespace IMGUIZMO_NAMESPACE
if (!gContext.mbUsing)
{
// 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 IMGUI_VERSION_NUM >= 18723
@ -2227,23 +2291,23 @@ namespace IMGUIZMO_NAMESPACE
if (CanActivate() && type != MT_NONE)
{
gContext.mbUsing = true;
gContext.mEditingID = gContext.mActualID;
gContext.mEditingID = gContext.GetCurrentID();
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
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);
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.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.mSaveMousePosx = io.MousePos.x;
}
}
// 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
ImGui::SetNextFrameWantCaptureMouse(true);
@ -2336,7 +2400,8 @@ namespace IMGUIZMO_NAMESPACE
if (!gContext.mbUsing)
{
type = GetRotateType(op);
type = gContext.mbOverGizmoHotspot ? MT_NONE : GetRotateType(op);
gContext.mbOverGizmoHotspot |= type != MT_NONE;
if (type != MT_NONE)
{
@ -2355,7 +2420,7 @@ namespace IMGUIZMO_NAMESPACE
if (CanActivate() && type != MT_NONE)
{
gContext.mbUsing = true;
gContext.mEditingID = gContext.mActualID;
gContext.mEditingID = gContext.GetCurrentID();
gContext.mCurrentOperation = type;
const vec_t rotatePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, -gContext.mCameraDir };
// pickup plan
@ -2376,7 +2441,7 @@ namespace IMGUIZMO_NAMESPACE
}
// 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
ImGui::SetNextFrameWantCaptureMouse(true);
@ -2482,9 +2547,74 @@ namespace IMGUIZMO_NAMESPACE
mat.v.position.Set(translation[0], translation[1], translation[2], 1.f);
}
void SetAlternativeWindow(ImGuiWindow* window)
{
gContext.mAlternativeWindow = window;
}
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)
@ -2497,13 +2627,28 @@ namespace IMGUIZMO_NAMESPACE
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)
{
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)
{
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
ComputeContext(view, projection, matrix, (operation & SCALE) ? LOCAL : mode);
@ -2547,6 +2692,8 @@ namespace IMGUIZMO_NAMESPACE
DrawScaleGizmo(operation, type);
DrawScaleUniveralGizmo(operation, type);
}
gContext.mDrawList->PopClipRect ();
return manipulated;
}
@ -2775,7 +2922,6 @@ namespace IMGUIZMO_NAMESPACE
{
static bool isDraging = false;
static bool isClicking = false;
static bool isInside = false;
static vec_t interpolationUp;
static vec_t interpolationDir;
static int interpolationFrames = 0;
@ -2888,10 +3034,11 @@ namespace IMGUIZMO_NAMESPACE
if (iPass)
{
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])
{
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) {
overBox = boxCoordInt;
@ -2917,7 +3064,7 @@ namespace IMGUIZMO_NAMESPACE
vec_t newEye = camTarget + newDir * length;
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)
{
@ -2990,6 +3137,15 @@ namespace IMGUIZMO_NAMESPACE
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
ComputeContext(svgView.m16, svgProjection.m16, gContext.mModelSource.m16, gContext.mMode);
}

View File

@ -1,5 +1,5 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
// v1.91.3 WIP
//
// The MIT License(MIT)
//
@ -39,9 +39,9 @@
// - display rotation/translation/scale infos in local/world space and not only local
// - finish local/world matrix application
// - OPERATION as bitmask
//
//
// -------------------------------------------------------------------------------------------
// Example
// Example
#if 0
void EditTransform(const Camera& camera, matrix_t& matrix)
{
@ -115,6 +115,8 @@ void EditTransform(const Camera& camera, matrix_t& matrix)
#define IMGUIZMO_NAMESPACE ImGuizmo
#endif
struct ImGuiWindow;
namespace IMGUIZMO_NAMESPACE
{
// 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
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
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);
// 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
// translation is applied in world space
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
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);
// 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
IMGUI_API bool IsOver(OPERATION op);
IMGUI_API void SetGizmoSizeClipSpace(float value);
@ -229,8 +259,12 @@ namespace IMGUIZMO_NAMESPACE
// Configure the limit where axis are hidden
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
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
{

View File

@ -1,6 +1,6 @@
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
of this software and associated documentation files (the "Software"), to deal

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.
//#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.
// 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_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_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_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_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_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
//---- Enable Test Engine / Automation features.
@ -59,9 +59,12 @@
//#define IMGUI_INCLUDE_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
//---- 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...)
//#define IMGUI_USE_WCHAR32
@ -83,10 +86,12 @@
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
//#define IMGUI_ENABLE_FREETYPE
//---- Use FreeType+lunasvg library 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).
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
// 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
//---- 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 __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
//#define IMGUI_DEBUG_PARANOID

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -80,7 +80,7 @@ ImGui_ImplRW_RenderDrawLists(ImDrawData* draw_data)
if(pcmd->UserCallback)
pcmd->UserCallback(cmd_list, pcmd);
else{
rw::Texture *tex = (rw::Texture*)pcmd->TextureId;
rw::Texture *tex = (rw::Texture*)pcmd->GetTexID();
if(tex && tex->raster){
rw::SetRenderStatePtr(rw::TEXTURERASTER, tex->raster);
rw::SetRenderState(rw::TEXTUREADDRESSU, tex->getAddressU());
@ -116,26 +116,6 @@ ImGui_ImplRW_Init(void)
ImGui::CreateContext();
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;
}
@ -189,9 +169,9 @@ ImGui_ImplRW_NewFrame(float timeDelta)
io.DisplaySize = ImVec2(sk::globals.width, sk::globals.height);
io.DeltaTime = timeDelta;
io.KeyCtrl = io.KeysDown[sk::KEY_LCTRL] || io.KeysDown[sk::KEY_RCTRL];
io.KeyShift = io.KeysDown[sk::KEY_LSHIFT] || io.KeysDown[sk::KEY_RSHIFT];
io.KeyAlt = io.KeysDown[sk::KEY_LALT] || io.KeysDown[sk::KEY_RALT];
io.KeyCtrl = ImGui::IsKeyPressed(ImGuiKey_LeftCtrl) || ImGui::IsKeyPressed(ImGuiKey_RightCtrl);
io.KeyShift = ImGui::IsKeyPressed(ImGuiKey_LeftShift) || ImGui::IsKeyPressed(ImGuiKey_RightShift);
io.KeyAlt = ImGui::IsKeyPressed(ImGuiKey_LeftAlt) || ImGui::IsKeyPressed(ImGuiKey_RightAlt);
io.KeySuper = false;
if(io.WantSetMousePos)
@ -200,6 +180,100 @@ ImGui_ImplRW_NewFrame(float timeDelta)
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
ImGuiEventHandler(sk::Event e, void *param)
{
@ -211,14 +285,10 @@ ImGuiEventHandler(sk::Event e, void *param)
switch(e){
case KEYDOWN:
c = *(int*)param;
if(c < 256)
io.KeysDown[c] = 1;
io.AddKeyEvent(SkKeyToImGuiKey(*(int*)param), true);
return EVENTPROCESSED;
case KEYUP:
c = *(int*)param;
if(c < 256)
io.KeysDown[c] = 0;
io.AddKeyEvent(SkKeyToImGuiKey(*(int*)param), false);
return EVENTPROCESSED;
case CHARINPUT:
c = (uint)(uintptr)param;

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// dear imgui, v1.91.0
// dear imgui, v1.92.2b
// (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 "-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 "-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 "-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
@ -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 "-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 "-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__)
#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 "-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
#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[].
const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0;
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 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)
{
ItemSize(outer_rect);
ItemAdd(outer_rect, id);
g.NextWindowData.ClearFlags();
return false;
}
@ -369,6 +377,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->ColumnsCount = columns_count;
table->IsLayoutLocked = false;
table->InnerWidth = inner_width;
table->NavLayer = (ImS8)outer_window->DC.NavLayerCurrent;
temp_data->UserOuterSize = outer_size;
// 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
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)
ImGuiWindowFlags child_window_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;
BeginChildEx(name, instance_id, outer_rect.GetSize(), ImGuiChildFlags_None, child_window_flags);
ImGuiChildFlags child_child_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : ImGuiChildFlags_None;
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->WorkRect = table->InnerWindow->WorkRect;
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().
table->WorkRect = table->OuterRect = table->InnerRect = outer_rect;
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
@ -460,16 +474,27 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size;
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
// 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).
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)
{
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)
{
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
@ -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.ClipWith(table->WorkRect); // We need this to honor inner_width
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->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
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;
if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly.
inner_window->DC.CurrentTableIdx = table_idx;
@ -555,6 +580,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// Initialize
table->SettingsOffset = -1;
table->IsSortSpecsDirty = true;
table->IsSettingsDirty = true; // Records itself into .ini file even when in default state (#7934)
table->InstanceInteracted = -1;
table->ContextPopupColumn = -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)
// 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);
// 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
const ImRect work_rect = table->WorkRect;
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_for_stretched_columns = width_avail - width_spacings - sum_width_requests;
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];
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)
{
@ -1059,16 +1086,12 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
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
column->MinX = offset_x;
// Lock width based on start position and minimum/maximum width for this position
float max_width = TableGetMaxColumnWidth(table, column_n);
column->WidthGiven = ImMin(column->WidthGiven, max_width);
column->WidthMax = TableCalcMaxColumnWidth(table, column_n);
column->WidthGiven = ImMin(column->WidthGiven, column->WidthMax);
column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth));
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;
if (column->SortOrder != -1)
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;
table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
}
// Alignment
// 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
if (table->HostSkipItems == false)
if (table->HostSkipItems == false && table->InstanceCurrent == 0)
{
column->AutoFitQueue >>= 1;
column->CannotSkipItemsQueue >>= 1;
@ -1223,7 +1251,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// [Part 11] Default context menu
// - 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,
// e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options.
if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table))
@ -1249,7 +1277,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
if (table->Flags & ImGuiTableFlags_NoClip)
table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);
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()
@ -1319,7 +1347,11 @@ void ImGui::EndTable()
{
ImGuiContext& g = *GImGui;
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
// 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
// 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)
{
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)
{
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();
g.CurrentTable = table;
inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask;
}
else
{
table->InnerWindow->DC.TreeDepth--;
ItemSize(table->OuterRect.GetSize());
ItemAdd(table->OuterRect, 0);
}
@ -1534,14 +1569,43 @@ void ImGui::EndTable()
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
// 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)
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!");
IM_ASSERT(table->IsLayoutLocked == false && "Need to call call TableSetupColumn() before first row!");
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((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()");
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.");
// 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 ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame)
flags |= ImGuiTableColumnFlags_WidthFixed;
@ -1576,27 +1640,10 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
column->InitStretchWeightOrWidth = init_width_or_weight;
if (table->IsInitializing)
{
// Init width or weight
ImGuiTableFlags init_flags = ~table->SettingsLoadedFlags;
if (column->WidthRequest < 0.0f && column->StretchWeight < 0.0f)
{
if ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f)
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);
}
init_flags |= ImGuiTableFlags_Resizable;
TableInitColumnDefaults(table, column, init_flags);
}
// 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)
{
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;
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(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS);
IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit
@ -1691,9 +1742,11 @@ void ImGui::TableSetColumnEnabled(int column_n, bool enabled)
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL);
if (!table)
if (table == NULL)
{
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
return;
}
IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above
if (column_n < 0)
column_n = table->CurrentColumn;
@ -1772,6 +1825,11 @@ void ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
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)
color = 0;
@ -1900,7 +1958,10 @@ void ImGui::TableEndRow(ImGuiTable* table)
IM_ASSERT(table->IsInsideRow);
if (table->CurrentColumn != -1)
{
TableEndCell(table);
table->CurrentColumn = -1;
}
// Logging
if (g.LogEnabled)
@ -1998,8 +2059,8 @@ void ImGui::TableEndRow(ImGuiTable* table)
if (unfreeze_rows_request)
{
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main;
const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);
table->Columns[column_n].NavLayerCurrent = table->NavLayer;
const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y);
table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;
if (unfreeze_rows_actual)
@ -2008,8 +2069,8 @@ void ImGui::TableEndRow(ImGuiTable* table)
table->IsUnfrozenRows = true;
// 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.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = 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 = table->InnerClipRect.Max.y;
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
@ -2065,7 +2126,11 @@ bool ImGui::TableSetColumnIndex(int column_n)
{
if (table->CurrentColumn != -1)
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);
}
@ -2136,6 +2201,7 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n)
g.LastItemData.StatusFlags = 0;
}
// Also see TablePushColumnChannel()
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.
@ -2195,8 +2261,8 @@ void ImGui::TableEndCell(ImGuiTable* table)
// 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.
float ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n)
// Maximum column content width given current layout. Use column->MinX so this value differs on a per-column basis.
float ImGui::TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n)
{
const ImGuiTableColumn* column = &table->Columns[column_n];
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)
IM_ASSERT(table->MinColumnWidth > 0.0f);
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);
if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width)
return;
@ -2409,10 +2475,38 @@ void ImGui::TablePopBackgroundChannel()
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiTable* table = g.CurrentTable;
ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
// Optimization: avoid PopClipRect() + SetCurrentChannel()
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);
}
@ -2743,7 +2837,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
const ImU32 outer_col = table->BorderColorStrong;
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)
{
@ -2787,9 +2881,7 @@ ImGuiTableSortSpecs* ImGui::TableGetSortSpecs()
{
ImGuiContext& g = *GImGui;
ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL);
if (!(table->Flags & ImGuiTableFlags_Sortable))
if (table == NULL || !(table->Flags & ImGuiTableFlags_Sortable))
return NULL;
// 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
// 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.
// 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.
void ImGui::TableHeadersRow()
{
ImGuiContext& g = *GImGui;
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)
TableUpdateLayout(table);
@ -3032,8 +3130,7 @@ void ImGui::TableHeadersRow()
if (!TableSetColumnIndex(column_n))
continue;
// Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them)
// In your own code you may omit the PushID/PopID all-together, provided you know they won't collide.
// 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)
const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n);
PushID(column_n);
TableHeader(name);
@ -3058,7 +3155,12 @@ void ImGui::TableHeader(const char* label)
return;
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);
const int column_n = table->CurrentColumn;
ImGuiTableColumn* column = &table->Columns[column_n];
@ -3124,7 +3226,7 @@ void ImGui::TableHeader(const char* label)
if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0)
TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn);
}
RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact | ImGuiNavHighlightFlags_NoRounding);
RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding);
if (held)
table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n;
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
// be merged into a single draw call.
//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);
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;
ImGuiWindow* window = g.CurrentWindow;
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");
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);
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 ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
const ImVec2 align = g.Style.TableAngledHeadersTextAlign;
// 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 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));
int vtx_idx_begin = draw_list->_VtxCurrentIdx;
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();
int vtx_idx_end = draw_list->_VtxCurrentIdx;
@ -3664,6 +3770,14 @@ void ImGui::TableLoadSettings(ImGuiTable* table)
table->SettingsLoadedFlags = settings->SaveFlags;
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
ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();
ImU64 display_order_mask = 0;
@ -3680,14 +3794,12 @@ void ImGui::TableLoadSettings(ImGuiTable* table)
column->StretchWeight = column_settings->WidthOrWeight;
else
column->WidthRequest = column_settings->WidthOrWeight;
column->AutoFitQueue = 0x00;
}
if (settings->SaveFlags & ImGuiTableFlags_Reorderable)
column->DisplayOrder = column_settings->DisplayOrder;
else
column->DisplayOrder = (ImGuiTableColumnIdx)column_n;
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->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_order = (settings->SaveFlags & ImGuiTableFlags_Reorderable) != 0;
const bool save_sort = (settings->SaveFlags & ImGuiTableFlags_Sortable) != 0;
if (!save_size && !save_visible && !save_order && !save_sort)
continue;
// We need to save the [Table] entry even if all the bools are false, since this records a table with "default settings".
buf->reserve(buf->size() + 30 + settings->ColumnsCount * 50); // ballpark reserve
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);
if (hovered || held)
g.MouseCursor = ImGuiMouseCursor_ResizeEW;
SetMouseCursor(ImGuiMouseCursor_ResizeEW);
if (held && !(column->Flags & ImGuiOldColumnFlags_NoResize))
dragging_column = n;
}
@ -4428,12 +4539,12 @@ void ImGui::EndColumns()
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();
IM_ASSERT(columns_count >= 1);
ImGuiOldColumnFlags flags = (border ? 0 : ImGuiOldColumnFlags_NoBorder);
ImGuiOldColumnFlags flags = (borders ? 0 : ImGuiOldColumnFlags_NoBorder);
//flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior
ImGuiOldColumns* columns = window->DC.CurrentColumns;
if (columns != NULL && columns->Count == columns_count && columns->Flags == flags)

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,8 @@
// 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_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.
// - 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)
// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character
// (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_NEWLINE the character returned by _GETCHAR() we recognize
// 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_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
// 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,
@ -209,6 +219,7 @@
// 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)
// 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
// state.
@ -243,7 +254,14 @@
// 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
// 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
@ -318,7 +336,7 @@ typedef struct
int undo_char_point, redo_char_point;
} StbUndoState;
typedef struct
typedef struct STB_TexteditState
{
/////////////////////
//
@ -392,6 +410,16 @@ typedef struct
#define IMSTB_TEXTEDIT_memmove memmove
#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) {
// search characters in row for one that straddles 'x'
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);
if (x < prev_x+w) {
if (x < prev_x+w/2)
return k+i;
else
return k+i+1;
return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k);
}
prev_x += w;
}
@ -563,7 +591,7 @@ static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING
// now scan to find xpos
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);
}
@ -649,9 +677,9 @@ static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
{
--c; // always move at least one character
while( c >= 0 && !is_word_boundary( str, c ) )
--c;
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX( str, c ); // always move at least one character
while (c >= 0 && !is_word_boundary(str, c))
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, c);
if( 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 )
{
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 ) )
++c;
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c);
if( 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
#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
static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
{
retry:
switch (key) {
default: {
#ifdef STB_TEXTEDIT_KEYTOTEXT
// This is not suitable for UTF-8 support.
int c = STB_TEXTEDIT_KEYTOTEXT(key);
if (c > 0) {
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE) c;
// 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;
}
}
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
stb_textedit_text(str, state, &ch, 1);
}
#endif
break;
}
@ -775,7 +812,7 @@ retry:
stb_textedit_move_to_first(state);
else
if (state->cursor > 0)
--state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
state->has_preferred_x = 0;
break;
@ -784,7 +821,7 @@ retry:
if (STB_TEXT_HAS_SELECTION(state))
stb_textedit_move_to_last(str, state);
else
++state->cursor;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
stb_textedit_clamp(str, state);
state->has_preferred_x = 0;
break;
@ -794,7 +831,7 @@ retry:
stb_textedit_prep_selection_at_cursor(state);
// move selection left
if (state->select_end > 0)
--state->select_end;
state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end);
state->cursor = state->select_end;
state->has_preferred_x = 0;
break;
@ -844,7 +881,7 @@ retry:
case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:
stb_textedit_prep_selection_at_cursor(state);
// move selection right
++state->select_end;
state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end);
stb_textedit_clamp(str, state);
state->cursor = state->select_end;
state->has_preferred_x = 0;
@ -891,8 +928,9 @@ retry:
state->cursor = start;
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
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);
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
@ -900,7 +938,8 @@ retry:
x += dx;
if (x > goal_x)
break;
++state->cursor;
i += next - state->cursor;
state->cursor = next;
}
stb_textedit_clamp(str, state);
@ -953,8 +992,9 @@ retry:
state->cursor = find.prev_first;
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
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);
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
break;
@ -962,7 +1002,8 @@ retry:
x += dx;
if (x > goal_x)
break;
++state->cursor;
i += next - state->cursor;
state->cursor = next;
}
stb_textedit_clamp(str, state);
@ -975,8 +1016,13 @@ retry:
// go to previous line
// (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;
while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE)
--prev_scan;
while (prev_scan > 0)
{
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.prev_first = prev_scan;
}
@ -990,7 +1036,7 @@ retry:
else {
int n = STB_TEXTEDIT_STRINGLEN(str);
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;
break;
@ -1002,8 +1048,9 @@ retry:
else {
stb_textedit_clamp(str, state);
if (state->cursor > 0) {
stb_textedit_delete(str, state, state->cursor-1, 1);
--state->cursor;
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
stb_textedit_delete(str, state, prev, state->cursor - prev);
state->cursor = prev;
}
}
state->has_preferred_x = 0;
@ -1054,7 +1101,7 @@ retry:
if (state->single_line)
state->cursor = 0;
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;
break;
@ -1066,9 +1113,9 @@ retry:
stb_textedit_clamp(str, state);
stb_textedit_move_to_first(state);
if (state->single_line)
state->cursor = n;
state->cursor = n;
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;
break;
}
@ -1082,7 +1129,7 @@ retry:
if (state->single_line)
state->cursor = 0;
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->has_preferred_x = 0;
break;
@ -1097,7 +1144,7 @@ retry:
if (state->single_line)
state->cursor = n;
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->has_preferred_x = 0;
break;

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[1] = (float)y2;
if (equal(q0,q1) || equal(q1,q2)) {
x0 = (int)verts[i-1].x;
y0 = (int)verts[i-1].y;
x0 = (int)verts[i-1].x; //-V1048
y0 = (int)verts[i-1].y; //-V1048
x1 = (int)verts[i ].x;
y1 = (int)verts[i ].y;
if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {

323
skeleton/sdl3.cpp Normal file
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

View File

@ -153,6 +153,7 @@ if(LIBRW_PLATFORM_GL3)
if (NOT TARGET glfw)
find_package(glfw3 REQUIRED)
endif()
target_compile_definitions(librw PUBLIC LIBRW_GLFW)
target_link_libraries(librw
PUBLIC
glfw
@ -169,6 +170,13 @@ if(LIBRW_PLATFORM_GL3)
PUBLIC
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()
set(OpenGL_GL_PREFERENCE GLVND)

View File

@ -1246,8 +1246,12 @@ getFramebufferRect(Raster *frameBuffer)
if(fb->type == Raster::CAMERA){
#ifdef LIBRW_SDL2
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);
#else
missing implementation
#endif
}else{
r.w = fb->width;
@ -1412,12 +1416,20 @@ showRaster(Raster *raster, uint32 flags)
else
SDL_GL_SetSwapInterval(0);
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)
glfwSwapInterval(1);
else
glfwSwapInterval(0);
glfwSwapBuffers(glGlobals.window);
#else
not implemented
#endif
}
@ -1603,7 +1615,174 @@ stopSDL2(void)
SDL_DestroyWindow(glGlobals.window);
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
addVideoMode(const GLFWvidmode *mode)
@ -1767,6 +1946,8 @@ stopGLFW(void)
glfwDestroyWindow(glGlobals.window);
return 1;
}
#else
not implemented
#endif
static int
@ -2007,7 +2188,94 @@ deviceSystemSDL2(DeviceReq req, void *arg, int32 n)
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
deviceSystemGLFW(DeviceReq req, void *arg, int32 n)
@ -2094,6 +2362,10 @@ deviceSystemGLFW(DeviceReq req, void *arg, int32 n)
return 1;
}
#else
not implemented
#endif
Device renderdevice = {
@ -2115,8 +2387,12 @@ Device renderdevice = {
gl3::im3DEnd,
#ifdef LIBRW_SDL2
gl3::deviceSystemSDL2
#else
#elif defined(LIBRW_SDL3)
gl3::deviceSystemSDL3
#elif defined(LIBRW_GLFW)
gl3::deviceSystemGLFW
#else
not implemented
#endif
};

View File

@ -2,8 +2,12 @@
#include "glad/glad.h"
#ifdef LIBRW_SDL2
#include <SDL.h>
#else
#elif defined(LIBRW_SDL3)
#include <SDL3/SDL.h>
#elif defined(LIBRW_GLFW)
#include <GLFW/glfw3.h>
#else
not implemented
#endif
#endif
@ -15,8 +19,13 @@ struct EngineOpenParams
#ifdef LIBRW_SDL2
SDL_Window **window;
bool32 fullscreen;
#else
#elif defined(LIBRW_SDL3)
SDL_Window **window;
bool32 fullscreen;
#elif defined(LIBRW_GLFW)
GLFWwindow **window;
#else
not implemented
#endif
int width, height;
const char *windowtitle;

View File

@ -26,8 +26,12 @@ struct DisplayMode
{
#ifdef LIBRW_SDL2
SDL_DisplayMode mode;
#else
#elif defined(LIBRW_SDL3)
SDL_DisplayMode mode;
#elif defined(LIBRW_GLFW)
GLFWvidmode mode;
#else
not implemented
#endif
int32 depth;
uint32 flags;
@ -42,13 +46,22 @@ struct GlGlobals
int numDisplays;
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 *window;
GLFWmonitor *monitor;
int numMonitors;
int currentMonitor;
#else
not implemented
#endif
DisplayMode *modes;

View File

@ -7,7 +7,7 @@ target_link_libraries(dumprwtree
librw::librw
)
if(LIBRW_GL3_GFXLIB STREQUAL "SDL2")
if(LIBRW_GL3_GFXLIB MATCHES "SDL[23]")
target_compile_definitions(dumprwtree PRIVATE SDL_MAIN_HANDLED)
endif()

View File

@ -7,7 +7,7 @@ target_link_libraries(ska2anm
librw::librw
)
if(LIBRW_GL3_GFXLIB STREQUAL "SDL2")
if(LIBRW_GL3_GFXLIB MATCHES "SDL[23]")
target_compile_definitions(ska2anm PRIVATE SDL_MAIN_HANDLED)
endif()