├── imgui-qt3d.pro ├── src ├── src.pro ├── imgui-qt3d-test │ ├── imgui-qt3d-test.pro │ ├── gui.h │ ├── gui.cpp │ └── main.cpp ├── imgui-qt3d │ ├── imgui-qt3d.pro │ ├── imguiqt3dwindow.h │ ├── imguimanager.h │ ├── imguiqt3dwindow.cpp │ └── imguimanager.cpp └── 3rdparty │ └── imgui │ ├── LICENSE.txt │ ├── imconfig.h │ ├── stb_rect_pack.h │ ├── TODO.txt │ ├── README.md │ └── stb_textedit.h └── README.md /imgui-qt3d.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | SUBDIRS = src 3 | -------------------------------------------------------------------------------- /src/src.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | SUBDIRS = imgui-qt3d imgui-qt3d-test 3 | imgui-qt3d-test.depends = imgui-qt3d 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Integration of [Dear ImGui](https://github.com/ocornut/imgui) with [Qt 3D](https://doc.qt.io/qt-5/qt3d-index.html) via the Entity-Component system and the framegraph, 2 | meaning that direct usage of OpenGL or other graphics APIs is avoided and no private APIs or engine changes are necessary. 3 | 4 | Also a good example for integrating any foreign engine that streams vertex/index data. 5 | 6 | Tested with qt3d/5.11. Should work with dev and older branches as well but those have not been tested. 7 | -------------------------------------------------------------------------------- /src/imgui-qt3d-test/imgui-qt3d-test.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | TARGET = imgui-qt3d-test 3 | BINDIR = $$OUT_PWD/../../bin 4 | DESTDIR = $$BINDIR 5 | LIBS += -L"$$BINDIR" -limgui-qt3d 6 | PRE_TARGETDEPS += $$BINDIR/$${QMAKE_PREFIX_STATICLIB}imgui-qt3d.$${QMAKE_EXTENSION_STATICLIB} 7 | 8 | QT += 3dcore 3drender 3dinput 3dlogic 3danimation 3dextras 9 | 10 | INCLUDEPATH += $$PWD/../imgui-qt3d $$PWD/../3rdparty/imgui 11 | 12 | SOURCES += \ 13 | main.cpp \ 14 | gui.cpp 15 | 16 | HEADERS += \ 17 | gui.h 18 | -------------------------------------------------------------------------------- /src/imgui-qt3d/imgui-qt3d.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = lib 2 | TARGET = imgui-qt3d 3 | BINDIR = $$OUT_PWD/../../bin 4 | DESTDIR = $$BINDIR 5 | 6 | CONFIG += staticlib 7 | QT += 3dcore 3drender 3dinput 3dlogic 3danimation 8 | 9 | INCLUDEPATH += ../3rdparty/imgui 10 | 11 | SOURCES += \ 12 | ../3rdparty/imgui/imgui.cpp \ 13 | ../3rdparty/imgui/imgui_draw.cpp \ 14 | ../3rdparty/imgui/imgui_demo.cpp \ 15 | imguimanager.cpp \ 16 | imguiqt3dwindow.cpp 17 | 18 | HEADERS += \ 19 | imguimanager.h \ 20 | imguiqt3dwindow.h 21 | -------------------------------------------------------------------------------- /src/imgui-qt3d-test/gui.h: -------------------------------------------------------------------------------- 1 | #ifndef GUI_H 2 | #define GUI_H 3 | 4 | #include 5 | 6 | class ImguiManager; 7 | 8 | namespace Qt3DCore { 9 | class QEntity; 10 | } 11 | 12 | namespace Qt3DRender { 13 | class QTexture2D; 14 | } 15 | 16 | class Gui 17 | { 18 | public: 19 | void setManager(ImguiManager *mgr) { m_manager = mgr; } 20 | void frame(Qt3DCore::QEntity *rootEntity); 21 | 22 | private: 23 | ImguiManager *m_manager = nullptr; 24 | bool show_test_window = true; 25 | bool show_another_window = false; 26 | ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); 27 | Qt3DRender::QTexture2D *m_customTexture = nullptr; 28 | }; 29 | 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/3rdparty/imgui/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2017 Omar Cornut and ImGui contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/imgui-qt3d-test/gui.cpp: -------------------------------------------------------------------------------- 1 | #include "gui.h" 2 | #include "imguimanager.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class TextureContents : public Qt3DRender::QPaintedTextureImage 9 | { 10 | public: 11 | void paint(QPainter *painter) override { 12 | const QRect r = QRect(QPoint(0, 0), size()); 13 | painter->fillRect(r, Qt::white); 14 | painter->setBrush(Qt::green); 15 | painter->drawEllipse(r.center(), 100, 50); 16 | painter->setPen(Qt::red); 17 | painter->drawText(r, Qt::AlignCenter, QLatin1String("Hello from QPainter")); 18 | } 19 | }; 20 | 21 | void Gui::frame(Qt3DCore::QEntity *rootEntity) 22 | { 23 | // 1. Show a simple window 24 | // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets appears in a window automatically called "Debug" 25 | { 26 | static float f = 0.0f; 27 | ImGui::Text("Hello, world!"); 28 | ImGui::SliderFloat("float", &f, 0.0f, 1.0f); 29 | ImGui::ColorEdit3("clear color", (float*)&clear_color); 30 | if (ImGui::Button("Test Window")) show_test_window ^= 1; 31 | if (ImGui::Button("Another Window")) show_another_window ^= 1; 32 | ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); 33 | 34 | // Add some extra content to exercise our Qt3D integration a bit. 35 | if (ImGui::Button("Scale up")) 36 | m_manager->setScale(m_manager->scale() + 0.2f); 37 | if (ImGui::Button("Scale down")) 38 | m_manager->setScale(m_manager->scale() - 0.2f); 39 | 40 | const QSize customImageSize(320, 200); 41 | if (!m_customTexture) { 42 | // rootEntity's only purpose is to serve as a parent for resources like textures 43 | m_customTexture = new Qt3DRender::QTexture2D(rootEntity); 44 | Qt3DRender::QPaintedTextureImage *customTextureContents = new TextureContents; 45 | customTextureContents->setWidth(customImageSize.width()); 46 | customTextureContents->setHeight(customImageSize.height()); 47 | m_customTexture->addTextureImage(customTextureContents); 48 | } 49 | ImGui::Image(m_customTexture, ImVec2(customImageSize.width(), customImageSize.height())); 50 | } 51 | 52 | // 2. Show another simple window, this time using an explicit Begin/End pair 53 | if (show_another_window) 54 | { 55 | ImGui::Begin("Another Window", &show_another_window); 56 | ImGui::Text("Hello from another window!"); 57 | ImGui::End(); 58 | } 59 | 60 | // 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowTestWindow() 61 | if (show_test_window) 62 | { 63 | ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiCond_FirstUseEver); 64 | ImGui::ShowTestWindow(&show_test_window); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/3rdparty/imgui/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // USER IMPLEMENTATION 3 | // This file contains compile-time options for ImGui. 4 | // Other options (memory allocation overrides, callbacks, etc.) can be set at runtime via the ImGuiIO structure - ImGui::GetIO(). 5 | //----------------------------------------------------------------------------- 6 | 7 | #pragma once 8 | 9 | //---- Define assertion handler. Defaults to calling assert(). 10 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 11 | 12 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows. 13 | //#define IMGUI_API __declspec( dllexport ) 14 | //#define IMGUI_API __declspec( dllimport ) 15 | 16 | //---- Include imgui_user.h at the end of imgui.h 17 | //#define IMGUI_INCLUDE_IMGUI_USER_H 18 | 19 | //---- Don't implement default handlers for Windows (so as not to link with OpenClipboard() and others Win32 functions) 20 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS 21 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS 22 | 23 | //---- Don't implement test window functionality (ShowTestWindow()/ShowStyleEditor()/ShowUserGuide() methods will be empty) 24 | //---- It is very strongly recommended to NOT disable the test windows. Please read the comment at the top of imgui_demo.cpp to learn why. 25 | //#define IMGUI_DISABLE_TEST_WINDOWS 26 | 27 | //---- Don't define obsolete functions names. Consider enabling from time to time or when updating to reduce like hood of using already obsolete function/names 28 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 29 | 30 | //---- Pack colors to BGRA instead of RGBA (remove need to post process vertex buffer in back ends) 31 | //#define IMGUI_USE_BGRA_PACKED_COLOR 32 | 33 | //---- Implement STB libraries in a namespace to avoid conflicts 34 | //#define IMGUI_STB_NAMESPACE ImGuiStb 35 | 36 | //---- Define constructor and implicit cast operators to convert back<>forth from your math types and ImVec2/ImVec4. 37 | /* 38 | #define IM_VEC2_CLASS_EXTRA \ 39 | ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ 40 | operator MyVec2() const { return MyVec2(x,y); } 41 | 42 | #define IM_VEC4_CLASS_EXTRA \ 43 | ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ 44 | operator MyVec4() const { return MyVec4(x,y,z,w); } 45 | */ 46 | 47 | //---- Use 32-bit vertex indices (instead of default: 16-bit) to allow meshes with more than 64K vertices 48 | //#define ImDrawIdx unsigned int 49 | 50 | //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. 51 | //---- e.g. create variants of the ImGui::Value() helper for your low-level math types, or your own widgets/helpers. 52 | /* 53 | namespace ImGui 54 | { 55 | void Value(const char* prefix, const MyMatrix44& v, const char* float_format = NULL); 56 | } 57 | */ 58 | 59 | // Qt3D does not like 2 component position attributes (in bounding volume calculation for instance). 60 | // Therefore, add a z vertex coordinate. 61 | #define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT \ 62 | struct ImDrawVert { \ 63 | ImVec2 pos; \ 64 | float z; \ 65 | ImVec2 uv; \ 66 | ImU32 col; \ 67 | }; 68 | -------------------------------------------------------------------------------- /src/imgui-qt3d/imguiqt3dwindow.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #ifndef IMGUIQT3DWINDOW_H 52 | #define IMGUIQT3DWINDOW_H 53 | 54 | #include 55 | #include 56 | #include 57 | 58 | namespace Qt3DRender { 59 | class QCamera; 60 | class QLayer; 61 | class QFilterKey; 62 | } 63 | 64 | // Provides a window with a Qt3D scene and a framegraph with a gui pass. This 65 | // is just an example since the framegraph has to be adapted in arbitrary ways 66 | // in real projects. 67 | class ImguiQt3DWindow : public QWindow 68 | { 69 | public: 70 | ImguiQt3DWindow(); 71 | 72 | typedef std::function CreateSceneFunc; 73 | void setCreateSceneFunc(CreateSceneFunc f) { m_createScene = f; } 74 | 75 | Qt3DRender::QLayer *guiTag() const { return m_guiTag; } 76 | Qt3DRender::QLayer *activeGuiTag() const { return m_activeGuiTag; } 77 | Qt3DRender::QFilterKey *guiTechniqueFilterKey() const { return m_guiTechniqueFilterKey; } 78 | 79 | protected: 80 | void exposeEvent(QExposeEvent *) override; 81 | void resizeEvent(QResizeEvent *) override; 82 | 83 | private: 84 | void createAspectEngine(); 85 | void createFramegraph(); 86 | 87 | QScopedPointer m_aspectEngine; 88 | Qt3DCore::QEntity *m_rootEntity = nullptr; 89 | Qt3DRender::QCamera *m_sceneCamera = nullptr; 90 | Qt3DRender::QCamera *m_guiCamera = nullptr; 91 | Qt3DRender::QLayer *m_guiTag = nullptr; 92 | Qt3DRender::QLayer *m_activeGuiTag = nullptr; 93 | Qt3DRender::QFilterKey *m_guiTechniqueFilterKey = nullptr; 94 | 95 | CreateSceneFunc m_createScene = nullptr; 96 | }; 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /src/imgui-qt3d/imguimanager.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #ifndef IMGUIMANAGER_H 52 | #define IMGUIMANAGER_H 53 | 54 | #include 55 | #include 56 | #include 57 | 58 | class ImguiInputEventFilter; 59 | 60 | namespace Qt3DCore { 61 | class QTransform; 62 | } 63 | 64 | namespace Qt3DRender { 65 | class QBuffer; 66 | class QTexture2D; 67 | class QMaterial; 68 | class QLayer; 69 | class QGeometryRenderer; 70 | class QShaderProgram; 71 | class QScissorTest; 72 | class QParameter; 73 | class QFilterKey; 74 | class QDepthTest; 75 | class QNoDepthMask; 76 | class QBlendEquation; 77 | class QBlendEquationArguments; 78 | class QCullFace; 79 | class QColorMask; 80 | } 81 | 82 | class ImguiManager { 83 | public: 84 | ~ImguiManager(); 85 | 86 | typedef std::function FrameFunc; 87 | void setFrameFunc(FrameFunc f) { m_frame = f; } 88 | 89 | void setInputEventSource(QObject *src); 90 | 91 | struct OutputInfo { 92 | QSize size; 93 | qreal dpr; 94 | Qt3DRender::QLayer *guiTag; 95 | Qt3DRender::QLayer *activeGuiTag; 96 | Qt3DRender::QFilterKey *guiTechniqueFilterKey; 97 | }; 98 | typedef std::function OutputInfoFunc; 99 | void setOutputInfoFunc(OutputInfoFunc f) { m_outputInfoFunc = f; } 100 | 101 | void initialize(Qt3DCore::QEntity *rootEntity); 102 | void releaseResources(); 103 | 104 | bool isEnabled() const { return m_enabled; } 105 | void setEnabled(bool enabled); 106 | 107 | float scale() const { return m_scale; } 108 | void setScale(float scale); 109 | 110 | private: 111 | struct CmdListEntry; 112 | void resizePool(CmdListEntry *e, int newSize); 113 | Qt3DRender::QMaterial *buildMaterial(Qt3DRender::QScissorTest **scissor); 114 | void updateGeometry(CmdListEntry *e, int idx, uint elemCount, int vertexCount, int indexCount, const void *indexOffset); 115 | void update3D(); 116 | void updateInput(); 117 | 118 | FrameFunc m_frame = nullptr; 119 | OutputInfoFunc m_outputInfoFunc = nullptr; 120 | OutputInfo m_outputInfo; 121 | QObject *m_inputEventSource = nullptr; 122 | ImguiInputEventFilter *m_inputEventFilter = nullptr; 123 | bool m_inputInitialized = false; 124 | bool m_enabled = true; 125 | float m_scale = 1.0f; 126 | 127 | Qt3DCore::QEntity *m_rootEntity = nullptr; 128 | Qt3DRender::QTexture2D *m_atlasTex = nullptr; 129 | struct SharedRenderPassData { 130 | bool valid = false; 131 | Qt3DRender::QShaderProgram *progES2; 132 | Qt3DRender::QShaderProgram *progGL3; 133 | Qt3DRender::QFilterKey *techniqueFilterKey; 134 | Qt3DRender::QDepthTest *depthTest; 135 | Qt3DRender::QNoDepthMask *noDepthWrite; 136 | Qt3DRender::QBlendEquation *blendFunc; 137 | Qt3DRender::QBlendEquationArguments *blendArgs; 138 | Qt3DRender::QCullFace *cullFace; 139 | Qt3DRender::QColorMask *colorMask; 140 | QVector enabledToggle; 141 | } rpd; 142 | 143 | struct CmdEntry { 144 | Qt3DCore::QEntity *entity = nullptr; 145 | Qt3DRender::QMaterial *material = nullptr; 146 | Qt3DRender::QGeometryRenderer *geomRenderer = nullptr; 147 | Qt3DCore::QTransform *transform = nullptr; 148 | Qt3DRender::QScissorTest *scissor = nullptr; 149 | Qt3DRender::QParameter *texParam = nullptr; 150 | }; 151 | 152 | struct CmdListEntry { 153 | Qt3DRender::QBuffer *vbuf = nullptr; 154 | Qt3DRender::QBuffer *ibuf = nullptr; 155 | QVector cmds; 156 | int activeSize = 0; 157 | }; 158 | 159 | QVector m_cmdList; 160 | }; 161 | 162 | #endif 163 | -------------------------------------------------------------------------------- /src/imgui-qt3d-test/main.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | 64 | #include "imguimanager.h" 65 | #include "imguiqt3dwindow.h" 66 | 67 | #include "gui.h" 68 | 69 | int main(int argc, char **argv) 70 | { 71 | QGuiApplication app(argc, argv); 72 | 73 | QSurfaceFormat fmt; 74 | fmt.setDepthBufferSize(24); 75 | if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { 76 | fmt.setVersion(3, 2); 77 | fmt.setProfile(QSurfaceFormat::CoreProfile); 78 | } 79 | QSurfaceFormat::setDefaultFormat(fmt); 80 | 81 | ImguiQt3DWindow w; 82 | w.setFormat(fmt); 83 | 84 | Gui gui; 85 | ImguiManager guiMgr; 86 | gui.setManager(&guiMgr); 87 | guiMgr.setFrameFunc(std::bind(&Gui::frame, &gui, std::placeholders::_1)); 88 | guiMgr.setInputEventSource(&w); 89 | guiMgr.setOutputInfoFunc([&w]() { 90 | ImguiManager::OutputInfo outputInfo; 91 | outputInfo.size = w.size(); 92 | outputInfo.dpr = w.devicePixelRatio(); 93 | outputInfo.guiTag = w.guiTag(); 94 | outputInfo.activeGuiTag = w.activeGuiTag(); 95 | outputInfo.guiTechniqueFilterKey = w.guiTechniqueFilterKey(); 96 | return outputInfo; 97 | }); 98 | // uncomment to start with gui hidden 99 | //guiMgr.setEnabled(false); 100 | 101 | w.setCreateSceneFunc([&guiMgr](Qt3DCore::QEntity *parent) { 102 | guiMgr.initialize(parent); 103 | 104 | Qt3DCore::QEntity *cube = new Qt3DCore::QEntity(parent); 105 | Qt3DExtras::QCuboidMesh *cubeGeom = new Qt3DExtras::QCuboidMesh; 106 | cubeGeom->setXExtent(2); 107 | cubeGeom->setYExtent(2); 108 | cubeGeom->setZExtent(2); 109 | Qt3DCore::QTransform *cubeTrans = new Qt3DCore::QTransform; 110 | cubeTrans->setTranslation(QVector3D(18, 5, -10)); 111 | Qt3DExtras::QPhongMaterial *cubeMat = new Qt3DExtras::QPhongMaterial; 112 | cube->addComponent(cubeGeom); 113 | cube->addComponent(cubeTrans); 114 | cube->addComponent(cubeMat); 115 | 116 | Qt3DCore::QEntity *cube2 = new Qt3DCore::QEntity(parent); 117 | // or, why have a cube when we can have a torus instead 118 | Qt3DExtras::QTorusMesh *cubeGeom2 = new Qt3DExtras::QTorusMesh; 119 | cubeGeom2->setRadius(5); 120 | cubeGeom2->setMinorRadius(1); 121 | cubeGeom2->setRings(100); 122 | cubeGeom2->setSlices(20); 123 | Qt3DCore::QTransform *cubeTrans2 = new Qt3DCore::QTransform; 124 | cubeTrans2->setTranslation(QVector3D(-15, -4, -20)); 125 | cube2->addComponent(cubeGeom2); 126 | cube2->addComponent(cubeTrans2); 127 | cube2->addComponent(cubeMat); 128 | 129 | auto rotAnim = [](QObject *obj, const QByteArray &name, float start, float end, int duration) { 130 | QPropertyAnimation *anim = new QPropertyAnimation(obj, name); 131 | anim->setStartValue(start); 132 | anim->setEndValue(end); 133 | anim->setDuration(duration); 134 | anim->setLoopCount(-1); 135 | anim->start(); 136 | }; 137 | rotAnim(cubeTrans, "rotationX", 0.0f, 360.0f, 5000); 138 | rotAnim(cubeTrans2, "rotationY", 0.0f, 360.0f, 5000); 139 | 140 | // an entity that toggles the gui when pressed. 141 | // replace with a QText2DEntity some day when it actually works. in the meantime a sphere will do. 142 | Qt3DCore::QEntity *toggleText = new Qt3DCore::QEntity(parent); 143 | Qt3DExtras::QSphereMesh *toggleTextGeom = new Qt3DExtras::QSphereMesh; 144 | Qt3DCore::QTransform *toggleTextTrans = new Qt3DCore::QTransform; 145 | toggleTextTrans->setTranslation(QVector3D(-14, 7, -5)); 146 | toggleTextTrans->setScale(0.5f); 147 | Qt3DExtras::QPhongMaterial *toggleTextMat = new Qt3DExtras::QPhongMaterial; 148 | toggleTextMat->setDiffuse(guiMgr.isEnabled() ? Qt::green : Qt::red); 149 | toggleText->addComponent(toggleTextGeom); 150 | toggleText->addComponent(toggleTextTrans); 151 | toggleText->addComponent(toggleTextMat); 152 | Qt3DRender::QObjectPicker *toggleTextPicker = new Qt3DRender::QObjectPicker; 153 | QObject::connect(toggleTextPicker, &Qt3DRender::QObjectPicker::pressed, [toggleTextMat, &guiMgr](Qt3DRender::QPickEvent *) { 154 | guiMgr.setEnabled(!guiMgr.isEnabled()); 155 | toggleTextMat->setDiffuse(guiMgr.isEnabled() ? Qt::green : Qt::red); 156 | }); 157 | toggleText->addComponent(toggleTextPicker); 158 | }); 159 | 160 | w.resize(1280, 720); 161 | w.show(); 162 | 163 | return app.exec(); 164 | } 165 | -------------------------------------------------------------------------------- /src/imgui-qt3d/imguiqt3dwindow.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #include "imguiqt3dwindow.h" 52 | 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | 73 | ImguiQt3DWindow::ImguiQt3DWindow() 74 | { 75 | setSurfaceType(QSurface::OpenGLSurface); 76 | createAspectEngine(); 77 | } 78 | 79 | void ImguiQt3DWindow::createAspectEngine() 80 | { 81 | m_aspectEngine.reset(new Qt3DCore::QAspectEngine); 82 | m_aspectEngine->registerAspect(new Qt3DRender::QRenderAspect); 83 | m_aspectEngine->registerAspect(new Qt3DInput::QInputAspect); 84 | m_aspectEngine->registerAspect(new Qt3DAnimation::QAnimationAspect); 85 | m_aspectEngine->registerAspect(new Qt3DLogic::QLogicAspect); 86 | } 87 | 88 | void ImguiQt3DWindow::exposeEvent(QExposeEvent *) 89 | { 90 | if (isExposed() && !m_rootEntity) { 91 | createFramegraph(); 92 | if (m_createScene) 93 | m_createScene(m_rootEntity); 94 | m_aspectEngine->setRootEntity(Qt3DCore::QEntityPtr(m_rootEntity)); 95 | } 96 | } 97 | 98 | void ImguiQt3DWindow::resizeEvent(QResizeEvent *) 99 | { 100 | if (m_sceneCamera) 101 | m_sceneCamera->setAspectRatio(width() / float(height())); 102 | 103 | if (m_guiCamera) { 104 | m_guiCamera->setRight(width() * devicePixelRatio()); 105 | m_guiCamera->setBottom(height() * devicePixelRatio()); 106 | } 107 | } 108 | 109 | void ImguiQt3DWindow::createFramegraph() 110 | { 111 | m_rootEntity = new Qt3DCore::QEntity; 112 | m_rootEntity->setObjectName(QLatin1String("root")); 113 | 114 | Qt3DRender::QRenderSettings *frameGraphComponent = new Qt3DRender::QRenderSettings(m_rootEntity); 115 | 116 | // RenderSurfaceSelector -> Viewport -> (ClearBuffers -> NoDraw) | (CameraSelector -> TechniqueFilter -> LayerFilter) | (CameraSelector -> TechniqueFilter -> SortPolicy -> LayerFilter) 117 | 118 | Qt3DRender::QRenderSurfaceSelector *targetSel = new Qt3DRender::QRenderSurfaceSelector; 119 | 120 | Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport(targetSel); 121 | viewport->setNormalizedRect(QRectF(0, 0, 1, 1)); 122 | 123 | Qt3DRender::QClearBuffers *clearBuffers = new Qt3DRender::QClearBuffers(viewport); 124 | clearBuffers->setBuffers(Qt3DRender::QClearBuffers::ColorDepthBuffer); 125 | clearBuffers->setClearColor(Qt::lightGray); 126 | new Qt3DRender::QNoDraw(clearBuffers); 127 | 128 | m_guiTag = new Qt3DRender::QLayer; // all gui entities are tagged with this 129 | m_activeGuiTag = new Qt3DRender::QLayer; // active gui entities - added/removed to entities dynamically by imguimanager 130 | m_guiTechniqueFilterKey = new Qt3DRender::QFilterKey; 131 | m_guiTechniqueFilterKey->setName(QLatin1String("gui")); 132 | m_guiTechniqueFilterKey->setValue(QLatin1String("1")); 133 | 134 | // The normal scene, filter for 'forward' which is set by the "default" materials in Qt3DExtras. 135 | // Change the key (or switch to RenderPassFilter, extend for multi-pass, etc.) when using custom materials. 136 | Qt3DRender::QCameraSelector *cameraSel = new Qt3DRender::QCameraSelector(viewport); 137 | m_sceneCamera = new Qt3DRender::QCamera; 138 | m_sceneCamera->setProjectionType(Qt3DRender::QCameraLens::PerspectiveProjection); 139 | m_sceneCamera->setFieldOfView(60); 140 | m_sceneCamera->setPosition(QVector3D(0, 0, 10)); 141 | m_sceneCamera->setViewCenter(QVector3D(0, 0, 0)); 142 | m_sceneCamera->setNearPlane(10); 143 | m_sceneCamera->setFarPlane(5000); 144 | m_sceneCamera->setAspectRatio(width() / float(height())); 145 | cameraSel->setCamera(m_sceneCamera); 146 | Qt3DRender::QTechniqueFilter *tfilter = new Qt3DRender::QTechniqueFilter(cameraSel); 147 | Qt3DRender::QFilterKey *tfilterkey = new Qt3DRender::QFilterKey; 148 | tfilterkey->setName(QLatin1String("renderingStyle")); 149 | tfilterkey->setValue(QLatin1String("forward")); 150 | tfilter->addMatch(tfilterkey); 151 | // Skip all gui entities in this pass. 152 | Qt3DRender::QLayerFilter *lfilter = new Qt3DRender::QLayerFilter(tfilter); 153 | lfilter->setFilterMode(Qt3DRender::QLayerFilter::DiscardAnyMatchingLayers); 154 | lfilter->addLayer(m_guiTag); 155 | 156 | // The gui pass. 157 | tfilter = new Qt3DRender::QTechniqueFilter(viewport); 158 | tfilter->addMatch(m_guiTechniqueFilterKey); 159 | cameraSel = new Qt3DRender::QCameraSelector(tfilter); 160 | m_guiCamera = new Qt3DRender::QCamera; 161 | m_guiCamera->setProjectionType(Qt3DRender::QCameraLens::OrthographicProjection); 162 | m_guiCamera->setLeft(0); 163 | m_guiCamera->setRight(width() * devicePixelRatio()); 164 | m_guiCamera->setTop(0); 165 | m_guiCamera->setBottom(height() * devicePixelRatio()); 166 | m_guiCamera->setNearPlane(-1); 167 | m_guiCamera->setFarPlane(1); 168 | cameraSel->setCamera(m_guiCamera); 169 | // imgui provides "draw calls" in back-to-front order but that will get 170 | // lost in the pipeline with Qt3D. Ensure via a SortPolicy instead. 171 | Qt3DRender::QSortPolicy *sortPolicy = new Qt3DRender::QSortPolicy(cameraSel); 172 | sortPolicy->setSortTypes(QVector() << Qt3DRender::QSortPolicy::BackToFront); 173 | // Only include entities for the _active_ gui in this pass. 174 | lfilter = new Qt3DRender::QLayerFilter(sortPolicy); 175 | lfilter->addLayer(m_activeGuiTag); 176 | 177 | targetSel->setSurface(this); 178 | frameGraphComponent->setActiveFrameGraph(targetSel); 179 | m_rootEntity->addComponent(frameGraphComponent); 180 | 181 | Qt3DInput::QInputSettings *inputSettings = new Qt3DInput::QInputSettings; 182 | inputSettings->setEventSource(this); 183 | m_rootEntity->addComponent(inputSettings); 184 | } 185 | -------------------------------------------------------------------------------- /src/3rdparty/imgui/stb_rect_pack.h: -------------------------------------------------------------------------------- 1 | // stb_rect_pack.h - v0.10 - public domain - rectangle packing 2 | // Sean Barrett 2014 3 | // 4 | // Useful for e.g. packing rectangular textures into an atlas. 5 | // Does not do rotation. 6 | // 7 | // Not necessarily the awesomest packing method, but better than 8 | // the totally naive one in stb_truetype (which is primarily what 9 | // this is meant to replace). 10 | // 11 | // Has only had a few tests run, may have issues. 12 | // 13 | // More docs to come. 14 | // 15 | // No memory allocations; uses qsort() and assert() from stdlib. 16 | // Can override those by defining STBRP_SORT and STBRP_ASSERT. 17 | // 18 | // This library currently uses the Skyline Bottom-Left algorithm. 19 | // 20 | // Please note: better rectangle packers are welcome! Please 21 | // implement them to the same API, but with a different init 22 | // function. 23 | // 24 | // Credits 25 | // 26 | // Library 27 | // Sean Barrett 28 | // Minor features 29 | // Martins Mozeiko 30 | // Bugfixes / warning fixes 31 | // Jeremy Jaussaud 32 | // 33 | // Version history: 34 | // 35 | // 0.10 (2016-10-25) remove cast-away-const to avoid warnings 36 | // 0.09 (2016-08-27) fix compiler warnings 37 | // 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) 38 | // 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) 39 | // 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort 40 | // 0.05: added STBRP_ASSERT to allow replacing assert 41 | // 0.04: fixed minor bug in STBRP_LARGE_RECTS support 42 | // 0.01: initial release 43 | // 44 | // LICENSE 45 | // 46 | // This software is dual-licensed to the public domain and under the following 47 | // license: you are granted a perpetual, irrevocable license to copy, modify, 48 | // publish, and distribute this file as you see fit. 49 | 50 | ////////////////////////////////////////////////////////////////////////////// 51 | // 52 | // INCLUDE SECTION 53 | // 54 | 55 | #ifndef STB_INCLUDE_STB_RECT_PACK_H 56 | #define STB_INCLUDE_STB_RECT_PACK_H 57 | 58 | #define STB_RECT_PACK_VERSION 1 59 | 60 | #ifdef STBRP_STATIC 61 | #define STBRP_DEF static 62 | #else 63 | #define STBRP_DEF extern 64 | #endif 65 | 66 | #ifdef __cplusplus 67 | extern "C" { 68 | #endif 69 | 70 | typedef struct stbrp_context stbrp_context; 71 | typedef struct stbrp_node stbrp_node; 72 | typedef struct stbrp_rect stbrp_rect; 73 | 74 | #ifdef STBRP_LARGE_RECTS 75 | typedef int stbrp_coord; 76 | #else 77 | typedef unsigned short stbrp_coord; 78 | #endif 79 | 80 | STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); 81 | // Assign packed locations to rectangles. The rectangles are of type 82 | // 'stbrp_rect' defined below, stored in the array 'rects', and there 83 | // are 'num_rects' many of them. 84 | // 85 | // Rectangles which are successfully packed have the 'was_packed' flag 86 | // set to a non-zero value and 'x' and 'y' store the minimum location 87 | // on each axis (i.e. bottom-left in cartesian coordinates, top-left 88 | // if you imagine y increasing downwards). Rectangles which do not fit 89 | // have the 'was_packed' flag set to 0. 90 | // 91 | // You should not try to access the 'rects' array from another thread 92 | // while this function is running, as the function temporarily reorders 93 | // the array while it executes. 94 | // 95 | // To pack into another rectangle, you need to call stbrp_init_target 96 | // again. To continue packing into the same rectangle, you can call 97 | // this function again. Calling this multiple times with multiple rect 98 | // arrays will probably produce worse packing results than calling it 99 | // a single time with the full rectangle array, but the option is 100 | // available. 101 | 102 | struct stbrp_rect 103 | { 104 | // reserved for your use: 105 | int id; 106 | 107 | // input: 108 | stbrp_coord w, h; 109 | 110 | // output: 111 | stbrp_coord x, y; 112 | int was_packed; // non-zero if valid packing 113 | 114 | }; // 16 bytes, nominally 115 | 116 | 117 | STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); 118 | // Initialize a rectangle packer to: 119 | // pack a rectangle that is 'width' by 'height' in dimensions 120 | // using temporary storage provided by the array 'nodes', which is 'num_nodes' long 121 | // 122 | // You must call this function every time you start packing into a new target. 123 | // 124 | // There is no "shutdown" function. The 'nodes' memory must stay valid for 125 | // the following stbrp_pack_rects() call (or calls), but can be freed after 126 | // the call (or calls) finish. 127 | // 128 | // Note: to guarantee best results, either: 129 | // 1. make sure 'num_nodes' >= 'width' 130 | // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' 131 | // 132 | // If you don't do either of the above things, widths will be quantized to multiples 133 | // of small integers to guarantee the algorithm doesn't run out of temporary storage. 134 | // 135 | // If you do #2, then the non-quantized algorithm will be used, but the algorithm 136 | // may run out of temporary storage and be unable to pack some rectangles. 137 | 138 | STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); 139 | // Optionally call this function after init but before doing any packing to 140 | // change the handling of the out-of-temp-memory scenario, described above. 141 | // If you call init again, this will be reset to the default (false). 142 | 143 | 144 | STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); 145 | // Optionally select which packing heuristic the library should use. Different 146 | // heuristics will produce better/worse results for different data sets. 147 | // If you call init again, this will be reset to the default. 148 | 149 | enum 150 | { 151 | STBRP_HEURISTIC_Skyline_default=0, 152 | STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, 153 | STBRP_HEURISTIC_Skyline_BF_sortHeight 154 | }; 155 | 156 | 157 | ////////////////////////////////////////////////////////////////////////////// 158 | // 159 | // the details of the following structures don't matter to you, but they must 160 | // be visible so you can handle the memory allocations for them 161 | 162 | struct stbrp_node 163 | { 164 | stbrp_coord x,y; 165 | stbrp_node *next; 166 | }; 167 | 168 | struct stbrp_context 169 | { 170 | int width; 171 | int height; 172 | int align; 173 | int init_mode; 174 | int heuristic; 175 | int num_nodes; 176 | stbrp_node *active_head; 177 | stbrp_node *free_head; 178 | stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' 179 | }; 180 | 181 | #ifdef __cplusplus 182 | } 183 | #endif 184 | 185 | #endif 186 | 187 | ////////////////////////////////////////////////////////////////////////////// 188 | // 189 | // IMPLEMENTATION SECTION 190 | // 191 | 192 | #ifdef STB_RECT_PACK_IMPLEMENTATION 193 | #ifndef STBRP_SORT 194 | #include 195 | #define STBRP_SORT qsort 196 | #endif 197 | 198 | #ifndef STBRP_ASSERT 199 | #include 200 | #define STBRP_ASSERT assert 201 | #endif 202 | 203 | #ifdef _MSC_VER 204 | #define STBRP__NOTUSED(v) (void)(v) 205 | #else 206 | #define STBRP__NOTUSED(v) (void)sizeof(v) 207 | #endif 208 | 209 | enum 210 | { 211 | STBRP__INIT_skyline = 1 212 | }; 213 | 214 | STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) 215 | { 216 | switch (context->init_mode) { 217 | case STBRP__INIT_skyline: 218 | STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); 219 | context->heuristic = heuristic; 220 | break; 221 | default: 222 | STBRP_ASSERT(0); 223 | } 224 | } 225 | 226 | STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) 227 | { 228 | if (allow_out_of_mem) 229 | // if it's ok to run out of memory, then don't bother aligning them; 230 | // this gives better packing, but may fail due to OOM (even though 231 | // the rectangles easily fit). @TODO a smarter approach would be to only 232 | // quantize once we've hit OOM, then we could get rid of this parameter. 233 | context->align = 1; 234 | else { 235 | // if it's not ok to run out of memory, then quantize the widths 236 | // so that num_nodes is always enough nodes. 237 | // 238 | // I.e. num_nodes * align >= width 239 | // align >= width / num_nodes 240 | // align = ceil(width/num_nodes) 241 | 242 | context->align = (context->width + context->num_nodes-1) / context->num_nodes; 243 | } 244 | } 245 | 246 | STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) 247 | { 248 | int i; 249 | #ifndef STBRP_LARGE_RECTS 250 | STBRP_ASSERT(width <= 0xffff && height <= 0xffff); 251 | #endif 252 | 253 | for (i=0; i < num_nodes-1; ++i) 254 | nodes[i].next = &nodes[i+1]; 255 | nodes[i].next = NULL; 256 | context->init_mode = STBRP__INIT_skyline; 257 | context->heuristic = STBRP_HEURISTIC_Skyline_default; 258 | context->free_head = &nodes[0]; 259 | context->active_head = &context->extra[0]; 260 | context->width = width; 261 | context->height = height; 262 | context->num_nodes = num_nodes; 263 | stbrp_setup_allow_out_of_mem(context, 0); 264 | 265 | // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) 266 | context->extra[0].x = 0; 267 | context->extra[0].y = 0; 268 | context->extra[0].next = &context->extra[1]; 269 | context->extra[1].x = (stbrp_coord) width; 270 | #ifdef STBRP_LARGE_RECTS 271 | context->extra[1].y = (1<<30); 272 | #else 273 | context->extra[1].y = 65535; 274 | #endif 275 | context->extra[1].next = NULL; 276 | } 277 | 278 | // find minimum y position if it starts at x1 279 | static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) 280 | { 281 | stbrp_node *node = first; 282 | int x1 = x0 + width; 283 | int min_y, visited_width, waste_area; 284 | 285 | STBRP__NOTUSED(c); 286 | 287 | STBRP_ASSERT(first->x <= x0); 288 | 289 | #if 0 290 | // skip in case we're past the node 291 | while (node->next->x <= x0) 292 | ++node; 293 | #else 294 | STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency 295 | #endif 296 | 297 | STBRP_ASSERT(node->x <= x0); 298 | 299 | min_y = 0; 300 | waste_area = 0; 301 | visited_width = 0; 302 | while (node->x < x1) { 303 | if (node->y > min_y) { 304 | // raise min_y higher. 305 | // we've accounted for all waste up to min_y, 306 | // but we'll now add more waste for everything we've visted 307 | waste_area += visited_width * (node->y - min_y); 308 | min_y = node->y; 309 | // the first time through, visited_width might be reduced 310 | if (node->x < x0) 311 | visited_width += node->next->x - x0; 312 | else 313 | visited_width += node->next->x - node->x; 314 | } else { 315 | // add waste area 316 | int under_width = node->next->x - node->x; 317 | if (under_width + visited_width > width) 318 | under_width = width - visited_width; 319 | waste_area += under_width * (min_y - node->y); 320 | visited_width += under_width; 321 | } 322 | node = node->next; 323 | } 324 | 325 | *pwaste = waste_area; 326 | return min_y; 327 | } 328 | 329 | typedef struct 330 | { 331 | int x,y; 332 | stbrp_node **prev_link; 333 | } stbrp__findresult; 334 | 335 | static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) 336 | { 337 | int best_waste = (1<<30), best_x, best_y = (1 << 30); 338 | stbrp__findresult fr; 339 | stbrp_node **prev, *node, *tail, **best = NULL; 340 | 341 | // align to multiple of c->align 342 | width = (width + c->align - 1); 343 | width -= width % c->align; 344 | STBRP_ASSERT(width % c->align == 0); 345 | 346 | node = c->active_head; 347 | prev = &c->active_head; 348 | while (node->x + width <= c->width) { 349 | int y,waste; 350 | y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); 351 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL 352 | // bottom left 353 | if (y < best_y) { 354 | best_y = y; 355 | best = prev; 356 | } 357 | } else { 358 | // best-fit 359 | if (y + height <= c->height) { 360 | // can only use it if it first vertically 361 | if (y < best_y || (y == best_y && waste < best_waste)) { 362 | best_y = y; 363 | best_waste = waste; 364 | best = prev; 365 | } 366 | } 367 | } 368 | prev = &node->next; 369 | node = node->next; 370 | } 371 | 372 | best_x = (best == NULL) ? 0 : (*best)->x; 373 | 374 | // if doing best-fit (BF), we also have to try aligning right edge to each node position 375 | // 376 | // e.g, if fitting 377 | // 378 | // ____________________ 379 | // |____________________| 380 | // 381 | // into 382 | // 383 | // | | 384 | // | ____________| 385 | // |____________| 386 | // 387 | // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned 388 | // 389 | // This makes BF take about 2x the time 390 | 391 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { 392 | tail = c->active_head; 393 | node = c->active_head; 394 | prev = &c->active_head; 395 | // find first node that's admissible 396 | while (tail->x < width) 397 | tail = tail->next; 398 | while (tail) { 399 | int xpos = tail->x - width; 400 | int y,waste; 401 | STBRP_ASSERT(xpos >= 0); 402 | // find the left position that matches this 403 | while (node->next->x <= xpos) { 404 | prev = &node->next; 405 | node = node->next; 406 | } 407 | STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); 408 | y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); 409 | if (y + height < c->height) { 410 | if (y <= best_y) { 411 | if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { 412 | best_x = xpos; 413 | STBRP_ASSERT(y <= best_y); 414 | best_y = y; 415 | best_waste = waste; 416 | best = prev; 417 | } 418 | } 419 | } 420 | tail = tail->next; 421 | } 422 | } 423 | 424 | fr.prev_link = best; 425 | fr.x = best_x; 426 | fr.y = best_y; 427 | return fr; 428 | } 429 | 430 | static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) 431 | { 432 | // find best position according to heuristic 433 | stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); 434 | stbrp_node *node, *cur; 435 | 436 | // bail if: 437 | // 1. it failed 438 | // 2. the best node doesn't fit (we don't always check this) 439 | // 3. we're out of memory 440 | if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { 441 | res.prev_link = NULL; 442 | return res; 443 | } 444 | 445 | // on success, create new node 446 | node = context->free_head; 447 | node->x = (stbrp_coord) res.x; 448 | node->y = (stbrp_coord) (res.y + height); 449 | 450 | context->free_head = node->next; 451 | 452 | // insert the new node into the right starting point, and 453 | // let 'cur' point to the remaining nodes needing to be 454 | // stiched back in 455 | 456 | cur = *res.prev_link; 457 | if (cur->x < res.x) { 458 | // preserve the existing one, so start testing with the next one 459 | stbrp_node *next = cur->next; 460 | cur->next = node; 461 | cur = next; 462 | } else { 463 | *res.prev_link = node; 464 | } 465 | 466 | // from here, traverse cur and free the nodes, until we get to one 467 | // that shouldn't be freed 468 | while (cur->next && cur->next->x <= res.x + width) { 469 | stbrp_node *next = cur->next; 470 | // move the current node to the free list 471 | cur->next = context->free_head; 472 | context->free_head = cur; 473 | cur = next; 474 | } 475 | 476 | // stitch the list back in 477 | node->next = cur; 478 | 479 | if (cur->x < res.x + width) 480 | cur->x = (stbrp_coord) (res.x + width); 481 | 482 | #ifdef _DEBUG 483 | cur = context->active_head; 484 | while (cur->x < context->width) { 485 | STBRP_ASSERT(cur->x < cur->next->x); 486 | cur = cur->next; 487 | } 488 | STBRP_ASSERT(cur->next == NULL); 489 | 490 | { 491 | stbrp_node *L1 = NULL, *L2 = NULL; 492 | int count=0; 493 | cur = context->active_head; 494 | while (cur) { 495 | L1 = cur; 496 | cur = cur->next; 497 | ++count; 498 | } 499 | cur = context->free_head; 500 | while (cur) { 501 | L2 = cur; 502 | cur = cur->next; 503 | ++count; 504 | } 505 | STBRP_ASSERT(count == context->num_nodes+2); 506 | } 507 | #endif 508 | 509 | return res; 510 | } 511 | 512 | static int rect_height_compare(const void *a, const void *b) 513 | { 514 | const stbrp_rect *p = (const stbrp_rect *) a; 515 | const stbrp_rect *q = (const stbrp_rect *) b; 516 | if (p->h > q->h) 517 | return -1; 518 | if (p->h < q->h) 519 | return 1; 520 | return (p->w > q->w) ? -1 : (p->w < q->w); 521 | } 522 | 523 | static int rect_width_compare(const void *a, const void *b) 524 | { 525 | const stbrp_rect *p = (const stbrp_rect *) a; 526 | const stbrp_rect *q = (const stbrp_rect *) b; 527 | if (p->w > q->w) 528 | return -1; 529 | if (p->w < q->w) 530 | return 1; 531 | return (p->h > q->h) ? -1 : (p->h < q->h); 532 | } 533 | 534 | static int rect_original_order(const void *a, const void *b) 535 | { 536 | const stbrp_rect *p = (const stbrp_rect *) a; 537 | const stbrp_rect *q = (const stbrp_rect *) b; 538 | return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); 539 | } 540 | 541 | #ifdef STBRP_LARGE_RECTS 542 | #define STBRP__MAXVAL 0xffffffff 543 | #else 544 | #define STBRP__MAXVAL 0xffff 545 | #endif 546 | 547 | STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) 548 | { 549 | int i; 550 | 551 | // we use the 'was_packed' field internally to allow sorting/unsorting 552 | for (i=0; i < num_rects; ++i) { 553 | rects[i].was_packed = i; 554 | #ifndef STBRP_LARGE_RECTS 555 | STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); 556 | #endif 557 | } 558 | 559 | // sort according to heuristic 560 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); 561 | 562 | for (i=0; i < num_rects; ++i) { 563 | if (rects[i].w == 0 || rects[i].h == 0) { 564 | rects[i].x = rects[i].y = 0; // empty rect needs no space 565 | } else { 566 | stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); 567 | if (fr.prev_link) { 568 | rects[i].x = (stbrp_coord) fr.x; 569 | rects[i].y = (stbrp_coord) fr.y; 570 | } else { 571 | rects[i].x = rects[i].y = STBRP__MAXVAL; 572 | } 573 | } 574 | } 575 | 576 | // unsort 577 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); 578 | 579 | // set was_packed flags 580 | for (i=0; i < num_rects; ++i) 581 | rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); 582 | } 583 | #endif 584 | -------------------------------------------------------------------------------- /src/3rdparty/imgui/TODO.txt: -------------------------------------------------------------------------------- 1 | dear imgui 2 | ISSUES & TODO LIST 3 | 4 | Issue numbers (#) refer to github issues listed at https://github.com/ocornut/imgui/issues/XXXX 5 | The list below consist mostly of ideas noted down before they are requested/discussed by users (at which point they usually exist on the github issue tracker). 6 | It's mostly a bunch of personal notes, probably incomplete. Feel free to query if you have any questions. 7 | 8 | - doc/test: add a proper documentation+regression testing system (#435) 9 | - doc/test: checklist app to verify binding/integration of imgui (test inputs, rendering, callback, etc.). 10 | - project: folder or separate repository with maintained helpers (e.g. imgui_memory_editor.h, imgui_stl.h, maybe imgui_dock would be there?) 11 | 12 | - window: calling SetNextWindowSize() every frame with <= 0 doesn't do anything, may be useful to allow (particularly when used for a single axis). (#690) 13 | - window: add a way for very transient windows (non-saved, temporary overlay over hundreds of objects) to "clean" up from the global window list. perhaps a lightweight explicit cleanup pass. 14 | - window: auto-fit feedback loop when user relies on any dynamic layout (window width multiplier, column) appears weird to end-user. clarify. 15 | - window: allow resizing of child windows (possibly given min/max for each axis?.) 16 | - window: background options for child windows, border option (disable rounding). 17 | - window: resizing from any sides? + mouse cursor directives for app. (#822) 18 | !- window: begin with *p_open == false should return false. 19 | - window: get size/pos helpers given names (see discussion in #249) 20 | - window: a collapsed window can be stuck behind the main menu bar? 21 | - window: when window is very small, prioritize resize button over close button. 22 | - window: detect extra End() call that pop the "Debug" window out and assert at End() call site instead of at end of frame. 23 | - window/tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic. 24 | - window: increase minimum size of a window with menus or fix the menu rendering so that it doesn't look odd. 25 | - window: double-clicking on title bar to minimize isn't consistent, perhaps move to single-click on left-most collapse icon? 26 | - window: expose contents size. (#1045) 27 | - window: GetWindowSize() returns (0,0) when not calculated? (#1045) 28 | - window: freeze window flag: if not focused/hovered, return false, render with previous ImDrawList. and/or reduce refresh rate. 29 | !- scrolling: allow immediately effective change of scroll after Begin() if we haven't appended items yet. 30 | - scrolling/clipping: separator on the initial position of a window is not visible (cursorpos.y <= clippos.y). (2017-08-20: can't repro) 31 | 32 | - drawlist: move Font, FontSize, FontTexUvWhitePixel inside ImDrawList and make it self-contained (apart from drawing settings?) 33 | - drawlist: make it easier to toggle AA per primitive, so we can use e.g. non-AA fill + AA borders more naturally 34 | - drawlist: end-user probably can't call Clear() directly because we expect a texture to be pushed in the stack. 35 | - drawlist: maintaining bounding box per command would allow to merge draw command when clipping isn't relied on (typical non-scrolling window or non-overflowing column would merge with previous command). 36 | - drawlist: avoid passing null (-9999,+9999) rectangle to end-user, instead perhaps pass rectangle based on io.DisplaySize? 37 | - drawlist: primtiives/helpers to manipulate vertices post submission, so e.g. a quad/rect can be resized to fit later submitted content, _without_ using the ChannelSplit api 38 | 39 | - main: considering adding an Init() function? some constructs are awkward in the implementation because of the lack of them. 40 | - main: find a way to preserve relative orders of multiple reappearing windows (so an app toggling between "modes" e.g. fullscreen vs all tools) won't lose relative ordering. 41 | - main: IsItemHovered() make it more consistent for various type of widgets, widgets with multiple components, etc. also effectively IsHovered() region sometimes differs from hot region, e.g tree nodes 42 | - main: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode? 43 | - main: rename the main "Debug" window to avoid ID collision with user who may want to use "Debug" with specific flags. 44 | 45 | - widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc. (#395) 46 | - widgets: clean up widgets internal toward exposing everything and stabilizing imgui_internals.h. 47 | - widgets: add disabled and read-only modes (#211) 48 | - widgets: add always-allow-overlap mode. 49 | - widgets: alignment options in style (e.g. center Selectable, Right-Align within Button, etc.) #1260 50 | - widgets: activate by identifier (trigger button, focus given id) 51 | 52 | - input text: clean up the mess caused by converting UTF-8 <> wchar. the code is rather inefficient right now and super fragile. 53 | - input text: reorganize event handling, allow CharFilter to modify buffers, allow multiple events? (#541) 54 | - input text: expose CursorPos in char filter event (#816) 55 | - input text: access public fields via a non-callback API e.g. InputTextGetState("xxx") that may return NULL if not active. 56 | - input text: flag to disable live update of the user buffer (also applies to float/int text input) (#701) 57 | - input text: way to dynamically grow the buffer without forcing the user to initially allocate for worse case, e.g. more natural std::string (follow up on #200) 58 | - input text: hover tooltip could show unclamped text 59 | - input text: option to Tab after an Enter validation. 60 | - input text: add ImGuiInputTextFlags_EnterToApply? (off #218) 61 | - input text: easier ways to update buffer (from source char*) while owned. preserve some sort of cursor position for multi-line text. 62 | - input text: add discard flag (e.g. ImGuiInputTextFlags_DiscardActiveBuffer) or make it easier to clear active focus for text replacement during edition (#725) 63 | - input text: display bug when clicking a drag/slider after an input text in a different window has all-selected text (order dependant). actually a very old bug but no one appears to have noticed it. 64 | - input text multi-line: don't directly call AddText() which does an unnecessary vertex reserve for character count prior to clipping. and/or more line-based clipping to AddText(). and/or reorganize TextUnformatted/RenderText for more efficiency for large text (e.g TextUnformatted could clip and log separately, etc). 65 | - input text multi-line: line numbers? status bar? (follow up on #200) 66 | - input text multi-line: behave better when user changes input buffer while editing is active (even though it is illegal behavior). namely, the change of buffer can create a scrollbar glitch (#725) 67 | - input text multi-line: better horizontal scrolling support (#383, #1224) 68 | - input text: allow centering/positioning text so that ctrl+clicking Drag or Slider keeps the textual value at the same pixel position. 69 | - input number: optional range min/max for Input*() functions 70 | - input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled) 71 | - input number: use mouse wheel to step up/down 72 | - input number: applying arithmetics ops (+,-,*,/) messes up with text edit undo stack. 73 | 74 | - layout: helper or a way to express ImGui::SameLine(ImGui::GetCursorStartPos().x + ImGui::CalcItemWidth() + ImGui::GetStyle().ItemInnerSpacing.x); in a simpler manner. 75 | - layout: generalization of the above: a concept equivalent to word processor ruler tab stop ~ mini columns (position in X, no clipping implied) (vaguely relate to #267, #395, also what is used internally for menu items) 76 | - layout: horizontal layout helper (#97) 77 | - layout: horizontal flow until no space left (#404) 78 | - layout: more generic alignment state (left/right/centered) for single items? 79 | - layout: clean up the InputFloatN/SliderFloatN/ColorEdit4 layout code. item width should include frame padding. 80 | - layout: BeginGroup() needs a border option. 81 | - layout: vertical alignement of mixed height items (e.g. buttons) within a same line (#1284) 82 | 83 | - columns: sizing policy (e.g. for each column: fixed size, %, fill, distribute default size among fills) (#513, #125) 84 | - columns: add a conditional parameter to SetColumnOffset() (#513, #125) 85 | - columns: headers. reorderable. (#513, #125) 86 | - columns: optional sorting modifiers (up/down), sort list so sorting can be done multi-critera. notify user when sort order changed. 87 | - columns: option to alternate background colors on odd/even scanlines. 88 | - columns: allow columns to recurse. 89 | - columns: separator function or parameter that works within the column (currently Separator() bypass all columns) (#125) 90 | - columns: flag to add horizontal separator above/below? 91 | - columns/layout: setup minimum line height (equivalent of automatically calling AlignFirstTextHeightToWidgets) 92 | 93 | !- color: the color conversion helpers/types are a mess and needs sorting out. 94 | - color: (api breaking) ImGui::ColorConvertXXX functions should be loose ImColorConvertXX to match imgui_internals.h 95 | - coloredit: it is still somehow awkward to copy colors around (unless going through Hex mode). 96 | 97 | - plot: full featured plot/graph api w/ scrolling, zooming etc. all bell & whistle. why not! 98 | - plot: PlotLines() should use the polygon-stroke facilities, less verticles (currently issues with averaging normals) 99 | - plot: make it easier for user to draw extra stuff into the graph (e.g: draw basis, highlight certain points, 2d plots, multiple plots) 100 | - plot: "smooth" automatic scale over time, user give an input 0.0(full user scale) 1.0(full derived from value) 101 | - plot: option/feature: draw the zero line 102 | - plot: option/feature: draw grid, vertical markers 103 | - plot: option/feature: draw unit 104 | - plot: add a helper e.g. Plot(char* label, float value, float time_span=2.0f) that stores values and Plot them for you - probably another function name. and/or automatically allow to plot ANY displayed value (more reliance on stable ID) 105 | 106 | - clipper: ability to force display 1 item in the list would be convenient (for patterns where we need to set active id etc.) 107 | - clipper: ability to disable the clipping through a simple flag/bool. 108 | - clipper: ability to run without knowing full count in advance. 109 | 110 | - splitter/separator: formalize the splitter idiom into an official api (we want to handle n-way split) (#319) 111 | 112 | - dock: docking extension 113 | - dock: dock out from a collapsing header? would work nicely but need emitting window to keep submitting the code. 114 | 115 | - tabs: re-ordering, close buttons, context menu, persistent order (#261, #351) 116 | 117 | - ext: stl-ish friendly extension (imgui_stl.h) that has wrapped for std::string, std::vector etc. 118 | 119 | - button: provide a button that looks framed. 120 | - image/image button: misalignment on padded/bordered button? 121 | - image/image button: parameters are confusing, image() has tint_col,border_col whereas imagebutton() has bg_col/tint_col. Even thou they are different parameters ordering could be more consistent. can we fix that? 122 | - image button: not taking an explicit id is odd. 123 | - slider: allow using the [-]/[+] buttons used by InputFloat()/InputInt() 124 | - slider: initial absolute click is imprecise. change to relative movement slider (same as scrollbar). 125 | - slider: add dragging-based widgets to edit values with mouse (on 2 axises), saving screen real-estate. 126 | - slider: tint background based on value (e.g. v_min -> v_max, or use 0.0f either side of the sign) 127 | - slider: precision dragging 128 | - slider: step option (#1183) 129 | - knob: rotating knob widget (#942) 130 | - slider & drag: int data passing through a float 131 | - drag float: up/down axis 132 | - drag float: added leeway on edge (e.g. a few invisible steps past the clamp limits) 133 | 134 | - combo: sparse combo boxes (via function call?) / iterators 135 | - combo: active item type could be anything else e.g. void* 136 | - combo: use clipper 137 | - combo: contents should extends to fit label if combo widget is small 138 | - combo: option for ComboEx to not return true when unchanged (#1182) 139 | - combo/listbox: keyboard control. need InputText-like non-active focus + key handling. considering keyboard for custom listbox (pr #203) 140 | - listbox: multiple selection. 141 | - listbox: unselect option (#1208) 142 | - listbox: make it easier/more natural to implement range-select (need some sort of info/ref about the last clicked/focused item that user can translate to an index?) 143 | - listbox: user may want to initial scroll to focus on the one selected value? 144 | - listbox: expose hovered item for a basic ListBox 145 | - listbox: keyboard navigation. 146 | - listbox: scrolling should track modified selection. 147 | 148 | !- popups/menus: clarify usage of popups id, how MenuItem/Selectable closing parent popups affects the ID, etc. this is quite fishy needs improvement! (#331, #402) 149 | - popups: reopening context menu at new position should be the behavior by default? (equivalent to internal OpenPopupEx() with reopen_existing=true) 150 | - popups: if the popup functions took explicit ImGuiID it would allow the user to manage the scope of those ID. (#331) 151 | - popups: clicking outside (to close popup) and holding shouldn't drag window below. 152 | - popups: add variant using global identifier similar to Begin/End (#402) 153 | - popups: border options. richer api like BeginChild() perhaps? (#197) 154 | - tooltip: tooltip that doesn't fit in entire screen seems to lose their "last preferred direction" and may teleport when moving mouse. 155 | 156 | - menus: calling BeginMenu() twice with a same name doesn't append as Begin() does for regular windows (#1207) 157 | - statusbar: add a per-window status bar helper similar to what menubar does. 158 | - shortcuts: local-style shortcut api, e.g. parse "&Save" 159 | - shortcuts,menus: global-style shortcut api e.g. "Save (CTRL+S)" -> explicit flag for recursing into closed menu 160 | - shortcuts: programmatically access shortcuts "Focus("&Save")) 161 | - menus: menubars: main menu-bar could affect clamping of windows position (~ akin to modifying DisplayMin) 162 | 163 | - text: proper alignment options in imgui_internal.h 164 | - text wrapped: figure out better way to use TextWrapped() in an always auto-resize context (tooltip, etc.) (#249) 165 | - text: it's currently impossible to have a window title with "##". perhaps an official workaround would be nice. \ style inhibitor? non-visible ascii code to insert between #? 166 | 167 | - tree node / optimization: avoid formatting when clipped. 168 | - tree node: tree-node/header right-most side doesn't take account of horizontal scrolling. 169 | - tree node: add treenode/treepush int variants? not there because (void*) cast from int warns on some platforms/settings? 170 | - tree node: try to apply scrolling at time of TreePop() if node was just opened and end of node is past scrolling limits? 171 | - tree node / selectable render mismatch which is visible if you use them both next to each other (e.g. cf. property viewer) 172 | - tree node: tweak color scheme to distinguish headers from selected tree node (#581) 173 | 174 | !- settings: expose enough to save/load .ini from RAM instead of fopen 175 | - settings: write more decent code to allow saving/loading new fields: columns, selected tree nodes? 176 | - settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file (#437) 177 | - stb: add defines to disable stb implementations 178 | 179 | !- style: better default styles. 180 | !- style: move border to style structure, remove _ShowBorder flag. 181 | - style: border types: out-screen, in-screen, etc. (#447) 182 | - style/optimization: store rounded corners in texture to use 1 quad per corner (filled and wireframe) to lower the cost of rounding. 183 | - style: add window shadow (fading away from the window. Paint-style calculation of vertices alpha after drawlist would be easier) 184 | - style: color-box not always square? 185 | - style: a concept of "compact style" that the end-user can easily rely on (e.g. PushStyleCompact()?) that maps to other settings? avoid implementing duplicate helpers such as SmallCheckbox(), etc. 186 | - style: try to make PushStyleVar() more robust to incorrect parameters (to be more friendly to edit & continues situation). 187 | - style: global scale setting. 188 | - style: WindowPadding needs to be EVEN as the 0.5 multiplier used on this value probably have a subtle effect on clip rectangle 189 | - style: have a more global HSV setter (e.g. alter hue on all elements). consider replacing active/hovered by offset in HSV space? (#438, #707, #1223) 190 | - style: gradients fill (#1223) ~ 2 bg colors for each fill? tricky with rounded shapes and using textures for corners. 191 | - style editor: color child window height expressed in multiple of line height. 192 | 193 | - log: LogButtons() options for specifying depth and/or hiding depth slider 194 | - log: have more control over the log scope (e.g. stop logging when leaving current tree node scope) 195 | - log: be able to log anything (e.g. right-click on a window/tree-node, shows context menu? log into tty/file/clipboard) 196 | - log: let user copy any window content to clipboard easily (CTRL+C on windows? while moving it? context menu?). code is commented because it fails with multiple Begin/End pairs. 197 | 198 | - filters: set a current filter that tree node can automatically query to hide themselves 199 | - filters: handle wildcards (with implicit leading/trailing *), regexps 200 | - filters: fuzzy matches (may use code at blog.forrestthewoods.com/4cffeed33fdb) 201 | 202 | - drag'n drop, dragging helpers, demo (carry dragging info, visualize drag source before clicking, drop target, etc.) (#143, #479) 203 | - node/graph editor (#306) 204 | - pie menus patterns (#434) 205 | - markup: simple markup language for color change? 206 | 207 | !- font: better CalcTextSizeA() API, at least for simple use cases. current one is horrible (perhaps have simple vs extended versions). 208 | - font: enforce monospace through ImFontConfig (for icons?) 209 | - font: finish CustomRectRegister() to allow mapping unicode codepoint to custom texture data 210 | - font: PushFontSize API (#1018) 211 | - font/atlas: incremental updates 212 | - font/atlas: dynamic font atlas to avoid baking huge ranges into bitmap and make scaling easier. 213 | - font/atlas: allow user to submit its own primitive to be rectpacked, and allow to map them on a Unicode point. 214 | - font: MemoryTTF taking ownership confusing/not obvious, maybe default should be opposite? 215 | - font/text: vertical and/or rotated text renderer (#705) - vertical is easier clipping wise 216 | - font: imgui_freetype.h alternative renderer (#618) 217 | - font: optimization: for monospace font (like the default one) we can trim IndexXAdvance as long as trailing value is == FallbackXAdvance (need to make sure TAB is still correct). 218 | - font: add support for kerning, probably optional. A) perhaps default to (32..128)^2 matrix ~ 9K entries = 36KB, then hash for non-ascii?. B) or sparse lookup into per-char list? 219 | - font: add a simpler CalcTextSizeA() api? current one ok but not welcome if user needs to call it directly (without going through ImGui::CalcTextSize) 220 | - font: fix AddRemapChar() to work before font has been built. 221 | - font: (api breaking) removed "TTF" from symbol names. also because it now supports OTF. 222 | 223 | !- nav/keyboard: tooltip & combo boxes are messing up / not honoring keyboard tabbing. 224 | - nav: integrate navigation branch into master. (#787) 225 | - nav: integrate/design keyboard controls. 226 | - nav: once tab should go through most/all widgets (in submission order?) 227 | - nav: esc/enter default behavior for popups, e.g. be able to mark an "ok" or "cancel" button that would get triggered by those keys. 228 | - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622) 229 | - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame) 230 | - focus: unable to use SetKeyboardFocusHere() on clipped widgets. (#787) 231 | - inputs: rework IO system to be able to pass actual ordered/timestamped events. use an event queue? (~#335, #71) 232 | - inputs: allow to pass explicit double-clicks if that's the only thing the user's backend can get them. (e.g. for windows by the CS_DBLCLKS style). 233 | - inputs: support track pad style scrolling & slider edit. 234 | 235 | - misc: make the ImGuiCond values linear (non-power-of-two). internal storage for ImGuiWindow can use integers to combine into flags. 236 | - misc: provide a way to compile out the entire implementation while providing a dummy API (e.g. #define IMGUI_DUMMY_IMPL) 237 | - misc: provide HoveredTime and ActivatedTime to ease the creation of animations. 238 | - misc: fix for compilation settings where stdcall isn't the default (e.g. vectorcall) (#1230) 239 | - remote: make a system like RemoteImGui first-class citizen/project (#75) 240 | 241 | - demo: add vertical separator demo 242 | - demo: add a virtual scrolling example? 243 | - examples: directx9: save/restore device state more thoroughly. 244 | - examples: window minimize, maximize (#583) 245 | - examples: provide a zero-framerate/idle example. 246 | - examples: document WantCaptureKeyboard, WantCaptureMouse in example apps. (#446) 247 | - examples: glfw: could go idle when minimized? if (glfwGetWindowAttrib(window, GLFW_ICONIFIED)) { glfwWaitEvents(); continue; } // the problem is that DeltaTime will be super high on resume, perhaps provide a way to let impl know (#440) 248 | - optimization: replace vsnprintf with stb_printf? or enable the defines/infrastructure to allow it (#1038) 249 | - optimization: add clipping for multi-component widgets (SliderFloatX, ColorEditX, etc.). one problem is that nav branch can't easily clip parent group when there is a move request. 250 | - optimization: add a flag to disable most of rendering, for the case where the user expect to skip it (#335) 251 | - optimization: use another hash function than crc32, e.g. FNV1a 252 | - optimization/render: merge command-lists with same clip-rect into one even if they aren't sequential? (as long as in-between clip rectangle don't overlap)? 253 | - optimization: turn some the various stack vectors into statically-sized arrays 254 | -------------------------------------------------------------------------------- /src/3rdparty/imgui/README.md: -------------------------------------------------------------------------------- 1 | dear imgui, 2 | ===== 3 | [![Build Status](https://travis-ci.org/ocornut/imgui.svg?branch=master)](https://travis-ci.org/ocornut/imgui) 4 | [![Coverity Status](https://scan.coverity.com/projects/4720/badge.svg)](https://scan.coverity.com/projects/4720) 5 | 6 | (This library is free but needs your support to sustain its development. There are lots of desirable new features and maintenance to do. If you are an individual using dear imgui, please consider donating via Patreon or PayPal. If your company is using dear imgui, please consider financial support (e.g. sponsoring a few weeks/months of development). I can invoice for private support, custom development etc. E-mail: omarcornut at gmail.) 7 | 8 | Monthly donations via Patreon: 9 |
[![Patreon](https://cloud.githubusercontent.com/assets/8225057/5990484/70413560-a9ab-11e4-8942-1a63607c0b00.png)](http://www.patreon.com/imgui) 10 | 11 | One-off donations via PayPal: 12 |
[![PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5Q73FPZ9C526U) 13 | 14 | Dear ImGui is a bloat-free graphical user interface library for C++. It outputs optimized vertex buffers that you can render anytime in your 3D-pipeline enabled application. It is fast, portable, renderer agnostic and self-contained (no external dependencies). 15 | 16 | Dear ImGui is designed to enable fast iteration and empower programmers to create content creation tools and visualization/ debug tools (as opposed to UI for the average end-user). It favors simplicity and productivity toward this goal, and thus lacks certain features normally found in more high-level libraries. 17 | 18 | Dear ImGui is particularly suited to integration in realtime 3D applications, fullscreen applications, embedded applications, games, or any applications on consoles platforms where operating system features are non-standard. 19 | 20 | Dear ImGui is self-contained within a few files that you can easily copy and compile into your application/engine: 21 | 22 | - imgui.cpp 23 | - imgui.h 24 | - imgui_demo.cpp 25 | - imgui_draw.cpp 26 | - imgui_internal.h 27 | - imconfig.h (empty by default, user-editable) 28 | - stb_rect_pack.h 29 | - stb_textedit.h 30 | - stb_truetype.h 31 | 32 | No specific build process is required. You can add the .cpp files to your project or #include them from an existing file. 33 | 34 | Your code passes mouse/keyboard inputs and settings to Dear ImGui (see example applications for more details). After Dear ImGui is setup, you can use it like in this example: 35 | 36 | ![screenshot of sample code alongside its output with dear imgui](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/code_sample_01.png) 37 | 38 | Dear ImGui outputs vertex buffers and simple command-lists that you can render in your application. The number of draw calls and state changes is typically very small. Because it doesn't know or touch graphics state directly, you can call ImGui commands anywhere in your code (e.g. in the middle of a running algorithm, or in the middle of your own rendering process). Refer to the sample applications in the examples/ folder for instructions on how to integrate dear imgui with your existing codebase. 39 | 40 | _A common misunderstanding is to think that immediate mode gui == immediate mode rendering, which usually implies hammering your driver/GPU with a bunch of inefficient draw calls and state changes, as the gui functions are called by the user. This is NOT what Dear ImGui does. Dear ImGui outputs vertex buffers and a small list of draw calls batches. It never touches your GPU directly. The draw call batches are decently optimal and you can render them later, in your app or even remotely._ 41 | 42 | Dear ImGui allows you create elaborate tools as well as very short-lived ones. On the extreme side of short-liveness: using the Edit&Continue feature of modern compilers you can add a few widgets to tweaks variables while your application is running, and remove the code a minute later! Dear ImGui is not just for tweaking values. You can use it to trace a running algorithm by just emitting text commands. You can use it along with your own reflection data to browse your dataset live. You can use it to expose the internals of a subsystem in your engine, to create a logger, an inspection tool, a profiler, a debugger, an entire game making editor/framework, etc. 43 | 44 | Binaries/Demo 45 | ------------- 46 | 47 | You should be able to build the examples from sources (tested on Windows/Mac/Linux). If you don't, let me know! If you want to have a quick look at some Dear ImGui features, you can download Windows binaries of the demo app here: 48 | - [imgui-demo-binaries-20171013.zip](http://www.miracleworld.net/imgui/binaries/imgui-demo-binaries-20171013.zip) (Windows binaries, Dear ImGui 1.52 WIP built 2017/10/13, 5 executables) 49 | 50 | Bindings 51 | -------- 52 | 53 | Integrating Dear ImGui within your custom engine is a matter of wiring mouse/keyboard inputs and providing a render function that can bind a texture and render simple textured triangles. The examples/ folder is populated with applications doing just that. If you are an experienced programmer it should take you less than an hour to integrate Dear ImGui in your custom engine, but make sure to spend time reading the FAQ, the comments and other documentation! 54 | 55 | _NB: those third-party bindings may be more or less maintained, more or less close to the spirit of original API and therefore I cannot give much guarantee about them. People who create language bindings sometimes haven't used the C++ API themselves (for the good reason that they aren't C++ users). Dear ImGui was designed with C++ in mind and some of the subtleties may be lost in translation with other languages. If your language supports it, I would suggest replicating the function overloading and default parameters used in the original, else the API may be harder to use. In doubt, please check the original C++ version first!_ 56 | 57 | Languages: 58 | - C (cimgui): thin c-api wrapper for ImGui https://github.com/Extrawurst/cimgui 59 | - C#/.Net (ImGui.NET): An ImGui wrapper for .NET Core https://github.com/mellinoe/ImGui.NET 60 | - D (DerelictImgui): Dynamic bindings for the D programming language: https://github.com/Extrawurst/DerelictImgui 61 | - Go (go-imgui): https://github.com/Armored-Dragon/go-imgui 62 | - Lua: https://github.com/patrickriordan/imgui_lua_bindings 63 | - Pascal (imgui-pas) https://github.com/dpethes/imgui-pas 64 | - Python (CyImGui): Python bindings for dear imgui using Cython: https://github.com/chromy/cyimgui 65 | - Python (pyimgui): Another Python bindings for dear imgui: https://github.com/swistakm/pyimgui 66 | - Rust (imgui-rs): Rust bindings for dear imgui https://github.com/Gekkio/imgui-rs 67 | 68 | Frameworks: 69 | - Main ImGui repository include examples for DirectX9, DirectX10, DirectX11, OpenGL2/3, Vulkan, Allegro 5, SDL+GL2/3, iOS and Marmalade: https://github.com/ocornut/imgui/tree/master/examples 70 | - Unmerged PR: DirectX12: https://github.com/ocornut/imgui/pull/301 71 | - Unmerged PR: SDL2 + OpenGLES + Emscripten: https://github.com/ocornut/imgui/pull/336 72 | - Unmerged PR: FreeGlut + OpenGL2: https://github.com/ocornut/imgui/pull/801 73 | - Unmerged PR: Native Win32 and OSX: https://github.com/ocornut/imgui/pull/281 74 | - Unmerged PR: Android: https://github.com/ocornut/imgui/pull/421 75 | - Cinder: https://github.com/simongeilfus/Cinder-ImGui 76 | - cocos2d-x: https://github.com/c0i/imguix https://github.com/ocornut/imgui/issues/551 77 | - Flexium/SFML (FlexGUI): https://github.com/DXsmiley/FlexGUI 78 | - Irrlicht (IrrIMGUI): https://github.com/ZahlGraf/IrrIMGUI 79 | - Ogre: https://bitbucket.org/LMCrashy/ogreimgui/src 80 | - openFrameworks (ofxImGui): https://github.com/jvcleave/ofxImGui 81 | - LÖVE: https://github.com/slages/love-imgui 82 | - NanoRT (software raytraced) https://github.com/syoyo/imgui/tree/nanort/examples/raytrace_example 83 | - Unreal Engine 4: https://github.com/segross/UnrealImGui or https://github.com/sronsse/UnrealEngine_ImGui 84 | - SFML: https://github.com/EliasD/imgui-sfml or https://github.com/Mischa-Alff/imgui-backends 85 | 86 | For other bindings: see [this page](https://github.com/ocornut/imgui/wiki/Links/). 87 | Please contact me with the Issues tracker or Twitter to fix/update this list. 88 | 89 | Gallery 90 | ------- 91 | 92 | See the [Screenshots Thread](https://github.com/ocornut/imgui/issues/123) for some user creations. 93 | Also see the [Mega screenshots](https://github.com/ocornut/imgui/issues/1273) for an idea of the available features. 94 | 95 | ![screenshot 1](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v148/examples_01.png) 96 | [![screenshot game](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v149/gallery_TheDragonsTrap-01-thumb.jpg)](https://cloud.githubusercontent.com/assets/8225057/20628927/33e14cac-b329-11e6-80f6-9524e93b048a.png) 97 | ![screenshot 2](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v148/examples_02.png) 98 | 99 | [![screenshot profiler](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v148/profiler-880.jpg)](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v148/profiler.png) 100 | 101 | ![screenshot picker](https://user-images.githubusercontent.com/8225057/29062188-471e95ba-7c53-11e7-9618-c4484c0b75fe.PNG) 102 | 103 | ![screenshot 5](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v151/menus.png) 104 | ![screenshot 6](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v143/skinning_sample_02.png) 105 | ![screenshot 7](https://cloud.githubusercontent.com/assets/8225057/7903336/96f0fb7c-07d0-11e5-95d6-41c6a1595e5a.png) 106 | 107 | Dear ImGui can load TTF/OTF fonts. UTF-8 is supported for text display and input. Here using Arial Unicode font to display Japanese. Initialize custom font with: 108 | ``` 109 | ImGuiIO& io = ImGui::GetIO(); 110 | io.Fonts->AddFontFromFileTTF("ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); 111 | 112 | // For Microsoft IME, pass your HWND to enable IME positioning: 113 | io.ImeWindowHandle = my_hwnd; 114 | ``` 115 | ![Japanese screenshot](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/code_sample_01_jp.png) 116 | 117 | References 118 | ---------- 119 | 120 | The Immediate Mode GUI paradigm may at first appear unusual to some users. This is mainly because "Retained Mode" GUIs have been so widespread and predominant. The following links can give you a better understanding about how Immediate Mode GUIs works. 121 | - [Johannes 'johno' Norneby's article](http://www.johno.se/book/imgui.html). 122 | - [A presentation by Rickard Gustafsson and Johannes Algelind](http://www.cse.chalmers.se/edu/year/2011/course/TDA361/Advanced%20Computer%20Graphics/IMGUI.pdf). 123 | - [Jari Komppa's tutorial on building an ImGui library](http://iki.fi/sol/imgui/). 124 | - [Casey Muratori's original video that popularized the concept](https://mollyrocket.com/861). 125 | - [Nicolas Guillemot's CppCon'16 flashtalk about Dear ImGui](https://www.youtube.com/watch?v=LSRJ1jZq90k). 126 | - [Thierry Excoffier's Zero Memory Widget](http://perso.univ-lyon1.fr/thierry.excoffier/ZMW/). 127 | 128 | See the [Links page](https://github.com/ocornut/imgui/wiki/Links) for third-party bindings to different languages and frameworks. 129 | 130 | Frequently Asked Question (FAQ) 131 | ------------------------------- 132 | 133 | Where is the documentation? 134 | 135 | - The documentation is at the top of imgui.cpp + effectively imgui.h. 136 | - Example code is in imgui_demo.cpp and particularly the ImGui::ShowTestWindow() function. It covers most features of ImGui so you can read the code and call the function itself to see its output. 137 | - Standalone example applications using e.g. OpenGL/DirectX are provided in the examples/ folder. 138 | - We obviously needs better documentation! Consider contributing or becoming a [Patron](http://www.patreon.com/imgui) to promote this effort. 139 | 140 | Which version should I get? 141 | 142 | I occasionally tag [Releases](https://github.com/ocornut/imgui/releases) but it is generally safe and recommended to sync to master. The library is fairly stable and regressions tend to be fixed fast when reported. You may also want to checkout the [navigation branch](https://github.com/ocornut/imgui/tree/navigation) if you want to use Dear ImGui with a gamepad (it is also possible to map keyboard inputs to some degree). The Navigation branch is being kept up to date with Master. 143 | 144 | Why the odd dual naming, "dear imgui" vs "ImGui"? 145 | 146 | The library started its life and is best known as "ImGui" only due to the fact that I didn't give it a proper name when I released it. However, the term IMGUI (immediate-mode graphical user interface) was coined before and is being used in variety of other situations. It seemed confusing and unfair to hog the name. To reduce the ambiguity without affecting existing codebases, I have decided on an alternate, longer name "dear imgui" that people can use to refer to this specific library in ambiguous situations. 147 | 148 | What is ImTextureID and how do I display an image? 149 |
I integrated Dear ImGui in my engine and the text or lines are blurry.. 150 |
I integrated Dear ImGui in my engine and some elements are disappearing when I move windows around.. 151 |
How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on labels/IDs. 152 |
How can I tell when Dear ImGui wants my mouse/keyboard inputs VS when I can pass them to my application? 153 |
How can I load a different font than the default? 154 |
How can I easily use icons in my application? 155 |
How can I load multiple fonts? 156 |
How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic? 157 |
How can I preserve my Dear ImGui context across reloading a DLL? (loss of the global/static variables) 158 |
How can I use the drawing facilities without an Dear ImGui window? (using ImDrawList API) 159 | 160 | See the FAQ in imgui.cpp for answers. 161 | 162 | How do you use Dear ImGui on a platform that may not have a mouse or keyboard? 163 | 164 | I recommend using [Synergy](http://synergy-project.org) ([sources](https://github.com/symless/synergy)). In particular, the _src/micro/uSynergy.c_ file contains a small client that you can use on any platform to connect to your host PC. You can seamlessly use your PC input devices from a video game console or a tablet. Dear ImGui allows to increase the hit box of widgets (via the _style.TouchPadding_ setting) to accommodate a little for the lack of precision of touch inputs, but it is recommended you use a mouse to allow optimising for screen real-estate. You can also checkout the beta [navigation branch](https://github.com/ocornut/imgui/tree/navigation) which provides support for using Dear ImGui with a game controller. 165 | 166 | Can you create elaborate/serious tools with Dear ImGui? 167 | 168 | Yes. People have written game editors, data browsers, debuggers, profilers and all sort of non-trivial tools with the library. In my experience the simplicity of the API is very empowering. Your UI runs close to your live data. Make the tools always-on and everybody in the team will be inclined to create new tools (as opposed to more "offline" UI toolkits where only a fraction of your team effectively creates tools). 169 | 170 | Dear ImGui is very programmer centric and the immediate-mode GUI paradigm might requires you to readjust some habits before you can realize its full potential. Dear ImGui is about making things that are simple, efficient and powerful. 171 | 172 | Is Dear ImGui fast? 173 | 174 | Probably fast enough for most uses. Down to the foundation of its visual design, Dear ImGui is engineered to be fairly performant both in term of CPU and GPU usage. Running elaborate code and creating elaborate UI will of course have a cost but Dear ImGui aims to minimize it. 175 | 176 | Mileage may vary but the following screenshot can give you a rough idea of the cost of running and rendering UI code (In the case of a trivial demo application like this one, your driver/os setup are likely to be the bottleneck. Testing performance as part of a real application is recommended). 177 | 178 | ![performance screenshot](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v138/performance_01.png) 179 | 180 | This is showing framerate for the full application loop on my 2011 iMac running Windows 7, OpenGL, AMD Radeon HD 6700M with an optimized executable. In contrast, librairies featuring higher-quality rendering and layouting techniques may have a higher resources footprint. 181 | 182 | If you intend to display large lists of items (say, 1000+) it can be beneficial for your code to perform clipping manually - one way is using helpers such as ImGuiListClipper - in order to avoid submitting them to Dear ImGui in the first place. Even though ImGui will discard your clipped items it still needs to calculate their size and that overhead will add up if you have thousands of items. If you can handle clipping and height positionning yourself then browsing a list with millions of items isn't a problem. 183 | 184 | Can you reskin the look of Dear ImGui? 185 | 186 | You can alter the look of the interface to some degree: changing colors, sizes, padding, rounding, fonts. However, as Dear ImGui is designed and optimised to create debug tools, the amount of skinning you can apply is limited. There is only so much you can stray away from the default look and feel of the interface. Below is a screenshot from [LumixEngine](https://github.com/nem0/LumixEngine) with custom colors + a docking/tabs extension (both of which you can find in the Issues section and will eventually be merged): 187 | 188 | ![LumixEngine](https://raw.githubusercontent.com/wiki/ocornut/imgui/web/v151/lumix-201710-rearranged.png) 189 | 190 | Why using C++ (as opposed to C)? 191 | 192 | Dear ImGui takes advantage of a few C++ languages features for convenience but nothing anywhere Boost-insanity/quagmire. Dear ImGui does NOT require C++11 so it can be used with most old C++ compilers. Dear ImGui doesn't use any C++ header file. Language-wise, function overloading and default parameters are used to make the API easier to use and code more terse. Doing so I believe the API is sitting on a sweet spot and giving up on those features would make the API more cumbersome. Other features such as namespace, constructors and templates (in the case of the ImVector<> class) are also relied on as a convenience. 193 | 194 | There is an reasonably maintained [c-api for ImGui](https://github.com/Extrawurst/cimgui) by Stephan Dilly designed for binding in other languages. I would suggest using your target language functionalities to try replicating the function overloading and default parameters used in C++ else the API may be harder to use. Also see [Links](https://github.com/ocornut/imgui/wiki/Links) for third-party bindings to other languages. 195 | 196 | Support dear imgui 197 | ------------------ 198 | 199 | How can I help financing further development of Dear ImGui? 200 | 201 | Your contributions are keeping the library alive. If you are an individual using dear imgui, please consider donating to enable me to spend more time improving the library. 202 | 203 | Monthly donations via Patreon: 204 |
[![Patreon](https://cloud.githubusercontent.com/assets/8225057/5990484/70413560-a9ab-11e4-8942-1a63607c0b00.png)](http://www.patreon.com/imgui) 205 | 206 | One-off donations via PayPal: 207 |
[![PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5Q73FPZ9C526U) 208 | 209 | If your company uses dear imgui, please consider financial support (e.g. sponsoring a few weeks/months of development). I can invoice for private support, custom development etc. E-mail: omarcornut at gmail. Thanks! 210 | 211 | Credits 212 | ------- 213 | 214 | Developed by [Omar Cornut](http://www.miracleworld.net) and every direct or indirect contributors to the GitHub. The early version of this library was developed with the support of [Media Molecule](http://www.mediamolecule.com) and first used internally on the game [Tearaway](http://tearaway.mediamolecule.com). 215 | 216 | I first discovered imgui principles at [Q-Games](http://www.q-games.com) where Atman had dropped his own simple imgui implementation in the codebase, which I spent quite some time improving and thinking about. It turned out that Atman was exposed to the concept directly by working with Casey. When I moved to Media Molecule I rewrote a new library trying to overcome the flaws and limitations of the first one I've worked with. It became this library and since then I have spent an unreasonable amount of time iterating on it. 217 | 218 | Embeds [ProggyClean.ttf](http://upperbounds.net) font by Tristan Grimmer (MIT license). 219 | 220 | Embeds [stb_textedit.h, stb_truetype.h, stb_rectpack.h](https://github.com/nothings/stb/) by Sean Barrett (public domain). 221 | 222 | Inspiration, feedback, and testing for early versions: Casey Muratori, Atman Binstock, Mikko Mononen, Emmanuel Briney, Stefan Kamoda, Anton Mikhailov, Matt Willis. And everybody posting feedback, questions and patches on the GitHub. 223 | 224 | Ongoing dear imgui development is financially supported on [**Patreon**](http://www.patreon.com/imgui). 225 | 226 | Double-chocolate sponsors: 227 | - Media Molecule 228 | - Mobigame 229 | - Insomniac Games (sponsored the gamepad/keyboard navigation branch) 230 | - Aras Pranckevičius 231 | - Lizardcube 232 | - Greggman 233 | 234 | Salty caramel supporters: 235 | - Jetha Chan, Wild Sheep Studio, Pastagames, Mārtiņš Možeiko, Daniel Collin, Recognition Robotics, Chris Genova, ikrima, Glenn Fiedler, Geoffrey Evans, Dakko Dakko, Mercury Labs, Singularity Demo Group, Mischa Alff, Sebastien Ronsse. 236 | 237 | Caramel supporters: 238 | - Michel Courtine, César Leblic, Dale Kim, Alex Evans, Rui Figueira, Paul Patrashcu, Jerome Lanquetot, Ctrl Alt Ninja, Paul Fleming, Neil Henning, Stephan Dilly, Neil Blakey-Milner, Aleksei, NeiloGD, Justin Paver, FiniteSol, Vincent Pancaldi, James Billot, Robin Hübner, furrtek, Eric, Simon Barratt, Game Atelier, Julian Bosch, Simon Lundmark, Vincent Hamm, Farhan Wali, Jeff Roberts, Matt Reyer, Colin Riley, Victor Martins, Josh Simmons, Garrett Hoofman, Sergio Gonzales, Andrew Berridge, Roy Eltham, Game Preservation Society, Kit framework, Josh Faust, Martin Donlon, Quinton, Felix, Andrew Belt, Codecat, Cort Stratton, Claudio Canepa, Doug McNabb, Emmanuel Julien, Guillaume Chereau, Jeffrey Slutter, Jeremiah Deckard, r-lyeh, Roger Clark, Nekith, Joshua Fisher, Malte Hoffmann, Mustafa Karaalioglu, Merlyn Morgan-Graham, Per Vognsen, Fabian Giesen, Jan Staubach, Matt Hargett, John Shearer, Jesse Chounard, kingcoopa, Miloš Tošić. 239 | 240 | And other supporters; thanks! 241 | (Please contact me or PR if you would like to be added or removed from this list) 242 | 243 | License 244 | ------- 245 | 246 | Dear ImGui is licensed under the MIT License, see LICENSE for more information. 247 | -------------------------------------------------------------------------------- /src/imgui-qt3d/imguimanager.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #include "imguimanager.h" 52 | #include 53 | 54 | #include 55 | #include 56 | #include 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | #include 79 | #include 80 | #include 81 | #include 82 | #include 83 | #include 84 | 85 | class ImguiTextureImageDataGen : public Qt3DRender::QTextureImageDataGenerator 86 | { 87 | public: 88 | ImguiTextureImageDataGen(const QImage &image) : m_image(image) { } 89 | 90 | Qt3DRender::QTextureImageDataPtr operator()() override 91 | { 92 | Qt3DRender::QTextureImageDataPtr textureData = Qt3DRender::QTextureImageDataPtr::create(); 93 | textureData->setImage(m_image); 94 | return textureData; 95 | } 96 | bool operator==(const Qt3DRender::QTextureImageDataGenerator &other) const override 97 | { 98 | const ImguiTextureImageDataGen *otherFunctor = functor_cast(&other); 99 | return otherFunctor && otherFunctor->m_image == m_image; 100 | } 101 | 102 | QT3D_FUNCTOR(ImguiTextureImageDataGen) 103 | 104 | private: 105 | QImage m_image; 106 | }; 107 | 108 | class TextureImage : public Qt3DRender::QAbstractTextureImage 109 | { 110 | public: 111 | TextureImage(const QImage &image) { m_gen = QSharedPointer::create(image); } 112 | 113 | private: 114 | Qt3DRender::QTextureImageDataGeneratorPtr dataGenerator() const override { return m_gen; } 115 | 116 | Qt3DRender::QTextureImageDataGeneratorPtr m_gen; 117 | }; 118 | 119 | void ImguiManager::initialize(Qt3DCore::QEntity *rootEntity) 120 | { 121 | m_rootEntity = rootEntity; 122 | 123 | Qt3DLogic::QFrameAction *frameUpdater = new Qt3DLogic::QFrameAction; 124 | QObject::connect(frameUpdater, &Qt3DLogic::QFrameAction::triggered, [this]() { 125 | if (!m_enabled || !m_frame || !m_outputInfoFunc) 126 | return; 127 | 128 | m_outputInfo = m_outputInfoFunc(); 129 | 130 | ImGuiIO &io = ImGui::GetIO(); 131 | io.DisplaySize.x = m_outputInfo.size.width() * m_outputInfo.dpr; 132 | io.DisplaySize.y = m_outputInfo.size.height() * m_outputInfo.dpr; 133 | 134 | updateInput(); 135 | 136 | // One weakness regarding input is that io.WantCaptureMouse/Keyboard 137 | // cannot really be supported. We could check for them after the 138 | // NewFrame() call below, but there is nothing we can do at this stage, 139 | // the Qt events have already been dispatched and processed. 140 | 141 | ImGui::NewFrame(); 142 | m_frame(m_rootEntity); 143 | ImGui::Render(); 144 | 145 | update3D(); 146 | }); 147 | 148 | m_rootEntity->addComponent(frameUpdater); 149 | 150 | ImGuiIO &io = ImGui::GetIO(); 151 | unsigned char *pixels; 152 | int w, h; 153 | io.Fonts->GetTexDataAsRGBA32(&pixels, &w, &h); 154 | 155 | m_atlasTex = new Qt3DRender::QTexture2D; 156 | m_atlasTex->setFormat(Qt3DRender::QAbstractTexture::RGBA8_UNorm); 157 | m_atlasTex->setWidth(w); 158 | m_atlasTex->setHeight(h); 159 | m_atlasTex->setMinificationFilter(Qt3DRender::QAbstractTexture::Linear); 160 | m_atlasTex->setMagnificationFilter(Qt3DRender::QAbstractTexture::Linear); 161 | 162 | QImage wrapperImg((const uchar *) pixels, w, h, QImage::Format_RGBA8888); 163 | m_atlasTex->addTextureImage(new TextureImage(wrapperImg)); 164 | 165 | io.Fonts->SetTexID(m_atlasTex); 166 | } 167 | 168 | // To be called when the Qt 3D scene goes down (and thus destroys the objects 169 | // ImguiManager references) but the ImguiManager instance will be reused later 170 | // on. Must be followed by a call to initialize(). 171 | void ImguiManager::releaseResources() 172 | { 173 | // assume that everything starting from our root entity is already gone 174 | rpd = SharedRenderPassData(); 175 | m_cmdList.clear(); 176 | } 177 | 178 | void ImguiManager::resizePool(CmdListEntry *e, int newSize) 179 | { 180 | Q_ASSERT(m_outputInfo.guiTag && m_outputInfo.activeGuiTag); 181 | 182 | const int oldSize = e->cmds.count(); 183 | if (newSize > oldSize) { 184 | e->cmds.resize(newSize); 185 | for (int i = oldSize; i < newSize; ++i) { 186 | CmdEntry *ecmd = &e->cmds[i]; 187 | Qt3DCore::QEntity *entity = new Qt3DCore::QEntity(m_rootEntity); 188 | entity->addComponent(m_outputInfo.guiTag); 189 | Qt3DRender::QMaterial *material = buildMaterial(&ecmd->scissor); 190 | entity->addComponent(material); 191 | Qt3DRender::QGeometryRenderer *geomRenderer = new Qt3DRender::QGeometryRenderer; 192 | entity->addComponent(geomRenderer); 193 | Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; 194 | entity->addComponent(transform); 195 | Qt3DRender::QParameter *texParam = new Qt3DRender::QParameter; 196 | texParam->setName(QLatin1String("tex")); 197 | texParam->setValue(QVariant::fromValue(m_atlasTex)); // needs a valid initial value 198 | material->addParameter(texParam); 199 | ecmd->entity = entity; 200 | ecmd->material = material; 201 | ecmd->geomRenderer = geomRenderer; 202 | ecmd->transform = transform; 203 | ecmd->texParam = texParam; 204 | } 205 | } 206 | 207 | // make sure only entities from the first newSize entries are tagged as active gui 208 | if (e->activeSize > newSize) { 209 | for (int i = newSize; i < e->activeSize; ++i) { 210 | e->cmds[i].entity->removeComponent(m_outputInfo.activeGuiTag); 211 | updateGeometry(e, i, 0, 0, 0, nullptr); 212 | } 213 | } else if (e->activeSize < newSize) { 214 | for (int i = e->activeSize; i < newSize; ++i) 215 | e->cmds[i].entity->addComponent(m_outputInfo.activeGuiTag); 216 | // up to the caller to do updateGeometry for [0..newSize-1] 217 | } 218 | 219 | e->activeSize = newSize; 220 | } 221 | 222 | void ImguiManager::updateGeometry(CmdListEntry *e, int idx, uint elemCount, int vertexCount, int indexCount, const void *indexOffset) 223 | { 224 | Qt3DRender::QGeometryRenderer *gr = e->cmds[idx].geomRenderer; 225 | Qt3DRender::QGeometry *g = gr->geometry(); 226 | 227 | if (!g) { 228 | gr->setPrimitiveType(Qt3DRender::QGeometryRenderer::Triangles); 229 | g = new Qt3DRender::QGeometry; 230 | 231 | for (int i = 0; i < 4; ++i) 232 | g->addAttribute(new Qt3DRender::QAttribute); 233 | 234 | const int vsize = 3; // assumes ImDrawVert was overridden in imconfig.h 235 | Q_ASSERT(sizeof(ImDrawVert) == (vsize + 2) * sizeof(float) + sizeof(ImU32)); 236 | 237 | const QVector attrs = g->attributes(); 238 | Qt3DRender::QAttribute *attr = attrs[0]; 239 | attr->setBuffer(e->vbuf); 240 | attr->setName(Qt3DRender::QAttribute::defaultPositionAttributeName()); 241 | attr->setVertexBaseType(Qt3DRender::QAttribute::Float); 242 | attr->setVertexSize(vsize); 243 | attr->setCount(vertexCount); 244 | attr->setByteOffset(0); 245 | attr->setByteStride(sizeof(ImDrawVert)); 246 | 247 | attr = attrs[1]; 248 | attr->setBuffer(e->vbuf); 249 | attr->setName(Qt3DRender::QAttribute::defaultTextureCoordinateAttributeName()); 250 | attr->setVertexBaseType(Qt3DRender::QAttribute::Float); 251 | attr->setVertexSize(2); 252 | attr->setCount(vertexCount); 253 | attr->setByteOffset(vsize * sizeof(float)); 254 | attr->setByteStride(sizeof(ImDrawVert)); 255 | 256 | attr = attrs[2]; 257 | attr->setBuffer(e->vbuf); 258 | attr->setName(Qt3DRender::QAttribute::defaultColorAttributeName()); 259 | attr->setVertexBaseType(Qt3DRender::QAttribute::UnsignedByte); 260 | attr->setVertexSize(4); 261 | attr->setCount(vertexCount); 262 | attr->setByteOffset((vsize + 2) * sizeof(float)); 263 | attr->setByteStride(sizeof(ImDrawVert)); 264 | 265 | attr = attrs[3]; 266 | attr->setBuffer(e->ibuf); 267 | attr->setAttributeType(Qt3DRender::QAttribute::IndexAttribute); 268 | attr->setVertexBaseType(sizeof(ImDrawIdx) == 2 ? Qt3DRender::QAttribute::UnsignedShort : Qt3DRender::QAttribute::UnsignedInt); 269 | attr->setVertexSize(1); 270 | attr->setCount(indexCount); 271 | attr->setByteOffset((quintptr) indexOffset); 272 | attr->setByteStride(0); 273 | 274 | gr->setGeometry(g); 275 | } else { 276 | // only update the potentially changing properties afterwards 277 | const QVector attrs = g->attributes(); 278 | Q_ASSERT(attrs.count() == 4); 279 | 280 | Qt3DRender::QAttribute *attr = attrs[0]; 281 | attr->setBuffer(e->vbuf); 282 | attr->setCount(vertexCount); 283 | 284 | attr = attrs[1]; 285 | attr->setBuffer(e->vbuf); 286 | attr->setCount(vertexCount); 287 | 288 | attr = attrs[2]; 289 | attr->setBuffer(e->vbuf); 290 | attr->setCount(vertexCount); 291 | 292 | attr = attrs[3]; 293 | attr->setBuffer(e->ibuf); 294 | attr->setCount(indexCount); 295 | attr->setByteOffset((quintptr) indexOffset); 296 | } 297 | 298 | gr->setVertexCount(elemCount); 299 | } 300 | 301 | // called once per frame, performs gui-related updates for the scene 302 | void ImguiManager::update3D() 303 | { 304 | ImDrawData *d = ImGui::GetDrawData(); 305 | ImGuiIO &io = ImGui::GetIO(); 306 | 307 | if (m_cmdList.count() < d->CmdListsCount) { 308 | m_cmdList.resize(d->CmdListsCount); 309 | } else { 310 | // Make sure all unused entities are untagged. 311 | for (int n = d->CmdListsCount; n < m_cmdList.count(); ++n) 312 | resizePool(&m_cmdList[n], 0); 313 | } 314 | 315 | d->ScaleClipRects(ImVec2(m_scale, m_scale)); 316 | 317 | // CmdLists is in back-to-front order, assign z values accordingly 318 | const float zstep = 0.01f; 319 | float z = -1000; 320 | 321 | for (int n = 0; n < d->CmdListsCount; ++n) { 322 | const ImDrawList *cmdList = d->CmdLists[n]; 323 | CmdListEntry *e = &m_cmdList[n]; 324 | 325 | if (!e->vbuf) { 326 | e->vbuf = new Qt3DRender::QBuffer; 327 | e->vbuf->setUsage(Qt3DRender::QBuffer::StreamDraw); 328 | } 329 | // NB! must make a copy in any case, fromRawData would be wrong here 330 | // even if we did not need to insert the dummy z values. 331 | QByteArray vdata((const char *) cmdList->VtxBuffer.Data, cmdList->VtxBuffer.Size * sizeof(ImDrawVert)); 332 | // Initialize Z values. The shader does not need it but some Qt3D stuff (back-to-front sorting, bounding volumes) does. 333 | { 334 | ImDrawVert *v = (ImDrawVert *) vdata.data(); 335 | uint idxOffset = 0; 336 | // Assign a Z value per draw call, not per draw call list. 337 | // This assumes no vertices are shared between the draw calls. 338 | for (int i = 0; i < cmdList->CmdBuffer.Size; ++i) { 339 | const ImDrawCmd *cmd = &cmdList->CmdBuffer[i]; 340 | if (!cmd->UserCallback) { 341 | for (uint ei = 0; ei < cmd->ElemCount; ++ei) { 342 | ImDrawIdx idx = cmdList->IdxBuffer.Data[idxOffset + ei]; 343 | v[idx].z = z; 344 | } 345 | } 346 | idxOffset += cmd->ElemCount; 347 | z += zstep; 348 | } 349 | } 350 | e->vbuf->setData(vdata); 351 | 352 | if (!e->ibuf) { 353 | e->ibuf = new Qt3DRender::QBuffer; 354 | e->ibuf->setUsage(Qt3DRender::QBuffer::StreamDraw); 355 | } 356 | // same here 357 | e->ibuf->setData(QByteArray((const char *) cmdList->IdxBuffer.Data, cmdList->IdxBuffer.Size * sizeof(ImDrawIdx))); 358 | 359 | // Ensure the needed number of entities and components are available; tag/untag as necessary 360 | resizePool(e, cmdList->CmdBuffer.Size); 361 | 362 | const ImDrawIdx *indexBufOffset = nullptr; 363 | for (int i = 0; i < cmdList->CmdBuffer.Size; ++i) { 364 | const ImDrawCmd *cmd = &cmdList->CmdBuffer[i]; 365 | CmdEntry *ecmd = &e->cmds[i]; 366 | if (!cmd->UserCallback) { 367 | updateGeometry(e, i, cmd->ElemCount, cmdList->VtxBuffer.Size, cmdList->IdxBuffer.Size, indexBufOffset); 368 | ecmd->texParam->setValue(QVariant::fromValue(static_cast(cmd->TextureId))); 369 | 370 | Qt3DRender::QScissorTest *scissor = ecmd->scissor; 371 | scissor->setLeft(cmd->ClipRect.x); 372 | scissor->setBottom(io.DisplaySize.y - cmd->ClipRect.w); 373 | scissor->setWidth(cmd->ClipRect.z - cmd->ClipRect.x); 374 | scissor->setHeight(cmd->ClipRect.w - cmd->ClipRect.y); 375 | 376 | Qt3DCore::QTransform *transform = ecmd->transform; 377 | transform->setScale(m_scale); 378 | } else { 379 | cmd->UserCallback(cmdList, cmd); 380 | } 381 | indexBufOffset += cmd->ElemCount; 382 | } 383 | } 384 | } 385 | 386 | static const char *vertSrcES2 = 387 | "attribute vec4 vertexPosition;\n" 388 | "attribute vec4 vertexColor;\n" 389 | "attribute vec2 vertexTexCoord;\n" 390 | "varying vec2 uv;\n" 391 | "varying vec4 color;\n" 392 | "uniform mat4 projectionMatrix;\n" 393 | "uniform mat4 modelMatrix;\n" 394 | "void main() {\n" 395 | " uv = vertexTexCoord;\n" 396 | " color = vertexColor;\n" 397 | " gl_Position = projectionMatrix * modelMatrix * vec4(vertexPosition.xy, 0.0, 1.0);\n" 398 | "}\n"; 399 | 400 | static const char *fragSrcES2 = 401 | "uniform sampler2D tex;\n" 402 | "varying highp vec2 uv;\n" 403 | "varying highp vec4 color;\n" 404 | "void main() {\n" 405 | " gl_FragColor = color * texture2D(tex, uv);\n" 406 | "}\n"; 407 | 408 | static const char *vertSrcGL3 = 409 | "#version 150\n" 410 | "in vec4 vertexPosition;\n" 411 | "in vec4 vertexColor;\n" 412 | "in vec2 vertexTexCoord;\n" 413 | "out vec2 uv;\n" 414 | "out vec4 color;\n" 415 | "uniform mat4 projectionMatrix;\n" 416 | "uniform mat4 modelMatrix;\n" 417 | "void main() {\n" 418 | " uv = vertexTexCoord;\n" 419 | " color = vertexColor;\n" 420 | " gl_Position = projectionMatrix * modelMatrix * vec4(vertexPosition.xy, 0.0, 1.0);\n" 421 | "}\n"; 422 | 423 | static const char *fragSrcGL3 = 424 | "#version 150\n" 425 | "uniform sampler2D tex;\n" 426 | "in vec2 uv;\n" 427 | "in vec4 color;\n" 428 | "out vec4 fragColor;\n" 429 | "void main() {\n" 430 | " fragColor = color * texture(tex, uv);\n" 431 | "}\n"; 432 | 433 | Qt3DRender::QMaterial *ImguiManager::buildMaterial(Qt3DRender::QScissorTest **scissor) 434 | { 435 | Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial; 436 | Qt3DRender::QEffect *effect = new Qt3DRender::QEffect; 437 | 438 | auto buildShaderProgram = [](const char *vs, const char *fs) { 439 | Qt3DRender::QShaderProgram *prog = new Qt3DRender::QShaderProgram; 440 | prog->setVertexShaderCode(vs); 441 | prog->setFragmentShaderCode(fs); 442 | return prog; 443 | }; 444 | 445 | if (!rpd.valid) { 446 | rpd.valid = true; 447 | 448 | rpd.progES2 = buildShaderProgram(vertSrcES2, fragSrcES2); 449 | rpd.progGL3 = buildShaderProgram(vertSrcGL3, fragSrcGL3); 450 | 451 | // the framegraph is expected to filter for this key in its gui pass 452 | rpd.techniqueFilterKey = m_outputInfo.guiTechniqueFilterKey; 453 | 454 | rpd.depthTest = new Qt3DRender::QDepthTest; 455 | rpd.depthTest->setDepthFunction(Qt3DRender::QDepthTest::Always); 456 | 457 | rpd.noDepthWrite = new Qt3DRender::QNoDepthMask; 458 | 459 | rpd.blendFunc = new Qt3DRender::QBlendEquation; 460 | rpd.blendArgs = new Qt3DRender::QBlendEquationArguments; 461 | rpd.blendFunc->setBlendFunction(Qt3DRender::QBlendEquation::Add); 462 | rpd.blendArgs->setSourceRgb(Qt3DRender::QBlendEquationArguments::SourceAlpha); 463 | rpd.blendArgs->setDestinationRgb(Qt3DRender::QBlendEquationArguments::OneMinusSourceAlpha); 464 | rpd.blendArgs->setSourceAlpha(Qt3DRender::QBlendEquationArguments::OneMinusSourceAlpha); 465 | rpd.blendArgs->setDestinationAlpha(Qt3DRender::QBlendEquationArguments::Zero); 466 | 467 | rpd.cullFace = new Qt3DRender::QCullFace; 468 | rpd.cullFace->setMode(Qt3DRender::QCullFace::NoCulling); 469 | 470 | rpd.colorMask = new Qt3DRender::QColorMask; 471 | rpd.colorMask->setAlphaMasked(false); 472 | } 473 | 474 | *scissor = new Qt3DRender::QScissorTest; 475 | 476 | // have two techniques: one for OpenGL ES (2.0+) and one for OpenGL core (3.2+) 477 | 478 | auto buildRenderPass = [this, scissor](Qt3DRender::QShaderProgram *prog) { 479 | Qt3DRender::QRenderPass *rpass = new Qt3DRender::QRenderPass; 480 | rpass->setShaderProgram(prog); 481 | 482 | rpass->addRenderState(rpd.depthTest); 483 | rpass->addRenderState(rpd.noDepthWrite); 484 | rpass->addRenderState(rpd.blendFunc); 485 | rpass->addRenderState(rpd.blendArgs); 486 | rpass->addRenderState(rpd.cullFace); 487 | rpass->addRenderState(rpd.colorMask); 488 | rpass->addRenderState(*scissor); 489 | 490 | // Our setEnabled() maps to QNode::setEnabled() on the QRenderPass 491 | // hence the need for keeping track. This is simpler than playing with 492 | // QLayer on entities. 493 | rpd.enabledToggle.append(rpass); 494 | 495 | return rpass; 496 | }; 497 | 498 | Qt3DRender::QTechnique *techniqueES2 = new Qt3DRender::QTechnique; 499 | techniqueES2->addFilterKey(rpd.techniqueFilterKey); 500 | Qt3DRender::QGraphicsApiFilter *apiFilterES2 = techniqueES2->graphicsApiFilter(); 501 | apiFilterES2->setApi(Qt3DRender::QGraphicsApiFilter::OpenGLES); 502 | apiFilterES2->setMajorVersion(2); 503 | apiFilterES2->setMinorVersion(0); 504 | apiFilterES2->setProfile(Qt3DRender::QGraphicsApiFilter::NoProfile); 505 | techniqueES2->addRenderPass(buildRenderPass(rpd.progES2)); 506 | 507 | Qt3DRender::QTechnique *techniqueGL3 = new Qt3DRender::QTechnique; 508 | techniqueGL3->addFilterKey(rpd.techniqueFilterKey); 509 | Qt3DRender::QGraphicsApiFilter *apiFilterGL3 = techniqueGL3->graphicsApiFilter(); 510 | apiFilterGL3->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL); 511 | apiFilterGL3->setMajorVersion(3); 512 | apiFilterGL3->setMinorVersion(2); 513 | apiFilterGL3->setProfile(Qt3DRender::QGraphicsApiFilter::CoreProfile); 514 | techniqueGL3->addRenderPass(buildRenderPass(rpd.progGL3)); 515 | 516 | effect->addTechnique(techniqueES2); 517 | effect->addTechnique(techniqueGL3); 518 | 519 | material->setEffect(effect); 520 | 521 | return material; 522 | } 523 | 524 | #define FIRSTSPECKEY (0x01000000) 525 | #define LASTSPECKEY (0x01000017) 526 | #define MAPSPECKEY(k) ((k) - FIRSTSPECKEY + 256) 527 | 528 | // Do not bother with 3D picking, assume the UI is displayed 1:1 in the window. 529 | class ImguiInputEventFilter : public QObject 530 | { 531 | public: 532 | ImguiInputEventFilter() 533 | { 534 | memset(keyDown, 0, sizeof(keyDown)); 535 | } 536 | 537 | bool eventFilter(QObject *watched, QEvent *event) override; 538 | 539 | QPointF mousePos; 540 | Qt::MouseButtons mouseButtonsDown = Qt::NoButton; 541 | float mouseWheel = 0; 542 | Qt::KeyboardModifiers modifiers = Qt::NoModifier; 543 | bool keyDown[256 + (LASTSPECKEY - FIRSTSPECKEY + 1)]; 544 | QString keyText; 545 | bool enabled = true; 546 | }; 547 | 548 | bool ImguiInputEventFilter::eventFilter(QObject *, QEvent *event) 549 | { 550 | if (!enabled) 551 | return false; 552 | 553 | switch (event->type()) { 554 | case QEvent::MouseButtonPress: 555 | case QEvent::MouseMove: 556 | case QEvent::MouseButtonRelease: 557 | { 558 | QMouseEvent *me = static_cast(event); 559 | mousePos = me->pos(); 560 | mouseButtonsDown = me->buttons(); 561 | modifiers = me->modifiers(); 562 | } 563 | break; 564 | 565 | case QEvent::Wheel: 566 | { 567 | QWheelEvent *we = static_cast(event); 568 | mouseWheel += we->angleDelta().y() / 120.0f; 569 | } 570 | break; 571 | 572 | case QEvent::KeyPress: 573 | case QEvent::KeyRelease: 574 | { 575 | const bool down = event->type() == QEvent::KeyPress; 576 | QKeyEvent *ke = static_cast(event); 577 | modifiers = ke->modifiers(); 578 | if (down) 579 | keyText.append(ke->text()); 580 | int k = ke->key(); 581 | if (k <= 0xFF) 582 | keyDown[k] = down; 583 | else if (k >= FIRSTSPECKEY && k <= LASTSPECKEY) 584 | keyDown[MAPSPECKEY(k)] = down; 585 | } 586 | break; 587 | 588 | default: 589 | break; 590 | } 591 | 592 | return false; 593 | } 594 | 595 | ImguiManager::~ImguiManager() 596 | { 597 | delete m_inputEventFilter; 598 | } 599 | 600 | void ImguiManager::setInputEventSource(QObject *src) 601 | { 602 | if (m_inputEventSource && m_inputEventFilter) 603 | m_inputEventSource->removeEventFilter(m_inputEventFilter); 604 | 605 | m_inputEventSource = src; 606 | 607 | if (!m_inputEventFilter) 608 | m_inputEventFilter = new ImguiInputEventFilter; 609 | 610 | m_inputEventSource->installEventFilter(m_inputEventFilter); 611 | } 612 | 613 | void ImguiManager::updateInput() 614 | { 615 | if (!m_inputEventFilter) 616 | return; 617 | 618 | ImGuiIO &io = ImGui::GetIO(); 619 | 620 | if (!m_inputInitialized) { 621 | m_inputInitialized = true; 622 | 623 | io.KeyMap[ImGuiKey_Tab] = MAPSPECKEY(Qt::Key_Tab); 624 | io.KeyMap[ImGuiKey_LeftArrow] = MAPSPECKEY(Qt::Key_Left); 625 | io.KeyMap[ImGuiKey_RightArrow] = MAPSPECKEY(Qt::Key_Right); 626 | io.KeyMap[ImGuiKey_UpArrow] = MAPSPECKEY(Qt::Key_Up); 627 | io.KeyMap[ImGuiKey_DownArrow] = MAPSPECKEY(Qt::Key_Down); 628 | io.KeyMap[ImGuiKey_PageUp] = MAPSPECKEY(Qt::Key_PageUp); 629 | io.KeyMap[ImGuiKey_PageDown] = MAPSPECKEY(Qt::Key_PageDown); 630 | io.KeyMap[ImGuiKey_Home] = MAPSPECKEY(Qt::Key_Home); 631 | io.KeyMap[ImGuiKey_End] = MAPSPECKEY(Qt::Key_End); 632 | io.KeyMap[ImGuiKey_Delete] = MAPSPECKEY(Qt::Key_Delete); 633 | io.KeyMap[ImGuiKey_Backspace] = MAPSPECKEY(Qt::Key_Backspace); 634 | io.KeyMap[ImGuiKey_Enter] = MAPSPECKEY(Qt::Key_Return); 635 | io.KeyMap[ImGuiKey_Escape] = MAPSPECKEY(Qt::Key_Escape); 636 | 637 | io.KeyMap[ImGuiKey_A] = Qt::Key_A; 638 | io.KeyMap[ImGuiKey_C] = Qt::Key_C; 639 | io.KeyMap[ImGuiKey_V] = Qt::Key_V; 640 | io.KeyMap[ImGuiKey_X] = Qt::Key_X; 641 | io.KeyMap[ImGuiKey_Y] = Qt::Key_Y; 642 | io.KeyMap[ImGuiKey_Z] = Qt::Key_Z; 643 | } 644 | 645 | ImguiInputEventFilter *w = m_inputEventFilter; 646 | 647 | io.MousePos = ImVec2((w->mousePos.x() / m_scale) * m_outputInfo.dpr, (w->mousePos.y() / m_scale) * m_outputInfo.dpr); 648 | 649 | io.MouseDown[0] = w->mouseButtonsDown.testFlag(Qt::LeftButton); 650 | io.MouseDown[1] = w->mouseButtonsDown.testFlag(Qt::RightButton); 651 | io.MouseDown[2] = w->mouseButtonsDown.testFlag(Qt::MiddleButton); 652 | 653 | io.MouseWheel = w->mouseWheel; 654 | w->mouseWheel = 0; 655 | 656 | io.KeyCtrl = w->modifiers.testFlag(Qt::ControlModifier); 657 | io.KeyShift = w->modifiers.testFlag(Qt::ShiftModifier); 658 | io.KeyAlt = w->modifiers.testFlag(Qt::AltModifier); 659 | io.KeySuper = w->modifiers.testFlag(Qt::MetaModifier); 660 | 661 | memcpy(io.KeysDown, w->keyDown, sizeof(w->keyDown)); 662 | 663 | if (!w->keyText.isEmpty()) { 664 | for (const QChar &c : w->keyText) { 665 | ImWchar u = c.unicode(); 666 | if (u) 667 | io.AddInputCharacter(u); 668 | } 669 | w->keyText.clear(); 670 | } 671 | } 672 | 673 | void ImguiManager::setEnabled(bool enabled) 674 | { 675 | if (m_enabled == enabled) 676 | return; 677 | 678 | m_enabled = enabled; 679 | 680 | for (Qt3DCore::QNode *n : rpd.enabledToggle) 681 | n->setEnabled(m_enabled); 682 | 683 | if (m_inputEventFilter) 684 | m_inputEventFilter->enabled = m_enabled; 685 | } 686 | 687 | void ImguiManager::setScale(float scale) 688 | { 689 | m_scale = scale; 690 | } 691 | -------------------------------------------------------------------------------- /src/3rdparty/imgui/stb_textedit.h: -------------------------------------------------------------------------------- 1 | // [ImGui] this is a slightly modified version of stb_truetype.h 1.9. Those changes would need to be pushed into nothings/sb 2 | // [ImGui] - fixed linestart handler when over last character of multi-line buffer + simplified existing code (#588, #815) 3 | // [ImGui] - fixed a state corruption/crash bug in stb_text_redo and stb_textedit_discard_redo (#715) 4 | // [ImGui] - fixed a crash bug in stb_textedit_discard_redo (#681) 5 | // [ImGui] - fixed some minor warnings 6 | 7 | // stb_textedit.h - v1.9 - public domain - Sean Barrett 8 | // Development of this library was sponsored by RAD Game Tools 9 | // 10 | // This C header file implements the guts of a multi-line text-editing 11 | // widget; you implement display, word-wrapping, and low-level string 12 | // insertion/deletion, and stb_textedit will map user inputs into 13 | // insertions & deletions, plus updates to the cursor position, 14 | // selection state, and undo state. 15 | // 16 | // It is intended for use in games and other systems that need to build 17 | // their own custom widgets and which do not have heavy text-editing 18 | // requirements (this library is not recommended for use for editing large 19 | // texts, as its performance does not scale and it has limited undo). 20 | // 21 | // Non-trivial behaviors are modelled after Windows text controls. 22 | // 23 | // 24 | // LICENSE 25 | // 26 | // This software is dual-licensed to the public domain and under the following 27 | // license: you are granted a perpetual, irrevocable license to copy, modify, 28 | // publish, and distribute this file as you see fit. 29 | // 30 | // 31 | // DEPENDENCIES 32 | // 33 | // Uses the C runtime function 'memmove', which you can override 34 | // by defining STB_TEXTEDIT_memmove before the implementation. 35 | // Uses no other functions. Performs no runtime allocations. 36 | // 37 | // 38 | // VERSION HISTORY 39 | // 40 | // 1.9 (2016-08-27) customizable move-by-word 41 | // 1.8 (2016-04-02) better keyboard handling when mouse button is down 42 | // 1.7 (2015-09-13) change y range handling in case baseline is non-0 43 | // 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove 44 | // 1.5 (2014-09-10) add support for secondary keys for OS X 45 | // 1.4 (2014-08-17) fix signed/unsigned warnings 46 | // 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary 47 | // 1.2 (2014-05-27) fix some RAD types that had crept into the new code 48 | // 1.1 (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE ) 49 | // 1.0 (2012-07-26) improve documentation, initial public release 50 | // 0.3 (2012-02-24) bugfixes, single-line mode; insert mode 51 | // 0.2 (2011-11-28) fixes to undo/redo 52 | // 0.1 (2010-07-08) initial version 53 | // 54 | // ADDITIONAL CONTRIBUTORS 55 | // 56 | // Ulf Winklemann: move-by-word in 1.1 57 | // Fabian Giesen: secondary key inputs in 1.5 58 | // Martins Mozeiko: STB_TEXTEDIT_memmove 59 | // 60 | // Bugfixes: 61 | // Scott Graham 62 | // Daniel Keller 63 | // Omar Cornut 64 | // 65 | // USAGE 66 | // 67 | // This file behaves differently depending on what symbols you define 68 | // before including it. 69 | // 70 | // 71 | // Header-file mode: 72 | // 73 | // If you do not define STB_TEXTEDIT_IMPLEMENTATION before including this, 74 | // it will operate in "header file" mode. In this mode, it declares a 75 | // single public symbol, STB_TexteditState, which encapsulates the current 76 | // state of a text widget (except for the string, which you will store 77 | // separately). 78 | // 79 | // To compile in this mode, you must define STB_TEXTEDIT_CHARTYPE to a 80 | // primitive type that defines a single character (e.g. char, wchar_t, etc). 81 | // 82 | // To save space or increase undo-ability, you can optionally define the 83 | // following things that are used by the undo system: 84 | // 85 | // STB_TEXTEDIT_POSITIONTYPE small int type encoding a valid cursor position 86 | // STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow 87 | // STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer 88 | // 89 | // If you don't define these, they are set to permissive types and 90 | // moderate sizes. The undo system does no memory allocations, so 91 | // it grows STB_TexteditState by the worst-case storage which is (in bytes): 92 | // 93 | // [4 + sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT 94 | // + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHAR_COUNT 95 | // 96 | // 97 | // Implementation mode: 98 | // 99 | // If you define STB_TEXTEDIT_IMPLEMENTATION before including this, it 100 | // will compile the implementation of the text edit widget, depending 101 | // on a large number of symbols which must be defined before the include. 102 | // 103 | // The implementation is defined only as static functions. You will then 104 | // need to provide your own APIs in the same file which will access the 105 | // static functions. 106 | // 107 | // The basic concept is that you provide a "string" object which 108 | // behaves like an array of characters. stb_textedit uses indices to 109 | // refer to positions in the string, implicitly representing positions 110 | // in the displayed textedit. This is true for both plain text and 111 | // rich text; even with rich text stb_truetype interacts with your 112 | // code as if there was an array of all the displayed characters. 113 | // 114 | // Symbols that must be the same in header-file and implementation mode: 115 | // 116 | // STB_TEXTEDIT_CHARTYPE the character type 117 | // STB_TEXTEDIT_POSITIONTYPE small type that a valid cursor position 118 | // STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow 119 | // STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer 120 | // 121 | // Symbols you must define for implementation mode: 122 | // 123 | // STB_TEXTEDIT_STRING the type of object representing a string being edited, 124 | // typically this is a wrapper object with other data you need 125 | // 126 | // STB_TEXTEDIT_STRINGLEN(obj) the length of the string (ideally O(1)) 127 | // STB_TEXTEDIT_LAYOUTROW(&r,obj,n) returns the results of laying out a line of characters 128 | // starting from character #n (see discussion below) 129 | // STB_TEXTEDIT_GETWIDTH(obj,n,i) returns the pixel delta from the xpos of the i'th character 130 | // to the xpos of the i+1'th char for a line of characters 131 | // starting at character #n (i.e. accounts for kerning 132 | // with previous char) 133 | // STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character 134 | // (return type is int, -1 means not valid to insert) 135 | // STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based 136 | // STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize 137 | // as manually wordwrapping for end-of-line positioning 138 | // 139 | // STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i 140 | // STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*) 141 | // 142 | // STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key 143 | // 144 | // STB_TEXTEDIT_K_LEFT keyboard input to move cursor left 145 | // STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right 146 | // STB_TEXTEDIT_K_UP keyboard input to move cursor up 147 | // STB_TEXTEDIT_K_DOWN keyboard input to move cursor down 148 | // STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line // e.g. HOME 149 | // STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line // e.g. END 150 | // STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text // e.g. ctrl-HOME 151 | // STB_TEXTEDIT_K_TEXTEND keyboard input to move cursor to end of text // e.g. ctrl-END 152 | // STB_TEXTEDIT_K_DELETE keyboard input to delete selection or character under cursor 153 | // STB_TEXTEDIT_K_BACKSPACE keyboard input to delete selection or character left of cursor 154 | // STB_TEXTEDIT_K_UNDO keyboard input to perform undo 155 | // STB_TEXTEDIT_K_REDO keyboard input to perform redo 156 | // 157 | // Optional: 158 | // STB_TEXTEDIT_K_INSERT keyboard input to toggle insert mode 159 | // STB_TEXTEDIT_IS_SPACE(ch) true if character is whitespace (e.g. 'isspace'), 160 | // required for default WORDLEFT/WORDRIGHT handlers 161 | // STB_TEXTEDIT_MOVEWORDLEFT(obj,i) custom handler for WORDLEFT, returns index to move cursor to 162 | // STB_TEXTEDIT_MOVEWORDRIGHT(obj,i) custom handler for WORDRIGHT, returns index to move cursor to 163 | // STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word // e.g. ctrl-LEFT 164 | // STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word // e.g. ctrl-RIGHT 165 | // STB_TEXTEDIT_K_LINESTART2 secondary keyboard input to move cursor to start of line 166 | // STB_TEXTEDIT_K_LINEEND2 secondary keyboard input to move cursor to end of line 167 | // STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text 168 | // STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text 169 | // 170 | // Todo: 171 | // STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page 172 | // STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page 173 | // 174 | // Keyboard input must be encoded as a single integer value; e.g. a character code 175 | // and some bitflags that represent shift states. to simplify the interface, SHIFT must 176 | // be a bitflag, so we can test the shifted state of cursor movements to allow selection, 177 | // i.e. (STB_TEXTED_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow. 178 | // 179 | // You can encode other things, such as CONTROL or ALT, in additional bits, and 180 | // then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example, 181 | // my Windows implementations add an additional CONTROL bit, and an additional KEYDOWN 182 | // bit. Then all of the STB_TEXTEDIT_K_ values bitwise-or in the KEYDOWN bit, 183 | // and I pass both WM_KEYDOWN and WM_CHAR events to the "key" function in the 184 | // API below. The control keys will only match WM_KEYDOWN events because of the 185 | // keydown bit I add, and STB_TEXTEDIT_KEYTOTEXT only tests for the KEYDOWN 186 | // bit so it only decodes WM_CHAR events. 187 | // 188 | // STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed 189 | // row of characters assuming they start on the i'th character--the width and 190 | // the height and the number of characters consumed. This allows this library 191 | // to traverse the entire layout incrementally. You need to compute word-wrapping 192 | // here. 193 | // 194 | // Each textfield keeps its own insert mode state, which is not how normal 195 | // applications work. To keep an app-wide insert mode, update/copy the 196 | // "insert_mode" field of STB_TexteditState before/after calling API functions. 197 | // 198 | // API 199 | // 200 | // void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) 201 | // 202 | // void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 203 | // void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 204 | // int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 205 | // int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) 206 | // void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) 207 | // 208 | // Each of these functions potentially updates the string and updates the 209 | // state. 210 | // 211 | // initialize_state: 212 | // set the textedit state to a known good default state when initially 213 | // constructing the textedit. 214 | // 215 | // click: 216 | // call this with the mouse x,y on a mouse down; it will update the cursor 217 | // and reset the selection start/end to the cursor point. the x,y must 218 | // be relative to the text widget, with (0,0) being the top left. 219 | // 220 | // drag: 221 | // call this with the mouse x,y on a mouse drag/up; it will update the 222 | // cursor and the selection end point 223 | // 224 | // cut: 225 | // call this to delete the current selection; returns true if there was 226 | // one. you should FIRST copy the current selection to the system paste buffer. 227 | // (To copy, just copy the current selection out of the string yourself.) 228 | // 229 | // paste: 230 | // call this to paste text at the current cursor point or over the current 231 | // selection if there is one. 232 | // 233 | // key: 234 | // call this for keyboard inputs sent to the textfield. you can use it 235 | // for "key down" events or for "translated" key events. if you need to 236 | // do both (as in Win32), or distinguish Unicode characters from control 237 | // inputs, set a high bit to distinguish the two; then you can define the 238 | // various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit 239 | // set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is 240 | // clear. 241 | // 242 | // When rendering, you can read the cursor position and selection state from 243 | // the STB_TexteditState. 244 | // 245 | // 246 | // Notes: 247 | // 248 | // This is designed to be usable in IMGUI, so it allows for the possibility of 249 | // running in an IMGUI that has NOT cached the multi-line layout. For this 250 | // reason, it provides an interface that is compatible with computing the 251 | // layout incrementally--we try to make sure we make as few passes through 252 | // as possible. (For example, to locate the mouse pointer in the text, we 253 | // could define functions that return the X and Y positions of characters 254 | // and binary search Y and then X, but if we're doing dynamic layout this 255 | // will run the layout algorithm many times, so instead we manually search 256 | // forward in one pass. Similar logic applies to e.g. up-arrow and 257 | // down-arrow movement.) 258 | // 259 | // If it's run in a widget that *has* cached the layout, then this is less 260 | // efficient, but it's not horrible on modern computers. But you wouldn't 261 | // want to edit million-line files with it. 262 | 263 | 264 | //////////////////////////////////////////////////////////////////////////// 265 | //////////////////////////////////////////////////////////////////////////// 266 | //// 267 | //// Header-file mode 268 | //// 269 | //// 270 | 271 | #ifndef INCLUDE_STB_TEXTEDIT_H 272 | #define INCLUDE_STB_TEXTEDIT_H 273 | 274 | //////////////////////////////////////////////////////////////////////// 275 | // 276 | // STB_TexteditState 277 | // 278 | // Definition of STB_TexteditState which you should store 279 | // per-textfield; it includes cursor position, selection state, 280 | // and undo state. 281 | // 282 | 283 | #ifndef STB_TEXTEDIT_UNDOSTATECOUNT 284 | #define STB_TEXTEDIT_UNDOSTATECOUNT 99 285 | #endif 286 | #ifndef STB_TEXTEDIT_UNDOCHARCOUNT 287 | #define STB_TEXTEDIT_UNDOCHARCOUNT 999 288 | #endif 289 | #ifndef STB_TEXTEDIT_CHARTYPE 290 | #define STB_TEXTEDIT_CHARTYPE int 291 | #endif 292 | #ifndef STB_TEXTEDIT_POSITIONTYPE 293 | #define STB_TEXTEDIT_POSITIONTYPE int 294 | #endif 295 | 296 | typedef struct 297 | { 298 | // private data 299 | STB_TEXTEDIT_POSITIONTYPE where; 300 | short insert_length; 301 | short delete_length; 302 | short char_storage; 303 | } StbUndoRecord; 304 | 305 | typedef struct 306 | { 307 | // private data 308 | StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT]; 309 | STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT]; 310 | short undo_point, redo_point; 311 | short undo_char_point, redo_char_point; 312 | } StbUndoState; 313 | 314 | typedef struct 315 | { 316 | ///////////////////// 317 | // 318 | // public data 319 | // 320 | 321 | int cursor; 322 | // position of the text cursor within the string 323 | 324 | int select_start; // selection start point 325 | int select_end; 326 | // selection start and end point in characters; if equal, no selection. 327 | // note that start may be less than or greater than end (e.g. when 328 | // dragging the mouse, start is where the initial click was, and you 329 | // can drag in either direction) 330 | 331 | unsigned char insert_mode; 332 | // each textfield keeps its own insert mode state. to keep an app-wide 333 | // insert mode, copy this value in/out of the app state 334 | 335 | ///////////////////// 336 | // 337 | // private data 338 | // 339 | unsigned char cursor_at_end_of_line; // not implemented yet 340 | unsigned char initialized; 341 | unsigned char has_preferred_x; 342 | unsigned char single_line; 343 | unsigned char padding1, padding2, padding3; 344 | float preferred_x; // this determines where the cursor up/down tries to seek to along x 345 | StbUndoState undostate; 346 | } STB_TexteditState; 347 | 348 | 349 | //////////////////////////////////////////////////////////////////////// 350 | // 351 | // StbTexteditRow 352 | // 353 | // Result of layout query, used by stb_textedit to determine where 354 | // the text in each row is. 355 | 356 | // result of layout query 357 | typedef struct 358 | { 359 | float x0,x1; // starting x location, end x location (allows for align=right, etc) 360 | float baseline_y_delta; // position of baseline relative to previous row's baseline 361 | float ymin,ymax; // height of row above and below baseline 362 | int num_chars; 363 | } StbTexteditRow; 364 | #endif //INCLUDE_STB_TEXTEDIT_H 365 | 366 | 367 | //////////////////////////////////////////////////////////////////////////// 368 | //////////////////////////////////////////////////////////////////////////// 369 | //// 370 | //// Implementation mode 371 | //// 372 | //// 373 | 374 | 375 | // implementation isn't include-guarded, since it might have indirectly 376 | // included just the "header" portion 377 | #ifdef STB_TEXTEDIT_IMPLEMENTATION 378 | 379 | #ifndef STB_TEXTEDIT_memmove 380 | #include 381 | #define STB_TEXTEDIT_memmove memmove 382 | #endif 383 | 384 | 385 | ///////////////////////////////////////////////////////////////////////////// 386 | // 387 | // Mouse input handling 388 | // 389 | 390 | // traverse the layout to locate the nearest character to a display position 391 | static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y) 392 | { 393 | StbTexteditRow r; 394 | int n = STB_TEXTEDIT_STRINGLEN(str); 395 | float base_y = 0, prev_x; 396 | int i=0, k; 397 | 398 | r.x0 = r.x1 = 0; 399 | r.ymin = r.ymax = 0; 400 | r.num_chars = 0; 401 | 402 | // search rows to find one that straddles 'y' 403 | while (i < n) { 404 | STB_TEXTEDIT_LAYOUTROW(&r, str, i); 405 | if (r.num_chars <= 0) 406 | return n; 407 | 408 | if (i==0 && y < base_y + r.ymin) 409 | return 0; 410 | 411 | if (y < base_y + r.ymax) 412 | break; 413 | 414 | i += r.num_chars; 415 | base_y += r.baseline_y_delta; 416 | } 417 | 418 | // below all text, return 'after' last character 419 | if (i >= n) 420 | return n; 421 | 422 | // check if it's before the beginning of the line 423 | if (x < r.x0) 424 | return i; 425 | 426 | // check if it's before the end of the line 427 | if (x < r.x1) { 428 | // search characters in row for one that straddles 'x' 429 | prev_x = r.x0; 430 | for (k=0; k < r.num_chars; ++k) { 431 | float w = STB_TEXTEDIT_GETWIDTH(str, i, k); 432 | if (x < prev_x+w) { 433 | if (x < prev_x+w/2) 434 | return k+i; 435 | else 436 | return k+i+1; 437 | } 438 | prev_x += w; 439 | } 440 | // shouldn't happen, but if it does, fall through to end-of-line case 441 | } 442 | 443 | // if the last character is a newline, return that. otherwise return 'after' the last character 444 | if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE) 445 | return i+r.num_chars-1; 446 | else 447 | return i+r.num_chars; 448 | } 449 | 450 | // API click: on mouse down, move the cursor to the clicked location, and reset the selection 451 | static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 452 | { 453 | state->cursor = stb_text_locate_coord(str, x, y); 454 | state->select_start = state->cursor; 455 | state->select_end = state->cursor; 456 | state->has_preferred_x = 0; 457 | } 458 | 459 | // API drag: on mouse drag, move the cursor and selection endpoint to the clicked location 460 | static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 461 | { 462 | int p = stb_text_locate_coord(str, x, y); 463 | if (state->select_start == state->select_end) 464 | state->select_start = state->cursor; 465 | state->cursor = state->select_end = p; 466 | } 467 | 468 | ///////////////////////////////////////////////////////////////////////////// 469 | // 470 | // Keyboard input handling 471 | // 472 | 473 | // forward declarations 474 | static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); 475 | static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); 476 | static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length); 477 | static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length); 478 | static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length); 479 | 480 | typedef struct 481 | { 482 | float x,y; // position of n'th character 483 | float height; // height of line 484 | int first_char, length; // first char of row, and length 485 | int prev_first; // first char of previous row 486 | } StbFindState; 487 | 488 | // find the x/y location of a character, and remember info about the previous row in 489 | // case we get a move-up event (for page up, we'll have to rescan) 490 | static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line) 491 | { 492 | StbTexteditRow r; 493 | int prev_start = 0; 494 | int z = STB_TEXTEDIT_STRINGLEN(str); 495 | int i=0, first; 496 | 497 | if (n == z) { 498 | // if it's at the end, then find the last line -- simpler than trying to 499 | // explicitly handle this case in the regular code 500 | if (single_line) { 501 | STB_TEXTEDIT_LAYOUTROW(&r, str, 0); 502 | find->y = 0; 503 | find->first_char = 0; 504 | find->length = z; 505 | find->height = r.ymax - r.ymin; 506 | find->x = r.x1; 507 | } else { 508 | find->y = 0; 509 | find->x = 0; 510 | find->height = 1; 511 | while (i < z) { 512 | STB_TEXTEDIT_LAYOUTROW(&r, str, i); 513 | prev_start = i; 514 | i += r.num_chars; 515 | } 516 | find->first_char = i; 517 | find->length = 0; 518 | find->prev_first = prev_start; 519 | } 520 | return; 521 | } 522 | 523 | // search rows to find the one that straddles character n 524 | find->y = 0; 525 | 526 | for(;;) { 527 | STB_TEXTEDIT_LAYOUTROW(&r, str, i); 528 | if (n < i + r.num_chars) 529 | break; 530 | prev_start = i; 531 | i += r.num_chars; 532 | find->y += r.baseline_y_delta; 533 | } 534 | 535 | find->first_char = first = i; 536 | find->length = r.num_chars; 537 | find->height = r.ymax - r.ymin; 538 | find->prev_first = prev_start; 539 | 540 | // now scan to find xpos 541 | find->x = r.x0; 542 | i = 0; 543 | for (i=0; first+i < n; ++i) 544 | find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); 545 | } 546 | 547 | #define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) 548 | 549 | // make the selection/cursor state valid if client altered the string 550 | static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 551 | { 552 | int n = STB_TEXTEDIT_STRINGLEN(str); 553 | if (STB_TEXT_HAS_SELECTION(state)) { 554 | if (state->select_start > n) state->select_start = n; 555 | if (state->select_end > n) state->select_end = n; 556 | // if clamping forced them to be equal, move the cursor to match 557 | if (state->select_start == state->select_end) 558 | state->cursor = state->select_start; 559 | } 560 | if (state->cursor > n) state->cursor = n; 561 | } 562 | 563 | // delete characters while updating undo 564 | static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len) 565 | { 566 | stb_text_makeundo_delete(str, state, where, len); 567 | STB_TEXTEDIT_DELETECHARS(str, where, len); 568 | state->has_preferred_x = 0; 569 | } 570 | 571 | // delete the section 572 | static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 573 | { 574 | stb_textedit_clamp(str, state); 575 | if (STB_TEXT_HAS_SELECTION(state)) { 576 | if (state->select_start < state->select_end) { 577 | stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start); 578 | state->select_end = state->cursor = state->select_start; 579 | } else { 580 | stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end); 581 | state->select_start = state->cursor = state->select_end; 582 | } 583 | state->has_preferred_x = 0; 584 | } 585 | } 586 | 587 | // canoncialize the selection so start <= end 588 | static void stb_textedit_sortselection(STB_TexteditState *state) 589 | { 590 | if (state->select_end < state->select_start) { 591 | int temp = state->select_end; 592 | state->select_end = state->select_start; 593 | state->select_start = temp; 594 | } 595 | } 596 | 597 | // move cursor to first character of selection 598 | static void stb_textedit_move_to_first(STB_TexteditState *state) 599 | { 600 | if (STB_TEXT_HAS_SELECTION(state)) { 601 | stb_textedit_sortselection(state); 602 | state->cursor = state->select_start; 603 | state->select_end = state->select_start; 604 | state->has_preferred_x = 0; 605 | } 606 | } 607 | 608 | // move cursor to last character of selection 609 | static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 610 | { 611 | if (STB_TEXT_HAS_SELECTION(state)) { 612 | stb_textedit_sortselection(state); 613 | stb_textedit_clamp(str, state); 614 | state->cursor = state->select_end; 615 | state->select_start = state->select_end; 616 | state->has_preferred_x = 0; 617 | } 618 | } 619 | 620 | #ifdef STB_TEXTEDIT_IS_SPACE 621 | static int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx ) 622 | { 623 | return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1; 624 | } 625 | 626 | #ifndef STB_TEXTEDIT_MOVEWORDLEFT 627 | static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c ) 628 | { 629 | --c; // always move at least one character 630 | while( c >= 0 && !is_word_boundary( str, c ) ) 631 | --c; 632 | 633 | if( c < 0 ) 634 | c = 0; 635 | 636 | return c; 637 | } 638 | #define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous 639 | #endif 640 | 641 | #ifndef STB_TEXTEDIT_MOVEWORDRIGHT 642 | static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c ) 643 | { 644 | const int len = STB_TEXTEDIT_STRINGLEN(str); 645 | ++c; // always move at least one character 646 | while( c < len && !is_word_boundary( str, c ) ) 647 | ++c; 648 | 649 | if( c > len ) 650 | c = len; 651 | 652 | return c; 653 | } 654 | #define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next 655 | #endif 656 | 657 | #endif 658 | 659 | // update selection and cursor to match each other 660 | static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state) 661 | { 662 | if (!STB_TEXT_HAS_SELECTION(state)) 663 | state->select_start = state->select_end = state->cursor; 664 | else 665 | state->cursor = state->select_end; 666 | } 667 | 668 | // API cut: delete selection 669 | static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 670 | { 671 | if (STB_TEXT_HAS_SELECTION(state)) { 672 | stb_textedit_delete_selection(str,state); // implicity clamps 673 | state->has_preferred_x = 0; 674 | return 1; 675 | } 676 | return 0; 677 | } 678 | 679 | // API paste: replace existing selection with passed-in text 680 | static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len) 681 | { 682 | STB_TEXTEDIT_CHARTYPE *text = (STB_TEXTEDIT_CHARTYPE *) ctext; 683 | // if there's a selection, the paste should delete it 684 | stb_textedit_clamp(str, state); 685 | stb_textedit_delete_selection(str,state); 686 | // try to insert the characters 687 | if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) { 688 | stb_text_makeundo_insert(state, state->cursor, len); 689 | state->cursor += len; 690 | state->has_preferred_x = 0; 691 | return 1; 692 | } 693 | // remove the undo since we didn't actually insert the characters 694 | if (state->undostate.undo_point) 695 | --state->undostate.undo_point; 696 | return 0; 697 | } 698 | 699 | // API key: process a keyboard input 700 | static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) 701 | { 702 | retry: 703 | switch (key) { 704 | default: { 705 | int c = STB_TEXTEDIT_KEYTOTEXT(key); 706 | if (c > 0) { 707 | STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c; 708 | 709 | // can't add newline in single-line mode 710 | if (c == '\n' && state->single_line) 711 | break; 712 | 713 | if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { 714 | stb_text_makeundo_replace(str, state, state->cursor, 1, 1); 715 | STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); 716 | if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { 717 | ++state->cursor; 718 | state->has_preferred_x = 0; 719 | } 720 | } else { 721 | stb_textedit_delete_selection(str,state); // implicity clamps 722 | if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { 723 | stb_text_makeundo_insert(state, state->cursor, 1); 724 | ++state->cursor; 725 | state->has_preferred_x = 0; 726 | } 727 | } 728 | } 729 | break; 730 | } 731 | 732 | #ifdef STB_TEXTEDIT_K_INSERT 733 | case STB_TEXTEDIT_K_INSERT: 734 | state->insert_mode = !state->insert_mode; 735 | break; 736 | #endif 737 | 738 | case STB_TEXTEDIT_K_UNDO: 739 | stb_text_undo(str, state); 740 | state->has_preferred_x = 0; 741 | break; 742 | 743 | case STB_TEXTEDIT_K_REDO: 744 | stb_text_redo(str, state); 745 | state->has_preferred_x = 0; 746 | break; 747 | 748 | case STB_TEXTEDIT_K_LEFT: 749 | // if currently there's a selection, move cursor to start of selection 750 | if (STB_TEXT_HAS_SELECTION(state)) 751 | stb_textedit_move_to_first(state); 752 | else 753 | if (state->cursor > 0) 754 | --state->cursor; 755 | state->has_preferred_x = 0; 756 | break; 757 | 758 | case STB_TEXTEDIT_K_RIGHT: 759 | // if currently there's a selection, move cursor to end of selection 760 | if (STB_TEXT_HAS_SELECTION(state)) 761 | stb_textedit_move_to_last(str, state); 762 | else 763 | ++state->cursor; 764 | stb_textedit_clamp(str, state); 765 | state->has_preferred_x = 0; 766 | break; 767 | 768 | case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT: 769 | stb_textedit_clamp(str, state); 770 | stb_textedit_prep_selection_at_cursor(state); 771 | // move selection left 772 | if (state->select_end > 0) 773 | --state->select_end; 774 | state->cursor = state->select_end; 775 | state->has_preferred_x = 0; 776 | break; 777 | 778 | #ifdef STB_TEXTEDIT_MOVEWORDLEFT 779 | case STB_TEXTEDIT_K_WORDLEFT: 780 | if (STB_TEXT_HAS_SELECTION(state)) 781 | stb_textedit_move_to_first(state); 782 | else { 783 | state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); 784 | stb_textedit_clamp( str, state ); 785 | } 786 | break; 787 | 788 | case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT: 789 | if( !STB_TEXT_HAS_SELECTION( state ) ) 790 | stb_textedit_prep_selection_at_cursor(state); 791 | 792 | state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); 793 | state->select_end = state->cursor; 794 | 795 | stb_textedit_clamp( str, state ); 796 | break; 797 | #endif 798 | 799 | #ifdef STB_TEXTEDIT_MOVEWORDRIGHT 800 | case STB_TEXTEDIT_K_WORDRIGHT: 801 | if (STB_TEXT_HAS_SELECTION(state)) 802 | stb_textedit_move_to_last(str, state); 803 | else { 804 | state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); 805 | stb_textedit_clamp( str, state ); 806 | } 807 | break; 808 | 809 | case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT: 810 | if( !STB_TEXT_HAS_SELECTION( state ) ) 811 | stb_textedit_prep_selection_at_cursor(state); 812 | 813 | state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); 814 | state->select_end = state->cursor; 815 | 816 | stb_textedit_clamp( str, state ); 817 | break; 818 | #endif 819 | 820 | case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT: 821 | stb_textedit_prep_selection_at_cursor(state); 822 | // move selection right 823 | ++state->select_end; 824 | stb_textedit_clamp(str, state); 825 | state->cursor = state->select_end; 826 | state->has_preferred_x = 0; 827 | break; 828 | 829 | case STB_TEXTEDIT_K_DOWN: 830 | case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: { 831 | StbFindState find; 832 | StbTexteditRow row; 833 | int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; 834 | 835 | if (state->single_line) { 836 | // on windows, up&down in single-line behave like left&right 837 | key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT); 838 | goto retry; 839 | } 840 | 841 | if (sel) 842 | stb_textedit_prep_selection_at_cursor(state); 843 | else if (STB_TEXT_HAS_SELECTION(state)) 844 | stb_textedit_move_to_last(str,state); 845 | 846 | // compute current position of cursor point 847 | stb_textedit_clamp(str, state); 848 | stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); 849 | 850 | // now find character position down a row 851 | if (find.length) { 852 | float goal_x = state->has_preferred_x ? state->preferred_x : find.x; 853 | float x; 854 | int start = find.first_char + find.length; 855 | state->cursor = start; 856 | STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); 857 | x = row.x0; 858 | for (i=0; i < row.num_chars; ++i) { 859 | float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); 860 | #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE 861 | if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) 862 | break; 863 | #endif 864 | x += dx; 865 | if (x > goal_x) 866 | break; 867 | ++state->cursor; 868 | } 869 | stb_textedit_clamp(str, state); 870 | 871 | state->has_preferred_x = 1; 872 | state->preferred_x = goal_x; 873 | 874 | if (sel) 875 | state->select_end = state->cursor; 876 | } 877 | break; 878 | } 879 | 880 | case STB_TEXTEDIT_K_UP: 881 | case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: { 882 | StbFindState find; 883 | StbTexteditRow row; 884 | int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; 885 | 886 | if (state->single_line) { 887 | // on windows, up&down become left&right 888 | key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT); 889 | goto retry; 890 | } 891 | 892 | if (sel) 893 | stb_textedit_prep_selection_at_cursor(state); 894 | else if (STB_TEXT_HAS_SELECTION(state)) 895 | stb_textedit_move_to_first(state); 896 | 897 | // compute current position of cursor point 898 | stb_textedit_clamp(str, state); 899 | stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); 900 | 901 | // can only go up if there's a previous row 902 | if (find.prev_first != find.first_char) { 903 | // now find character position up a row 904 | float goal_x = state->has_preferred_x ? state->preferred_x : find.x; 905 | float x; 906 | state->cursor = find.prev_first; 907 | STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); 908 | x = row.x0; 909 | for (i=0; i < row.num_chars; ++i) { 910 | float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); 911 | #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE 912 | if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) 913 | break; 914 | #endif 915 | x += dx; 916 | if (x > goal_x) 917 | break; 918 | ++state->cursor; 919 | } 920 | stb_textedit_clamp(str, state); 921 | 922 | state->has_preferred_x = 1; 923 | state->preferred_x = goal_x; 924 | 925 | if (sel) 926 | state->select_end = state->cursor; 927 | } 928 | break; 929 | } 930 | 931 | case STB_TEXTEDIT_K_DELETE: 932 | case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT: 933 | if (STB_TEXT_HAS_SELECTION(state)) 934 | stb_textedit_delete_selection(str, state); 935 | else { 936 | int n = STB_TEXTEDIT_STRINGLEN(str); 937 | if (state->cursor < n) 938 | stb_textedit_delete(str, state, state->cursor, 1); 939 | } 940 | state->has_preferred_x = 0; 941 | break; 942 | 943 | case STB_TEXTEDIT_K_BACKSPACE: 944 | case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT: 945 | if (STB_TEXT_HAS_SELECTION(state)) 946 | stb_textedit_delete_selection(str, state); 947 | else { 948 | stb_textedit_clamp(str, state); 949 | if (state->cursor > 0) { 950 | stb_textedit_delete(str, state, state->cursor-1, 1); 951 | --state->cursor; 952 | } 953 | } 954 | state->has_preferred_x = 0; 955 | break; 956 | 957 | #ifdef STB_TEXTEDIT_K_TEXTSTART2 958 | case STB_TEXTEDIT_K_TEXTSTART2: 959 | #endif 960 | case STB_TEXTEDIT_K_TEXTSTART: 961 | state->cursor = state->select_start = state->select_end = 0; 962 | state->has_preferred_x = 0; 963 | break; 964 | 965 | #ifdef STB_TEXTEDIT_K_TEXTEND2 966 | case STB_TEXTEDIT_K_TEXTEND2: 967 | #endif 968 | case STB_TEXTEDIT_K_TEXTEND: 969 | state->cursor = STB_TEXTEDIT_STRINGLEN(str); 970 | state->select_start = state->select_end = 0; 971 | state->has_preferred_x = 0; 972 | break; 973 | 974 | #ifdef STB_TEXTEDIT_K_TEXTSTART2 975 | case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: 976 | #endif 977 | case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT: 978 | stb_textedit_prep_selection_at_cursor(state); 979 | state->cursor = state->select_end = 0; 980 | state->has_preferred_x = 0; 981 | break; 982 | 983 | #ifdef STB_TEXTEDIT_K_TEXTEND2 984 | case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT: 985 | #endif 986 | case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT: 987 | stb_textedit_prep_selection_at_cursor(state); 988 | state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str); 989 | state->has_preferred_x = 0; 990 | break; 991 | 992 | 993 | #ifdef STB_TEXTEDIT_K_LINESTART2 994 | case STB_TEXTEDIT_K_LINESTART2: 995 | #endif 996 | case STB_TEXTEDIT_K_LINESTART: 997 | stb_textedit_clamp(str, state); 998 | stb_textedit_move_to_first(state); 999 | if (state->single_line) 1000 | state->cursor = 0; 1001 | else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE) 1002 | --state->cursor; 1003 | state->has_preferred_x = 0; 1004 | break; 1005 | 1006 | #ifdef STB_TEXTEDIT_K_LINEEND2 1007 | case STB_TEXTEDIT_K_LINEEND2: 1008 | #endif 1009 | case STB_TEXTEDIT_K_LINEEND: { 1010 | int n = STB_TEXTEDIT_STRINGLEN(str); 1011 | stb_textedit_clamp(str, state); 1012 | stb_textedit_move_to_first(state); 1013 | if (state->single_line) 1014 | state->cursor = n; 1015 | else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) 1016 | ++state->cursor; 1017 | state->has_preferred_x = 0; 1018 | break; 1019 | } 1020 | 1021 | #ifdef STB_TEXTEDIT_K_LINESTART2 1022 | case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT: 1023 | #endif 1024 | case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: 1025 | stb_textedit_clamp(str, state); 1026 | stb_textedit_prep_selection_at_cursor(state); 1027 | if (state->single_line) 1028 | state->cursor = 0; 1029 | else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE) 1030 | --state->cursor; 1031 | state->select_end = state->cursor; 1032 | state->has_preferred_x = 0; 1033 | break; 1034 | 1035 | #ifdef STB_TEXTEDIT_K_LINEEND2 1036 | case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT: 1037 | #endif 1038 | case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: { 1039 | int n = STB_TEXTEDIT_STRINGLEN(str); 1040 | stb_textedit_clamp(str, state); 1041 | stb_textedit_prep_selection_at_cursor(state); 1042 | if (state->single_line) 1043 | state->cursor = n; 1044 | else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) 1045 | ++state->cursor; 1046 | state->select_end = state->cursor; 1047 | state->has_preferred_x = 0; 1048 | break; 1049 | } 1050 | 1051 | // @TODO: 1052 | // STB_TEXTEDIT_K_PGUP - move cursor up a page 1053 | // STB_TEXTEDIT_K_PGDOWN - move cursor down a page 1054 | } 1055 | } 1056 | 1057 | ///////////////////////////////////////////////////////////////////////////// 1058 | // 1059 | // Undo processing 1060 | // 1061 | // @OPTIMIZE: the undo/redo buffer should be circular 1062 | 1063 | static void stb_textedit_flush_redo(StbUndoState *state) 1064 | { 1065 | state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; 1066 | state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; 1067 | } 1068 | 1069 | // discard the oldest entry in the undo list 1070 | static void stb_textedit_discard_undo(StbUndoState *state) 1071 | { 1072 | if (state->undo_point > 0) { 1073 | // if the 0th undo state has characters, clean those up 1074 | if (state->undo_rec[0].char_storage >= 0) { 1075 | int n = state->undo_rec[0].insert_length, i; 1076 | // delete n characters from all other records 1077 | state->undo_char_point = state->undo_char_point - (short) n; // vsnet05 1078 | STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) ((size_t)state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); 1079 | for (i=0; i < state->undo_point; ++i) 1080 | if (state->undo_rec[i].char_storage >= 0) 1081 | state->undo_rec[i].char_storage = state->undo_rec[i].char_storage - (short) n; // vsnet05 // @OPTIMIZE: get rid of char_storage and infer it 1082 | } 1083 | --state->undo_point; 1084 | STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) ((size_t)state->undo_point*sizeof(state->undo_rec[0]))); 1085 | } 1086 | } 1087 | 1088 | // discard the oldest entry in the redo list--it's bad if this 1089 | // ever happens, but because undo & redo have to store the actual 1090 | // characters in different cases, the redo character buffer can 1091 | // fill up even though the undo buffer didn't 1092 | static void stb_textedit_discard_redo(StbUndoState *state) 1093 | { 1094 | int k = STB_TEXTEDIT_UNDOSTATECOUNT-1; 1095 | 1096 | if (state->redo_point <= k) { 1097 | // if the k'th undo state has characters, clean those up 1098 | if (state->undo_rec[k].char_storage >= 0) { 1099 | int n = state->undo_rec[k].insert_length, i; 1100 | // delete n characters from all other records 1101 | state->redo_char_point = state->redo_char_point + (short) n; // vsnet05 1102 | STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((size_t)(STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); 1103 | for (i=state->redo_point; i < k; ++i) 1104 | if (state->undo_rec[i].char_storage >= 0) 1105 | state->undo_rec[i].char_storage = state->undo_rec[i].char_storage + (short) n; // vsnet05 1106 | } 1107 | STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point, state->undo_rec + state->redo_point-1, (size_t) ((size_t)(STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0]))); 1108 | ++state->redo_point; 1109 | } 1110 | } 1111 | 1112 | static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numchars) 1113 | { 1114 | // any time we create a new undo record, we discard redo 1115 | stb_textedit_flush_redo(state); 1116 | 1117 | // if we have no free records, we have to make room, by sliding the 1118 | // existing records down 1119 | if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT) 1120 | stb_textedit_discard_undo(state); 1121 | 1122 | // if the characters to store won't possibly fit in the buffer, we can't undo 1123 | if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) { 1124 | state->undo_point = 0; 1125 | state->undo_char_point = 0; 1126 | return NULL; 1127 | } 1128 | 1129 | // if we don't have enough free characters in the buffer, we have to make room 1130 | while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT) 1131 | stb_textedit_discard_undo(state); 1132 | 1133 | return &state->undo_rec[state->undo_point++]; 1134 | } 1135 | 1136 | static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len) 1137 | { 1138 | StbUndoRecord *r = stb_text_create_undo_record(state, insert_len); 1139 | if (r == NULL) 1140 | return NULL; 1141 | 1142 | r->where = pos; 1143 | r->insert_length = (short) insert_len; 1144 | r->delete_length = (short) delete_len; 1145 | 1146 | if (insert_len == 0) { 1147 | r->char_storage = -1; 1148 | return NULL; 1149 | } else { 1150 | r->char_storage = state->undo_char_point; 1151 | state->undo_char_point = state->undo_char_point + (short) insert_len; 1152 | return &state->undo_char[r->char_storage]; 1153 | } 1154 | } 1155 | 1156 | static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 1157 | { 1158 | StbUndoState *s = &state->undostate; 1159 | StbUndoRecord u, *r; 1160 | if (s->undo_point == 0) 1161 | return; 1162 | 1163 | // we need to do two things: apply the undo record, and create a redo record 1164 | u = s->undo_rec[s->undo_point-1]; 1165 | r = &s->undo_rec[s->redo_point-1]; 1166 | r->char_storage = -1; 1167 | 1168 | r->insert_length = u.delete_length; 1169 | r->delete_length = u.insert_length; 1170 | r->where = u.where; 1171 | 1172 | if (u.delete_length) { 1173 | // if the undo record says to delete characters, then the redo record will 1174 | // need to re-insert the characters that get deleted, so we need to store 1175 | // them. 1176 | 1177 | // there are three cases: 1178 | // there's enough room to store the characters 1179 | // characters stored for *redoing* don't leave room for redo 1180 | // characters stored for *undoing* don't leave room for redo 1181 | // if the last is true, we have to bail 1182 | 1183 | if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) { 1184 | // the undo records take up too much character space; there's no space to store the redo characters 1185 | r->insert_length = 0; 1186 | } else { 1187 | int i; 1188 | 1189 | // there's definitely room to store the characters eventually 1190 | while (s->undo_char_point + u.delete_length > s->redo_char_point) { 1191 | // there's currently not enough room, so discard a redo record 1192 | stb_textedit_discard_redo(s); 1193 | // should never happen: 1194 | if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) 1195 | return; 1196 | } 1197 | r = &s->undo_rec[s->redo_point-1]; 1198 | 1199 | r->char_storage = s->redo_char_point - u.delete_length; 1200 | s->redo_char_point = s->redo_char_point - (short) u.delete_length; 1201 | 1202 | // now save the characters 1203 | for (i=0; i < u.delete_length; ++i) 1204 | s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i); 1205 | } 1206 | 1207 | // now we can carry out the deletion 1208 | STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length); 1209 | } 1210 | 1211 | // check type of recorded action: 1212 | if (u.insert_length) { 1213 | // easy case: was a deletion, so we need to insert n characters 1214 | STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); 1215 | s->undo_char_point -= u.insert_length; 1216 | } 1217 | 1218 | state->cursor = u.where + u.insert_length; 1219 | 1220 | s->undo_point--; 1221 | s->redo_point--; 1222 | } 1223 | 1224 | static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 1225 | { 1226 | StbUndoState *s = &state->undostate; 1227 | StbUndoRecord *u, r; 1228 | if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) 1229 | return; 1230 | 1231 | // we need to do two things: apply the redo record, and create an undo record 1232 | u = &s->undo_rec[s->undo_point]; 1233 | r = s->undo_rec[s->redo_point]; 1234 | 1235 | // we KNOW there must be room for the undo record, because the redo record 1236 | // was derived from an undo record 1237 | 1238 | u->delete_length = r.insert_length; 1239 | u->insert_length = r.delete_length; 1240 | u->where = r.where; 1241 | u->char_storage = -1; 1242 | 1243 | if (r.delete_length) { 1244 | // the redo record requires us to delete characters, so the undo record 1245 | // needs to store the characters 1246 | 1247 | if (s->undo_char_point + u->insert_length > s->redo_char_point) { 1248 | u->insert_length = 0; 1249 | u->delete_length = 0; 1250 | } else { 1251 | int i; 1252 | u->char_storage = s->undo_char_point; 1253 | s->undo_char_point = s->undo_char_point + u->insert_length; 1254 | 1255 | // now save the characters 1256 | for (i=0; i < u->insert_length; ++i) 1257 | s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i); 1258 | } 1259 | 1260 | STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length); 1261 | } 1262 | 1263 | if (r.insert_length) { 1264 | // easy case: need to insert n characters 1265 | STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); 1266 | s->redo_char_point += r.insert_length; 1267 | } 1268 | 1269 | state->cursor = r.where + r.insert_length; 1270 | 1271 | s->undo_point++; 1272 | s->redo_point++; 1273 | } 1274 | 1275 | static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length) 1276 | { 1277 | stb_text_createundo(&state->undostate, where, 0, length); 1278 | } 1279 | 1280 | static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length) 1281 | { 1282 | int i; 1283 | STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0); 1284 | if (p) { 1285 | for (i=0; i < length; ++i) 1286 | p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); 1287 | } 1288 | } 1289 | 1290 | static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length) 1291 | { 1292 | int i; 1293 | STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length); 1294 | if (p) { 1295 | for (i=0; i < old_length; ++i) 1296 | p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); 1297 | } 1298 | } 1299 | 1300 | // reset the state to default 1301 | static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_line) 1302 | { 1303 | state->undostate.undo_point = 0; 1304 | state->undostate.undo_char_point = 0; 1305 | state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; 1306 | state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; 1307 | state->select_end = state->select_start = 0; 1308 | state->cursor = 0; 1309 | state->has_preferred_x = 0; 1310 | state->preferred_x = 0; 1311 | state->cursor_at_end_of_line = 0; 1312 | state->initialized = 1; 1313 | state->single_line = (unsigned char) is_single_line; 1314 | state->insert_mode = 0; 1315 | } 1316 | 1317 | // API initialize 1318 | static void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) 1319 | { 1320 | stb_textedit_clear_state(state, is_single_line); 1321 | } 1322 | #endif//STB_TEXTEDIT_IMPLEMENTATION 1323 | --------------------------------------------------------------------------------