├── glfw
├── lib-vc2010-32
│ └── glfw3.lib
├── lib-vc2010-64
│ └── glfw3.lib
├── COPYING.txt
└── include
│ └── GLFW
│ └── glfw3native.h
├── imgui.ini
├── .gitattributes
├── PerformanceAnalytics.vcxproj.filters
├── PerformanceAnalytics.sln
├── README.md
├── imgui_impl_glfw_gl3.h
├── imconfig.h
├── .gitignore
├── PerformanceAnalytics.vcxproj
├── main.cpp
├── imgui_impl_glfw_gl3.cpp
├── stb_rect_pack.h
├── imgui_internal.h
└── stb_textedit.h
/glfw/lib-vc2010-32/glfw3.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/playmer/PerformanceAnalytics/HEAD/glfw/lib-vc2010-32/glfw3.lib
--------------------------------------------------------------------------------
/glfw/lib-vc2010-64/glfw3.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/playmer/PerformanceAnalytics/HEAD/glfw/lib-vc2010-64/glfw3.lib
--------------------------------------------------------------------------------
/imgui.ini:
--------------------------------------------------------------------------------
1 | [Debug]
2 | Pos=60,60
3 | Size=400,400
4 | Collapsed=0
5 |
6 | [Performance Analyser]
7 | Pos=-55,37
8 | Size=1269,710
9 | Collapsed=0
10 |
11 | [Performance Analyser Test 1]
12 | Pos=255,70
13 | Size=2423,1722
14 | Collapsed=0
15 |
16 | [Performance Analyser Test 2]
17 | Pos=125,43
18 | Size=985,565
19 | Collapsed=0
20 |
21 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/PerformanceAnalytics.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/glfw/COPYING.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2002-2006 Marcus Geelnard
2 | Copyright (c) 2006-2010 Camilla Berglund
3 |
4 | This software is provided 'as-is', without any express or implied
5 | warranty. In no event will the authors be held liable for any damages
6 | arising from the use of this software.
7 |
8 | Permission is granted to anyone to use this software for any purpose,
9 | including commercial applications, and to alter it and redistribute it
10 | freely, subject to the following restrictions:
11 |
12 | 1. The origin of this software must not be misrepresented; you must not
13 | claim that you wrote the original software. If you use this software
14 | in a product, an acknowledgment in the product documentation would
15 | be appreciated but is not required.
16 |
17 | 2. Altered source versions must be plainly marked as such, and must not
18 | be misrepresented as being the original software.
19 |
20 | 3. This notice may not be removed or altered from any source
21 | distribution.
22 |
23 |
--------------------------------------------------------------------------------
/PerformanceAnalytics.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PerformanceAnalytics", "PerformanceAnalytics.vcxproj", "{F1361F3C-54C3-4807-9BD5-1ACC1A76B002}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|x64 = Debug|x64
11 | Debug|x86 = Debug|x86
12 | Release|x64 = Release|x64
13 | Release|x86 = Release|x86
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {F1361F3C-54C3-4807-9BD5-1ACC1A76B002}.Debug|x64.ActiveCfg = Debug|x64
17 | {F1361F3C-54C3-4807-9BD5-1ACC1A76B002}.Debug|x64.Build.0 = Debug|x64
18 | {F1361F3C-54C3-4807-9BD5-1ACC1A76B002}.Debug|x86.ActiveCfg = Debug|Win32
19 | {F1361F3C-54C3-4807-9BD5-1ACC1A76B002}.Debug|x86.Build.0 = Debug|Win32
20 | {F1361F3C-54C3-4807-9BD5-1ACC1A76B002}.Release|x64.ActiveCfg = Release|x64
21 | {F1361F3C-54C3-4807-9BD5-1ACC1A76B002}.Release|x64.Build.0 = Release|x64
22 | {F1361F3C-54C3-4807-9BD5-1ACC1A76B002}.Release|x86.ActiveCfg = Release|Win32
23 | {F1361F3C-54C3-4807-9BD5-1ACC1A76B002}.Release|x86.Build.0 = Release|Win32
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PerformanceAnalytics
2 | Working on a Performance Analyser using ImGui. It's pretty early, but it kindof works. I'd like to eventually make a few different ways to interact with it. One where it runs at it's own application and can take and look at various formats of data, perhaps where it can take live data from a socket. Maybe one where you can just use it with your own ImGui application and it's more of a library. I dunno!
3 |
4 | #Issues
5 | - Currently there's no "time" display. How the blocks are displayed is mostly based on however dear ImGui figures out where to put the next object on a line/next line based on how big they are and how they're being grouped. I'm not yet sure how to make this an "absolute" thing like what @ocornut (The creator of dear ImGui) does in his game engine or how the Telemetry folks do it.
6 |
7 | - A sub problem of that (something that would seemingly be easier to fix in the short-term) is that currently each grouping seems to indent slightly.
8 | - Another sub problem is that Selectables (what's being used to display each block) seem to have a miniumum size, and just odd size properties in general. So the sizes are skewed the smaller I go.
9 |
10 | - If a block is partially offscreen (to the left) the beginning of the text doesn't get moved to the start of the screen, it just stays happily offscreen.
11 | - Scrolling isn't normalized when zooming in and out. I suspect I'll fix this quickly (Next time I work on it), I'm just screwing up the math.
12 | - And so many more.
13 |
--------------------------------------------------------------------------------
/imgui_impl_glfw_gl3.h:
--------------------------------------------------------------------------------
1 | // ImGui GLFW binding with OpenGL3 + shaders
2 | // In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp.
3 |
4 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
5 | // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown().
6 | // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp.
7 | // https://github.com/ocornut/imgui
8 |
9 | struct GLFWwindow;
10 |
11 | IMGUI_API bool ImGui_ImplGlfwGL3_Init(GLFWwindow* window, bool install_callbacks);
12 | IMGUI_API void ImGui_ImplGlfwGL3_Shutdown();
13 | IMGUI_API void ImGui_ImplGlfwGL3_NewFrame();
14 |
15 | // Use if you want to reset your rendering device without losing ImGui state.
16 | IMGUI_API void ImGui_ImplGlfwGL3_InvalidateDeviceObjects();
17 | IMGUI_API bool ImGui_ImplGlfwGL3_CreateDeviceObjects();
18 |
19 | // GLFW callbacks (installed by default if you enable 'install_callbacks' during initialization)
20 | // Provided here if you want to chain callbacks.
21 | // You can also handle inputs yourself and use those as a reference.
22 | IMGUI_API void ImGui_ImplGlfwGL3_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
23 | IMGUI_API void ImGui_ImplGlfwGL3_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
24 | IMGUI_API void ImGui_ImplGlfwGL3_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
25 | IMGUI_API void ImGui_ImplGlfwGL3_CharCallback(GLFWwindow* window, unsigned int c);
26 |
--------------------------------------------------------------------------------
/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 help and test window functionality (ShowUserGuide()/ShowStyleEditor()/ShowTestWindow() methods will be empty)
24 | //#define IMGUI_DISABLE_TEST_WINDOWS
25 |
26 | //---- Don't define obsolete functions names
27 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
28 |
29 | //---- Pack colors to BGRA instead of RGBA (remove need to post process vertex buffer in back ends)
30 | //#define IMGUI_USE_BGRA_PACKED_COLOR
31 |
32 | //---- Implement STB libraries in a namespace to avoid conflicts
33 | //#define IMGUI_STB_NAMESPACE ImGuiStb
34 |
35 | //---- Define constructor and implicit cast operators to convert back<>forth from your math types and ImVec2/ImVec4.
36 | /*
37 | #define IM_VEC2_CLASS_EXTRA \
38 | ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \
39 | operator MyVec2() const { return MyVec2(x,y); }
40 |
41 | #define IM_VEC4_CLASS_EXTRA \
42 | ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \
43 | operator MyVec4() const { return MyVec4(x,y,z,w); }
44 | */
45 |
46 | //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
47 | //---- e.g. create variants of the ImGui::Value() helper for your low-level math types, or your own widgets/helpers.
48 | /*
49 | namespace ImGui
50 | {
51 | void Value(const char* prefix, const MyMatrix44& v, const char* float_format = NULL);
52 | }
53 | */
54 |
55 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 | *.VC.VC.opendb
85 |
86 | # Visual Studio profiler
87 | *.psess
88 | *.vsp
89 | *.vspx
90 | *.sap
91 |
92 | # TFS 2012 Local Workspace
93 | $tf/
94 |
95 | # Guidance Automation Toolkit
96 | *.gpState
97 |
98 | # ReSharper is a .NET coding add-in
99 | _ReSharper*/
100 | *.[Rr]e[Ss]harper
101 | *.DotSettings.user
102 |
103 | # JustCode is a .NET coding add-in
104 | .JustCode
105 |
106 | # TeamCity is a build add-in
107 | _TeamCity*
108 |
109 | # DotCover is a Code Coverage Tool
110 | *.dotCover
111 |
112 | # NCrunch
113 | _NCrunch_*
114 | .*crunch*.local.xml
115 | nCrunchTemp_*
116 |
117 | # MightyMoose
118 | *.mm.*
119 | AutoTest.Net/
120 |
121 | # Web workbench (sass)
122 | .sass-cache/
123 |
124 | # Installshield output folder
125 | [Ee]xpress/
126 |
127 | # DocProject is a documentation generator add-in
128 | DocProject/buildhelp/
129 | DocProject/Help/*.HxT
130 | DocProject/Help/*.HxC
131 | DocProject/Help/*.hhc
132 | DocProject/Help/*.hhk
133 | DocProject/Help/*.hhp
134 | DocProject/Help/Html2
135 | DocProject/Help/html
136 |
137 | # Click-Once directory
138 | publish/
139 |
140 | # Publish Web Output
141 | *.[Pp]ublish.xml
142 | *.azurePubxml
143 | # TODO: Comment the next line if you want to checkin your web deploy settings
144 | # but database connection strings (with potential passwords) will be unencrypted
145 | *.pubxml
146 | *.publishproj
147 |
148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
149 | # checkin your Azure Web App publish settings, but sensitive information contained
150 | # in these scripts will be unencrypted
151 | PublishScripts/
152 |
153 | # NuGet Packages
154 | *.nupkg
155 | # The packages folder can be ignored because of Package Restore
156 | **/packages/*
157 | # except build/, which is used as an MSBuild target.
158 | !**/packages/build/
159 | # Uncomment if necessary however generally it will be regenerated when needed
160 | #!**/packages/repositories.config
161 | # NuGet v3's project.json files produces more ignoreable files
162 | *.nuget.props
163 | *.nuget.targets
164 |
165 | # Microsoft Azure Build Output
166 | csx/
167 | *.build.csdef
168 |
169 | # Microsoft Azure Emulator
170 | ecf/
171 | rcf/
172 |
173 | # Windows Store app package directories and files
174 | AppPackages/
175 | BundleArtifacts/
176 | Package.StoreAssociation.xml
177 | _pkginfo.txt
178 |
179 | # Visual Studio cache files
180 | # files ending in .cache can be ignored
181 | *.[Cc]ache
182 | # but keep track of directories ending in .cache
183 | !*.[Cc]ache/
184 |
185 | # Others
186 | ClientBin/
187 | ~$*
188 | *~
189 | *.dbmdl
190 | *.dbproj.schemaview
191 | *.pfx
192 | *.publishsettings
193 | node_modules/
194 | orleans.codegen.cs
195 |
196 | # Since there are multiple workflows, uncomment next line to ignore bower_components
197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
198 | #bower_components/
199 |
200 | # RIA/Silverlight projects
201 | Generated_Code/
202 |
203 | # Backup & report files from converting an old project file
204 | # to a newer Visual Studio version. Backup files are not needed,
205 | # because we have git ;-)
206 | _UpgradeReport_Files/
207 | Backup*/
208 | UpgradeLog*.XML
209 | UpgradeLog*.htm
210 |
211 | # SQL Server files
212 | *.mdf
213 | *.ldf
214 |
215 | # Business Intelligence projects
216 | *.rdl.data
217 | *.bim.layout
218 | *.bim_*.settings
219 |
220 | # Microsoft Fakes
221 | FakesAssemblies/
222 |
223 | # GhostDoc plugin setting file
224 | *.GhostDoc.xml
225 |
226 | # Node.js Tools for Visual Studio
227 | .ntvs_analysis.dat
228 |
229 | # Visual Studio 6 build log
230 | *.plg
231 |
232 | # Visual Studio 6 workspace options file
233 | *.opt
234 |
235 | # Visual Studio LightSwitch build output
236 | **/*.HTMLClient/GeneratedArtifacts
237 | **/*.DesktopClient/GeneratedArtifacts
238 | **/*.DesktopClient/ModelManifest.xml
239 | **/*.Server/GeneratedArtifacts
240 | **/*.Server/ModelManifest.xml
241 | _Pvt_Extensions
242 |
243 | # Paket dependency manager
244 | .paket/paket.exe
245 | paket-files/
246 |
247 | # FAKE - F# Make
248 | .fake/
249 |
250 | # JetBrains Rider
251 | .idea/
252 | *.sln.iml
253 |
254 | # =========================
255 | # Operating System Files
256 | # =========================
257 |
258 | # OSX
259 | # =========================
260 |
261 | .DS_Store
262 | .AppleDouble
263 | .LSOverride
264 |
265 | # Thumbnails
266 | ._*
267 |
268 | # Files that might appear in the root of a volume
269 | .DocumentRevisions-V100
270 | .fseventsd
271 | .Spotlight-V100
272 | .TemporaryItems
273 | .Trashes
274 | .VolumeIcon.icns
275 |
276 | # Directories potentially created on remote AFP share
277 | .AppleDB
278 | .AppleDesktop
279 | Network Trash Folder
280 | Temporary Items
281 | .apdisk
282 |
283 | # Windows
284 | # =========================
285 |
286 | # Windows image file caches
287 | Thumbs.db
288 | ehthumbs.db
289 |
290 | # Folder config file
291 | Desktop.ini
292 |
293 | # Recycle Bin used on file shares
294 | $RECYCLE.BIN/
295 |
296 | # Windows Installer files
297 | *.cab
298 | *.msi
299 | *.msm
300 | *.msp
301 |
302 | # Windows shortcuts
303 | *.lnk
304 |
--------------------------------------------------------------------------------
/PerformanceAnalytics.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | {F1361F3C-54C3-4807-9BD5-1ACC1A76B002}
23 | PerformanceAnalytics
24 | 8.1
25 |
26 |
27 |
28 | Application
29 | true
30 | v140
31 | MultiByte
32 |
33 |
34 | Application
35 | false
36 | v140
37 | true
38 | MultiByte
39 |
40 |
41 | Application
42 | true
43 | v140
44 | MultiByte
45 |
46 |
47 | Application
48 | false
49 | v140
50 | true
51 | MultiByte
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | $(VC_IncludePath);$(WindowsSDK_IncludePath);$(IncludePath)
73 |
74 |
75 |
76 | Level3
77 | Disabled
78 | true
79 | $(SolutionDir);$(SolutionDir)\glfw\include;$(SolutionDir)\gl3w;$(VCPkgRoot)include;%(AdditionalIncludeDirectories)
80 | true
81 |
82 |
83 | opengl32.lib;glfw3.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
84 | $(SolutionDir)glfw\lib-vc2010-32;%(AdditionalLibraryDirectories)
85 | msvcrt.lib
86 |
87 |
88 |
89 |
90 | Level3
91 | Disabled
92 | true
93 |
94 |
95 | $(SolutionDir)\glfw\lib-vc2010-64;
96 |
97 |
98 |
99 |
100 | Level3
101 | MaxSpeed
102 | true
103 | true
104 | true
105 | $(SolutionDir);$(SolutionDir)\glfw\include;$(SolutionDir)\gl3w;$(VCPkgRoot)include;%(AdditionalIncludeDirectories)
106 |
107 |
108 | true
109 | true
110 | $(SolutionDir)\glfw\lib-vc2010-32;
111 | opengl32.lib;glfw3.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
112 |
113 |
114 |
115 |
116 | Level3
117 | MaxSpeed
118 | true
119 | true
120 | true
121 |
122 |
123 | true
124 | true
125 | $(SolutionDir)\glfw\lib-vc2010-64;
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/main.cpp:
--------------------------------------------------------------------------------
1 | // ImGui - standalone example application for Glfw + OpenGL 3, using programmable pipeline
2 | // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp.
3 |
4 | #include
5 | #include "imgui_impl_glfw_gl3.h"
6 | #include
7 | #include // This example is using gl3w to access OpenGL functions (because it is small). You may use glew/glad/glLoadGen/etc. whatever already works for you.
8 | #include
9 | #include
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | static void error_callback(int error, const char* description)
17 | {
18 | fprintf(stderr, "Error %d: %s\n", error, description);
19 | }
20 |
21 |
22 |
23 | struct PerformanceBlockData
24 | {
25 | const char *mName;
26 | const char *mFile;
27 | int mLine;
28 | };
29 |
30 | struct PerformanceData
31 | {
32 | PerformanceBlockData *mBlock;
33 |
34 | // How many levels below this are there.
35 | //size_t mDepth;
36 |
37 | // What level are we at?
38 | //size_t mLevel;
39 |
40 | std::chrono::time_point mBeginTime;
41 | std::chrono::time_point mEndTime;
42 |
43 | std::vector mChildren;
44 |
45 | PerformanceData(PerformanceBlockData *aBlock)
46 | : mBlock(aBlock)
47 | //mDepth(0),
48 | //mLevel(0)
49 | {
50 | mBeginTime = std::chrono::high_resolution_clock::now();
51 | }
52 |
53 | PerformanceData(PerformanceData &&aOther)
54 | : mBlock(aOther.mBlock),
55 | //mDepth(aOther.mDepth),
56 | //mLevel(aOther.mLevel),
57 | mBeginTime(aOther.mBeginTime),
58 | mEndTime(aOther.mEndTime),
59 | mChildren(std::move(aOther.mChildren))
60 | {
61 |
62 | }
63 |
64 | void End()
65 | {
66 | mEndTime = std::chrono::high_resolution_clock::now();
67 | }
68 |
69 | ~PerformanceData()
70 | {
71 | End();
72 | }
73 |
74 | double TimeTaken()
75 | {
76 | std::chrono::duration fp_ms = mEndTime - mBeginTime;
77 |
78 | return fp_ms.count();
79 | }
80 | };
81 |
82 | struct PerformanceRecorder : public PerformanceData
83 | {
84 | static PerformanceRecorder gRoot;
85 | static PerformanceRecorder *gLastPerformanceCheck;
86 |
87 | PerformanceRecorder(PerformanceBlockData *aBlock)
88 | : PerformanceData(aBlock),
89 | mParent(gLastPerformanceCheck)
90 | {
91 | gLastPerformanceCheck = this;
92 |
93 | //mLevel = mParent->mLevel + 1;
94 | }
95 |
96 | ~PerformanceRecorder()
97 | {
98 | if (this == mParent)
99 | {
100 | return;
101 | }
102 |
103 | gLastPerformanceCheck = mParent;
104 | PerformanceData *self = this;
105 |
106 | End();
107 |
108 | mParent->mChildren.emplace_back(std::move(*self));
109 |
110 | //if (mParent->mDepth < (mDepth + 1))
111 | //{
112 | // mParent->mDepth = mDepth + 1;
113 | //}
114 | }
115 |
116 | PerformanceRecorder *mParent;
117 | };
118 |
119 | PerformanceBlockData gBlockPerf{ "Global Root", __FILE__, __LINE__ };
120 |
121 | PerformanceRecorder *PerformanceRecorder::gLastPerformanceCheck = &gRoot;
122 | PerformanceRecorder PerformanceRecorder::gRoot{ &gBlockPerf };
123 |
124 |
125 | #define YTRACE(aName, x, y) \
126 | static PerformanceBlockData __block__##x##y{aName, __FILE__, __LINE__};\
127 | PerformanceRecorder __perf__##x##y {&__block__##x##y};
128 | //PerformanceBlockData *__block__##x##y = GetPerformanceBlock();
129 | #define XTRACE(aName, x, y) YTRACE(aName, x, y)
130 | #define TRACE(aName, x) XTRACE(aName, x, __COUNTER__)
131 |
132 | #define RecordPerf(aName) TRACE(aName, __RECORDER__)
133 |
134 |
135 | void DisplayPerf(PerformanceData *aRoot, double aTimeScale, size_t aCount = 0)
136 | {
137 | for (auto &perf : aRoot->mChildren)
138 | {
139 | ImGui::PushID(&perf);
140 | ImGui::SameLine();
141 |
142 |
143 | ImGui::PushItemWidth(0.0f);
144 | ImGui::BeginGroup();
145 |
146 | float hue = aCount*0.05f;
147 |
148 | auto size = ImVec2(static_cast(aTimeScale * perf.TimeTaken()), 0.0f);
149 |
150 | ImGui::PushStyleColor(ImGuiCol_Header, ImColor::HSV(hue, 0.6f, 0.6f));
151 | ImGui::Selectable(perf.mBlock->mName, true, 0, size);
152 |
153 | ImGui::PopStyleColor(1);
154 |
155 | if (ImGui::IsItemHovered())
156 | {
157 | ImGui::SetTooltip("%s\n"
158 | "Location: %s:%d\n"
159 | "Children: %d\n"
160 | "Total Time: %fms\n"
161 | "Size: %f\n",
162 | perf.mBlock->mName,
163 | perf.mBlock->mFile,
164 | perf.mBlock->mLine,
165 | perf.mChildren.size(),
166 | perf.TimeTaken(),
167 | size.x);
168 | }
169 | ImGui::NewLine();
170 | DisplayPerf(&perf, aTimeScale, aCount + 1);
171 | ImGui::EndGroup();
172 | ImGui::PopItemWidth();
173 | ImGui::PopID();
174 | }
175 | }
176 |
177 | void ShowPerfWindowTest1()
178 | {
179 | ImGui::Begin("Performance Analyser Test 1");
180 |
181 | static double timeWidthScaling = 1.0f;
182 | const double zoomSpeed = 1.15f;
183 |
184 | float wheel = ImGui::GetIO().MouseWheel;
185 |
186 |
187 | ImGui::BeginChild("scrolling", ImVec2(0.0f, 0.0f), true, ImGuiWindowFlags_HorizontalScrollbar);
188 |
189 |
190 |
191 | if (0.0f > wheel)
192 | {
193 | timeWidthScaling /= zoomSpeed;
194 | }
195 | else if (0.0f < wheel)
196 | {
197 | timeWidthScaling *= zoomSpeed;
198 | }
199 |
200 | DisplayPerf(&PerformanceRecorder::gRoot, timeWidthScaling);
201 |
202 | //static float lastScrollMax = ImGui::GetScrollMaxX();
203 | //float currentScrollMax = ImGui::GetScrollMaxX();
204 | //
205 | //static float lastScroll = ImGui::GetScrollX();
206 | //float currentScroll = ImGui::GetScrollX();
207 |
208 | //float ratio = lastScroll / lastScrollMax;
209 |
210 | //float scroll = ratio * currentScrollMax;
211 |
212 | //printf("L: %f LM: %f C: %f CM: %f S:%f\n",
213 | // lastScroll,
214 | // lastScrollMax,
215 | // currentScroll,
216 | // currentScrollMax,
217 | // scroll);
218 |
219 | if (ImGui::IsWindowFocused() && ImGui::IsMouseDragging())
220 | {
221 | ImVec2 offset(0.0f, 0.0f);
222 |
223 | offset.x -= ImGui::GetIO().MouseDelta.x;
224 | offset.y -= ImGui::GetIO().MouseDelta.y;
225 |
226 | ImGui::SetScrollX(ImGui::GetScrollX() + offset.x);
227 | ImGui::SetScrollY(ImGui::GetScrollY() + offset.y);
228 | }
229 | //else if (lastScrollMax != currentScrollMax)
230 | //{
231 | // //ImGui::SetScrollX(scroll);
232 | //
233 | // lastScrollMax = currentScrollMax;
234 | // lastScroll = currentScroll;
235 | //}
236 |
237 | ImGui::EndChild();
238 | ImGui::End();
239 | }
240 |
241 |
242 |
243 | void DrawPerfItem(PerformanceData &aData)
244 | {
245 | auto initialWindowPos = ImGui::GetCursorScreenPos();
246 | //initialWindowPos.x += 1.0f;
247 | //initialWindowPos.y += 1.0f;
248 |
249 | ImVec4 clip_rect(0.0f, 0.0f, 0.0f, 0.0f);
250 |
251 | //ImVec2 textSize(aData.mTotalTime, ImGui::GetFontSize());
252 | ImVec2 boxSize(aData.TimeTaken() / 2, ImGui::GetTextLineHeight());
253 |
254 | ImGui::Dummy(initialWindowPos, boxSize);
255 | if (ImGui::IsItemHovered())
256 | {
257 | ImGui::SetTooltip("%s\n"
258 | "Location: %s:%d\n"
259 | "Children: %d\n"
260 | "Total Time: %fms\n",
261 | aData.mBlock->mName,
262 | aData.mBlock->mFile,
263 | aData.mBlock->mLine,
264 | aData.mChildren.size(),
265 | aData.TimeTaken());
266 | }
267 |
268 | printf("x: %f, y: %f\n", initialWindowPos.x, initialWindowPos.y);
269 |
270 | ImGui::GetWindowDrawList()->AddRectFilled(initialWindowPos,
271 | boxSize,
272 | ImColor(90, 90, 120, 255));
273 |
274 | ImGui::GetWindowDrawList()->AddText(ImGui::GetFont(),
275 | ImGui::GetFontSize(),
276 | initialWindowPos,
277 | ImColor(255, 255, 255, 255),
278 | aData.mBlock->mName);
279 | }
280 |
281 |
282 |
283 | void ShowPerfWindowTest2()
284 | {
285 | ImGui::Begin("Performance Analyser Test 2");
286 |
287 | static double timeWidthScaling = 1.0f;
288 | const double zoomSpeed = 1.15f;
289 |
290 | float wheel = ImGui::GetIO().MouseWheel;
291 |
292 | if (0.0f > wheel)
293 | {
294 | timeWidthScaling /= zoomSpeed;
295 | }
296 | else if (0.0f < wheel)
297 | {
298 | timeWidthScaling *= zoomSpeed;
299 | }
300 |
301 | DrawPerfItem(PerformanceRecorder::gRoot.mChildren[0]);
302 |
303 | if (ImGui::IsWindowFocused() && ImGui::IsMouseDragging())
304 | {
305 | ImVec2 offset(0.0f, 0.0f);
306 |
307 | offset.x -= ImGui::GetIO().MouseDelta.x;
308 | offset.y -= ImGui::GetIO().MouseDelta.y;
309 |
310 | ImGui::SetScrollX(ImGui::GetScrollX() + offset.x);
311 | ImGui::SetScrollY(ImGui::GetScrollY() + offset.y);
312 | }
313 |
314 | ImGui::End();
315 | }
316 |
317 |
318 |
319 | using namespace std::chrono_literals;
320 |
321 | void GenerateRecursivePerf(size_t aTimes)
322 | {
323 | RecordPerf("GenerateRecursive");
324 |
325 | std::this_thread::sleep_for(2ms);
326 |
327 | if (0 == aTimes)
328 | {
329 | return;
330 | }
331 |
332 | GenerateRecursivePerf(aTimes - 1);
333 | GenerateRecursivePerf(aTimes / 2);
334 | GenerateRecursivePerf(aTimes / 3);
335 | GenerateRecursivePerf(aTimes / 4);
336 | }
337 |
338 |
339 | double GenerateNSquaredPerf(size_t aTimes)
340 | {
341 | RecordPerf("GenerateNSquared");
342 |
343 | double squaredSquared = aTimes;
344 |
345 | for (size_t i = 1; i <= aTimes; ++i)
346 | {
347 | RecordPerf("OuterLoop");
348 | for (size_t j = 1; j <= aTimes; ++j)
349 | {
350 | RecordPerf("InnerLoop");
351 | squaredSquared *= aTimes;
352 | std::this_thread::sleep_for(2ms);
353 | }
354 | }
355 |
356 | return squaredSquared;
357 | }
358 |
359 |
360 | void GenerateTimeData()
361 | {
362 | RecordPerf("GenerateTimeData");
363 |
364 | double d = GenerateNSquaredPerf(7);
365 | (void)d;
366 | GenerateRecursivePerf(12);
367 | }
368 |
369 |
370 | int main(int, char**)
371 | {
372 | GenerateTimeData();
373 |
374 | // Setup window
375 | glfwSetErrorCallback(error_callback);
376 | if (!glfwInit())
377 | return 1;
378 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
379 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
380 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
381 | #if __APPLE__
382 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
383 | #endif
384 | GLFWwindow* window = glfwCreateWindow(1280, 720, "ImGui OpenGL3 example", NULL, NULL);
385 | glfwMakeContextCurrent(window);
386 | gl3wInit();
387 |
388 | // Setup ImGui binding
389 | ImGui_ImplGlfwGL3_Init(window, true);
390 |
391 | // Load Fonts
392 | // (there is a default font, this is only if you want to change it. see extra_fonts/README.txt for more details)
393 | //ImGuiIO& io = ImGui::GetIO();
394 | //io.Fonts->AddFontDefault();
395 | //io.Fonts->AddFontFromFileTTF("../../extra_fonts/Cousine-Regular.ttf", 15.0f);
396 | //io.Fonts->AddFontFromFileTTF("../../extra_fonts/DroidSans.ttf", 16.0f);
397 | //io.Fonts->AddFontFromFileTTF("../../extra_fonts/ProggyClean.ttf", 13.0f);
398 | //io.Fonts->AddFontFromFileTTF("../../extra_fonts/ProggyTiny.ttf", 10.0f);
399 | //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
400 |
401 |
402 | ImVec4 clear_color = ImColor(114, 144, 154);
403 |
404 | // Main loop
405 | while (!glfwWindowShouldClose(window))
406 | {
407 | glfwPollEvents();
408 | ImGui_ImplGlfwGL3_NewFrame();
409 |
410 | ImGui::SetNextWindowPos(ImVec2(350, 20), ImGuiSetCond_FirstUseEver);
411 | ShowPerfWindowTest1();
412 | ShowPerfWindowTest2();
413 |
414 |
415 | // Rendering
416 | int display_w, display_h;
417 | glfwGetFramebufferSize(window, &display_w, &display_h);
418 | glViewport(0, 0, display_w, display_h);
419 | glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
420 | glClear(GL_COLOR_BUFFER_BIT);
421 | ImGui::Render();
422 | glfwSwapBuffers(window);
423 | }
424 |
425 | // Cleanup
426 | ImGui_ImplGlfwGL3_Shutdown();
427 | glfwTerminate();
428 |
429 | return 0;
430 | }
431 |
--------------------------------------------------------------------------------
/glfw/include/GLFW/glfw3native.h:
--------------------------------------------------------------------------------
1 | /*************************************************************************
2 | * GLFW 3.2 - www.glfw.org
3 | * A library for OpenGL, window and input
4 | *------------------------------------------------------------------------
5 | * Copyright (c) 2002-2006 Marcus Geelnard
6 | * Copyright (c) 2006-2010 Camilla Berglund
7 | *
8 | * This software is provided 'as-is', without any express or implied
9 | * warranty. In no event will the authors be held liable for any damages
10 | * arising from the use of this software.
11 | *
12 | * Permission is granted to anyone to use this software for any purpose,
13 | * including commercial applications, and to alter it and redistribute it
14 | * freely, subject to the following restrictions:
15 | *
16 | * 1. The origin of this software must not be misrepresented; you must not
17 | * claim that you wrote the original software. If you use this software
18 | * in a product, an acknowledgment in the product documentation would
19 | * be appreciated but is not required.
20 | *
21 | * 2. Altered source versions must be plainly marked as such, and must not
22 | * be misrepresented as being the original software.
23 | *
24 | * 3. This notice may not be removed or altered from any source
25 | * distribution.
26 | *
27 | *************************************************************************/
28 |
29 | #ifndef _glfw3_native_h_
30 | #define _glfw3_native_h_
31 |
32 | #ifdef __cplusplus
33 | extern "C" {
34 | #endif
35 |
36 |
37 | /*************************************************************************
38 | * Doxygen documentation
39 | *************************************************************************/
40 |
41 | /*! @file glfw3native.h
42 | * @brief The header of the native access functions.
43 | *
44 | * This is the header file of the native access functions. See @ref native for
45 | * more information.
46 | */
47 | /*! @defgroup native Native access
48 | *
49 | * **By using the native access functions you assert that you know what you're
50 | * doing and how to fix problems caused by using them. If you don't, you
51 | * shouldn't be using them.**
52 | *
53 | * Before the inclusion of @ref glfw3native.h, you may define exactly one
54 | * window system API macro and zero or more context creation API macros.
55 | *
56 | * The chosen backends must match those the library was compiled for. Failure
57 | * to do this will cause a link-time error.
58 | *
59 | * The available window API macros are:
60 | * * `GLFW_EXPOSE_NATIVE_WIN32`
61 | * * `GLFW_EXPOSE_NATIVE_COCOA`
62 | * * `GLFW_EXPOSE_NATIVE_X11`
63 | * * `GLFW_EXPOSE_NATIVE_WAYLAND`
64 | * * `GLFW_EXPOSE_NATIVE_MIR`
65 | *
66 | * The available context API macros are:
67 | * * `GLFW_EXPOSE_NATIVE_WGL`
68 | * * `GLFW_EXPOSE_NATIVE_NSGL`
69 | * * `GLFW_EXPOSE_NATIVE_GLX`
70 | * * `GLFW_EXPOSE_NATIVE_EGL`
71 | *
72 | * These macros select which of the native access functions that are declared
73 | * and which platform-specific headers to include. It is then up your (by
74 | * definition platform-specific) code to handle which of these should be
75 | * defined.
76 | */
77 |
78 |
79 | /*************************************************************************
80 | * System headers and types
81 | *************************************************************************/
82 |
83 | #if defined(GLFW_EXPOSE_NATIVE_WIN32)
84 | // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for
85 | // example to allow applications to correctly declare a GL_ARB_debug_output
86 | // callback) but windows.h assumes no one will define APIENTRY before it does
87 | #undef APIENTRY
88 | #include
89 | #elif defined(GLFW_EXPOSE_NATIVE_COCOA)
90 | #include
91 | #if defined(__OBJC__)
92 | #import
93 | #else
94 | typedef void* id;
95 | #endif
96 | #elif defined(GLFW_EXPOSE_NATIVE_X11)
97 | #include
98 | #include
99 | #elif defined(GLFW_EXPOSE_NATIVE_WAYLAND)
100 | #include
101 | #elif defined(GLFW_EXPOSE_NATIVE_MIR)
102 | #include
103 | #endif
104 |
105 | #if defined(GLFW_EXPOSE_NATIVE_WGL)
106 | /* WGL is declared by windows.h */
107 | #endif
108 | #if defined(GLFW_EXPOSE_NATIVE_NSGL)
109 | /* NSGL is declared by Cocoa.h */
110 | #endif
111 | #if defined(GLFW_EXPOSE_NATIVE_GLX)
112 | #include
113 | #endif
114 | #if defined(GLFW_EXPOSE_NATIVE_EGL)
115 | #include
116 | #endif
117 |
118 |
119 | /*************************************************************************
120 | * Functions
121 | *************************************************************************/
122 |
123 | #if defined(GLFW_EXPOSE_NATIVE_WIN32)
124 | /*! @brief Returns the adapter device name of the specified monitor.
125 | *
126 | * @return The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`)
127 | * of the specified monitor, or `NULL` if an [error](@ref error_handling)
128 | * occurred.
129 | *
130 | * @thread_safety This function may be called from any thread. Access is not
131 | * synchronized.
132 | *
133 | * @since Added in version 3.1.
134 | *
135 | * @ingroup native
136 | */
137 | GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor);
138 |
139 | /*! @brief Returns the display device name of the specified monitor.
140 | *
141 | * @return The UTF-8 encoded display device name (for example
142 | * `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an
143 | * [error](@ref error_handling) occurred.
144 | *
145 | * @thread_safety This function may be called from any thread. Access is not
146 | * synchronized.
147 | *
148 | * @since Added in version 3.1.
149 | *
150 | * @ingroup native
151 | */
152 | GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor);
153 |
154 | /*! @brief Returns the `HWND` of the specified window.
155 | *
156 | * @return The `HWND` of the specified window, or `NULL` if an
157 | * [error](@ref error_handling) occurred.
158 | *
159 | * @thread_safety This function may be called from any thread. Access is not
160 | * synchronized.
161 | *
162 | * @since Added in version 3.0.
163 | *
164 | * @ingroup native
165 | */
166 | GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window);
167 | #endif
168 |
169 | #if defined(GLFW_EXPOSE_NATIVE_WGL)
170 | /*! @brief Returns the `HGLRC` of the specified window.
171 | *
172 | * @return The `HGLRC` of the specified window, or `NULL` if an
173 | * [error](@ref error_handling) occurred.
174 | *
175 | * @thread_safety This function may be called from any thread. Access is not
176 | * synchronized.
177 | *
178 | * @since Added in version 3.0.
179 | *
180 | * @ingroup native
181 | */
182 | GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window);
183 | #endif
184 |
185 | #if defined(GLFW_EXPOSE_NATIVE_COCOA)
186 | /*! @brief Returns the `CGDirectDisplayID` of the specified monitor.
187 | *
188 | * @return The `CGDirectDisplayID` of the specified monitor, or
189 | * `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred.
190 | *
191 | * @thread_safety This function may be called from any thread. Access is not
192 | * synchronized.
193 | *
194 | * @since Added in version 3.1.
195 | *
196 | * @ingroup native
197 | */
198 | GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor);
199 |
200 | /*! @brief Returns the `NSWindow` of the specified window.
201 | *
202 | * @return The `NSWindow` of the specified window, or `nil` if an
203 | * [error](@ref error_handling) occurred.
204 | *
205 | * @thread_safety This function may be called from any thread. Access is not
206 | * synchronized.
207 | *
208 | * @since Added in version 3.0.
209 | *
210 | * @ingroup native
211 | */
212 | GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window);
213 | #endif
214 |
215 | #if defined(GLFW_EXPOSE_NATIVE_NSGL)
216 | /*! @brief Returns the `NSOpenGLContext` of the specified window.
217 | *
218 | * @return The `NSOpenGLContext` of the specified window, or `nil` if an
219 | * [error](@ref error_handling) occurred.
220 | *
221 | * @thread_safety This function may be called from any thread. Access is not
222 | * synchronized.
223 | *
224 | * @since Added in version 3.0.
225 | *
226 | * @ingroup native
227 | */
228 | GLFWAPI id glfwGetNSGLContext(GLFWwindow* window);
229 | #endif
230 |
231 | #if defined(GLFW_EXPOSE_NATIVE_X11)
232 | /*! @brief Returns the `Display` used by GLFW.
233 | *
234 | * @return The `Display` used by GLFW, or `NULL` if an
235 | * [error](@ref error_handling) occurred.
236 | *
237 | * @thread_safety This function may be called from any thread. Access is not
238 | * synchronized.
239 | *
240 | * @since Added in version 3.0.
241 | *
242 | * @ingroup native
243 | */
244 | GLFWAPI Display* glfwGetX11Display(void);
245 |
246 | /*! @brief Returns the `RRCrtc` of the specified monitor.
247 | *
248 | * @return The `RRCrtc` of the specified monitor, or `None` if an
249 | * [error](@ref error_handling) occurred.
250 | *
251 | * @thread_safety This function may be called from any thread. Access is not
252 | * synchronized.
253 | *
254 | * @since Added in version 3.1.
255 | *
256 | * @ingroup native
257 | */
258 | GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor);
259 |
260 | /*! @brief Returns the `RROutput` of the specified monitor.
261 | *
262 | * @return The `RROutput` of the specified monitor, or `None` if an
263 | * [error](@ref error_handling) occurred.
264 | *
265 | * @thread_safety This function may be called from any thread. Access is not
266 | * synchronized.
267 | *
268 | * @since Added in version 3.1.
269 | *
270 | * @ingroup native
271 | */
272 | GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor);
273 |
274 | /*! @brief Returns the `Window` of the specified window.
275 | *
276 | * @return The `Window` of the specified window, or `None` if an
277 | * [error](@ref error_handling) occurred.
278 | *
279 | * @thread_safety This function may be called from any thread. Access is not
280 | * synchronized.
281 | *
282 | * @since Added in version 3.0.
283 | *
284 | * @ingroup native
285 | */
286 | GLFWAPI Window glfwGetX11Window(GLFWwindow* window);
287 | #endif
288 |
289 | #if defined(GLFW_EXPOSE_NATIVE_GLX)
290 | /*! @brief Returns the `GLXContext` of the specified window.
291 | *
292 | * @return The `GLXContext` of the specified window, or `NULL` if an
293 | * [error](@ref error_handling) occurred.
294 | *
295 | * @thread_safety This function may be called from any thread. Access is not
296 | * synchronized.
297 | *
298 | * @since Added in version 3.0.
299 | *
300 | * @ingroup native
301 | */
302 | GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window);
303 |
304 | /*! @brief Returns the `GLXWindow` of the specified window.
305 | *
306 | * @return The `GLXWindow` of the specified window, or `None` if an
307 | * [error](@ref error_handling) occurred.
308 | *
309 | * @thread_safety This function may be called from any thread. Access is not
310 | * synchronized.
311 | *
312 | * @since Added in version 3.2.
313 | *
314 | * @ingroup native
315 | */
316 | GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window);
317 | #endif
318 |
319 | #if defined(GLFW_EXPOSE_NATIVE_WAYLAND)
320 | /*! @brief Returns the `struct wl_display*` used by GLFW.
321 | *
322 | * @return The `struct wl_display*` used by GLFW, or `NULL` if an
323 | * [error](@ref error_handling) occurred.
324 | *
325 | * @thread_safety This function may be called from any thread. Access is not
326 | * synchronized.
327 | *
328 | * @since Added in version 3.2.
329 | *
330 | * @ingroup native
331 | */
332 | GLFWAPI struct wl_display* glfwGetWaylandDisplay(void);
333 |
334 | /*! @brief Returns the `struct wl_output*` of the specified monitor.
335 | *
336 | * @return The `struct wl_output*` of the specified monitor, or `NULL` if an
337 | * [error](@ref error_handling) occurred.
338 | *
339 | * @thread_safety This function may be called from any thread. Access is not
340 | * synchronized.
341 | *
342 | * @since Added in version 3.2.
343 | *
344 | * @ingroup native
345 | */
346 | GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor);
347 |
348 | /*! @brief Returns the main `struct wl_surface*` of the specified window.
349 | *
350 | * @return The main `struct wl_surface*` of the specified window, or `NULL` if
351 | * an [error](@ref error_handling) occurred.
352 | *
353 | * @thread_safety This function may be called from any thread. Access is not
354 | * synchronized.
355 | *
356 | * @since Added in version 3.2.
357 | *
358 | * @ingroup native
359 | */
360 | GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window);
361 | #endif
362 |
363 | #if defined(GLFW_EXPOSE_NATIVE_MIR)
364 | /*! @brief Returns the `MirConnection*` used by GLFW.
365 | *
366 | * @return The `MirConnection*` used by GLFW, or `NULL` if an
367 | * [error](@ref error_handling) occurred.
368 | *
369 | * @thread_safety This function may be called from any thread. Access is not
370 | * synchronized.
371 | *
372 | * @since Added in version 3.2.
373 | *
374 | * @ingroup native
375 | */
376 | GLFWAPI MirConnection* glfwGetMirDisplay(void);
377 |
378 | /*! @brief Returns the Mir output ID of the specified monitor.
379 | *
380 | * @return The Mir output ID of the specified monitor, or zero if an
381 | * [error](@ref error_handling) occurred.
382 | *
383 | * @thread_safety This function may be called from any thread. Access is not
384 | * synchronized.
385 | *
386 | * @since Added in version 3.2.
387 | *
388 | * @ingroup native
389 | */
390 | GLFWAPI int glfwGetMirMonitor(GLFWmonitor* monitor);
391 |
392 | /*! @brief Returns the `MirSurface*` of the specified window.
393 | *
394 | * @return The `MirSurface*` of the specified window, or `NULL` if an
395 | * [error](@ref error_handling) occurred.
396 | *
397 | * @thread_safety This function may be called from any thread. Access is not
398 | * synchronized.
399 | *
400 | * @since Added in version 3.2.
401 | *
402 | * @ingroup native
403 | */
404 | GLFWAPI MirSurface* glfwGetMirWindow(GLFWwindow* window);
405 | #endif
406 |
407 | #if defined(GLFW_EXPOSE_NATIVE_EGL)
408 | /*! @brief Returns the `EGLDisplay` used by GLFW.
409 | *
410 | * @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an
411 | * [error](@ref error_handling) occurred.
412 | *
413 | * @thread_safety This function may be called from any thread. Access is not
414 | * synchronized.
415 | *
416 | * @since Added in version 3.0.
417 | *
418 | * @ingroup native
419 | */
420 | GLFWAPI EGLDisplay glfwGetEGLDisplay(void);
421 |
422 | /*! @brief Returns the `EGLContext` of the specified window.
423 | *
424 | * @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an
425 | * [error](@ref error_handling) occurred.
426 | *
427 | * @thread_safety This function may be called from any thread. Access is not
428 | * synchronized.
429 | *
430 | * @since Added in version 3.0.
431 | *
432 | * @ingroup native
433 | */
434 | GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window);
435 |
436 | /*! @brief Returns the `EGLSurface` of the specified window.
437 | *
438 | * @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an
439 | * [error](@ref error_handling) occurred.
440 | *
441 | * @thread_safety This function may be called from any thread. Access is not
442 | * synchronized.
443 | *
444 | * @since Added in version 3.0.
445 | *
446 | * @ingroup native
447 | */
448 | GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window);
449 | #endif
450 |
451 | #ifdef __cplusplus
452 | }
453 | #endif
454 |
455 | #endif /* _glfw3_native_h_ */
456 |
457 |
--------------------------------------------------------------------------------
/imgui_impl_glfw_gl3.cpp:
--------------------------------------------------------------------------------
1 | // ImGui GLFW binding with OpenGL3 + shaders
2 | // In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp.
3 |
4 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
5 | // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown().
6 | // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp.
7 | // https://github.com/ocornut/imgui
8 |
9 | #include
10 | #include "imgui_impl_glfw_gl3.h"
11 |
12 | // GL3W/GLFW
13 | #include // This example is using gl3w to access OpenGL functions (because it is small). You may use glew/glad/glLoadGen/etc. whatever already works for you.
14 | #include
15 | #ifdef _WIN32
16 | #undef APIENTRY
17 | #define GLFW_EXPOSE_NATIVE_WIN32
18 | #define GLFW_EXPOSE_NATIVE_WGL
19 | #include
20 | #endif
21 |
22 | // Data
23 | static GLFWwindow* g_Window = NULL;
24 | static double g_Time = 0.0f;
25 | static bool g_MousePressed[3] = { false, false, false };
26 | static float g_MouseWheel = 0.0f;
27 | static GLuint g_FontTexture = 0;
28 | static int g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
29 | static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0;
30 | static int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0;
31 | static unsigned int g_VboHandle = 0, g_VaoHandle = 0, g_ElementsHandle = 0;
32 |
33 | // This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure)
34 | // If text or lines are blurry when integrating ImGui in your engine:
35 | // - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f)
36 | void ImGui_ImplGlfwGL3_RenderDrawLists(ImDrawData* draw_data)
37 | {
38 | // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
39 | ImGuiIO& io = ImGui::GetIO();
40 | int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x);
41 | int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y);
42 | if (fb_width == 0 || fb_height == 0)
43 | return;
44 | draw_data->ScaleClipRects(io.DisplayFramebufferScale);
45 |
46 | // Backup GL state
47 | GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
48 | GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
49 | GLint last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, &last_active_texture);
50 | GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
51 | GLint last_element_array_buffer; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer);
52 | GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
53 | GLint last_blend_src; glGetIntegerv(GL_BLEND_SRC, &last_blend_src);
54 | GLint last_blend_dst; glGetIntegerv(GL_BLEND_DST, &last_blend_dst);
55 | GLint last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, &last_blend_equation_rgb);
56 | GLint last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &last_blend_equation_alpha);
57 | GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
58 | GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
59 | GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
60 | GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
61 | GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
62 | GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
63 |
64 | // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
65 | glEnable(GL_BLEND);
66 | glBlendEquation(GL_FUNC_ADD);
67 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
68 | glDisable(GL_CULL_FACE);
69 | glDisable(GL_DEPTH_TEST);
70 | glEnable(GL_SCISSOR_TEST);
71 | glActiveTexture(GL_TEXTURE0);
72 |
73 | // Setup viewport, orthographic projection matrix
74 | glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
75 | const float ortho_projection[4][4] =
76 | {
77 | { 2.0f/io.DisplaySize.x, 0.0f, 0.0f, 0.0f },
78 | { 0.0f, 2.0f/-io.DisplaySize.y, 0.0f, 0.0f },
79 | { 0.0f, 0.0f, -1.0f, 0.0f },
80 | {-1.0f, 1.0f, 0.0f, 1.0f },
81 | };
82 | glUseProgram(g_ShaderHandle);
83 | glUniform1i(g_AttribLocationTex, 0);
84 | glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
85 | glBindVertexArray(g_VaoHandle);
86 |
87 | for (int n = 0; n < draw_data->CmdListsCount; n++)
88 | {
89 | const ImDrawList* cmd_list = draw_data->CmdLists[n];
90 | const ImDrawIdx* idx_buffer_offset = 0;
91 |
92 | glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
93 | glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
94 |
95 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
96 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
97 |
98 | for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
99 | {
100 | const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
101 | if (pcmd->UserCallback)
102 | {
103 | pcmd->UserCallback(cmd_list, pcmd);
104 | }
105 | else
106 | {
107 | glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
108 | glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y));
109 | glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
110 | }
111 | idx_buffer_offset += pcmd->ElemCount;
112 | }
113 | }
114 |
115 | // Restore modified GL state
116 | glUseProgram(last_program);
117 | glActiveTexture(last_active_texture);
118 | glBindTexture(GL_TEXTURE_2D, last_texture);
119 | glBindVertexArray(last_vertex_array);
120 | glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
121 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer);
122 | glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
123 | glBlendFunc(last_blend_src, last_blend_dst);
124 | if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
125 | if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
126 | if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
127 | if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
128 | glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
129 | glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
130 | }
131 |
132 | static const char* ImGui_ImplGlfwGL3_GetClipboardText(void* user_data)
133 | {
134 | return glfwGetClipboardString((GLFWwindow*)user_data);
135 | }
136 |
137 | static void ImGui_ImplGlfwGL3_SetClipboardText(void* user_data, const char* text)
138 | {
139 | glfwSetClipboardString((GLFWwindow*)user_data, text);
140 | }
141 |
142 | void ImGui_ImplGlfwGL3_MouseButtonCallback(GLFWwindow*, int button, int action, int /*mods*/)
143 | {
144 | if (action == GLFW_PRESS && button >= 0 && button < 3)
145 | g_MousePressed[button] = true;
146 | }
147 |
148 | void ImGui_ImplGlfwGL3_ScrollCallback(GLFWwindow*, double /*xoffset*/, double yoffset)
149 | {
150 | g_MouseWheel += (float)yoffset; // Use fractional mouse wheel, 1.0 unit 5 lines.
151 | }
152 |
153 | void ImGui_ImplGlfwGL3_KeyCallback(GLFWwindow*, int key, int, int action, int mods)
154 | {
155 | ImGuiIO& io = ImGui::GetIO();
156 | if (action == GLFW_PRESS)
157 | io.KeysDown[key] = true;
158 | if (action == GLFW_RELEASE)
159 | io.KeysDown[key] = false;
160 |
161 | (void)mods; // Modifiers are not reliable across systems
162 | io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL];
163 | io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT];
164 | io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT];
165 | io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER];
166 | }
167 |
168 | void ImGui_ImplGlfwGL3_CharCallback(GLFWwindow*, unsigned int c)
169 | {
170 | ImGuiIO& io = ImGui::GetIO();
171 | if (c > 0 && c < 0x10000)
172 | io.AddInputCharacter((unsigned short)c);
173 | }
174 |
175 | bool ImGui_ImplGlfwGL3_CreateFontsTexture()
176 | {
177 | // Build texture atlas
178 | ImGuiIO& io = ImGui::GetIO();
179 | unsigned char* pixels;
180 | int width, height;
181 | io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
182 |
183 | // Upload texture to graphics system
184 | GLint last_texture;
185 | glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
186 | glGenTextures(1, &g_FontTexture);
187 | glBindTexture(GL_TEXTURE_2D, g_FontTexture);
188 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
189 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
190 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
191 |
192 | // Store our identifier
193 | io.Fonts->TexID = (void *)(intptr_t)g_FontTexture;
194 |
195 | // Restore state
196 | glBindTexture(GL_TEXTURE_2D, last_texture);
197 |
198 | return true;
199 | }
200 |
201 | bool ImGui_ImplGlfwGL3_CreateDeviceObjects()
202 | {
203 | // Backup GL state
204 | GLint last_texture, last_array_buffer, last_vertex_array;
205 | glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
206 | glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
207 | glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
208 |
209 | const GLchar *vertex_shader =
210 | "#version 330\n"
211 | "uniform mat4 ProjMtx;\n"
212 | "in vec2 Position;\n"
213 | "in vec2 UV;\n"
214 | "in vec4 Color;\n"
215 | "out vec2 Frag_UV;\n"
216 | "out vec4 Frag_Color;\n"
217 | "void main()\n"
218 | "{\n"
219 | " Frag_UV = UV;\n"
220 | " Frag_Color = Color;\n"
221 | " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
222 | "}\n";
223 |
224 | const GLchar* fragment_shader =
225 | "#version 330\n"
226 | "uniform sampler2D Texture;\n"
227 | "in vec2 Frag_UV;\n"
228 | "in vec4 Frag_Color;\n"
229 | "out vec4 Out_Color;\n"
230 | "void main()\n"
231 | "{\n"
232 | " Out_Color = Frag_Color * texture( Texture, Frag_UV.st);\n"
233 | "}\n";
234 |
235 | g_ShaderHandle = glCreateProgram();
236 | g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
237 | g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
238 | glShaderSource(g_VertHandle, 1, &vertex_shader, 0);
239 | glShaderSource(g_FragHandle, 1, &fragment_shader, 0);
240 | glCompileShader(g_VertHandle);
241 | glCompileShader(g_FragHandle);
242 | glAttachShader(g_ShaderHandle, g_VertHandle);
243 | glAttachShader(g_ShaderHandle, g_FragHandle);
244 | glLinkProgram(g_ShaderHandle);
245 |
246 | g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
247 | g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
248 | g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position");
249 | g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV");
250 | g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color");
251 |
252 | glGenBuffers(1, &g_VboHandle);
253 | glGenBuffers(1, &g_ElementsHandle);
254 |
255 | glGenVertexArrays(1, &g_VaoHandle);
256 | glBindVertexArray(g_VaoHandle);
257 | glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
258 | glEnableVertexAttribArray(g_AttribLocationPosition);
259 | glEnableVertexAttribArray(g_AttribLocationUV);
260 | glEnableVertexAttribArray(g_AttribLocationColor);
261 |
262 | #define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT))
263 | glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, pos));
264 | glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, uv));
265 | glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, col));
266 | #undef OFFSETOF
267 |
268 | ImGui_ImplGlfwGL3_CreateFontsTexture();
269 |
270 | // Restore modified GL state
271 | glBindTexture(GL_TEXTURE_2D, last_texture);
272 | glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
273 | glBindVertexArray(last_vertex_array);
274 |
275 | return true;
276 | }
277 |
278 | void ImGui_ImplGlfwGL3_InvalidateDeviceObjects()
279 | {
280 | if (g_VaoHandle) glDeleteVertexArrays(1, &g_VaoHandle);
281 | if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle);
282 | if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle);
283 | g_VaoHandle = g_VboHandle = g_ElementsHandle = 0;
284 |
285 | if (g_ShaderHandle && g_VertHandle) glDetachShader(g_ShaderHandle, g_VertHandle);
286 | if (g_VertHandle) glDeleteShader(g_VertHandle);
287 | g_VertHandle = 0;
288 |
289 | if (g_ShaderHandle && g_FragHandle) glDetachShader(g_ShaderHandle, g_FragHandle);
290 | if (g_FragHandle) glDeleteShader(g_FragHandle);
291 | g_FragHandle = 0;
292 |
293 | if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle);
294 | g_ShaderHandle = 0;
295 |
296 | if (g_FontTexture)
297 | {
298 | glDeleteTextures(1, &g_FontTexture);
299 | ImGui::GetIO().Fonts->TexID = 0;
300 | g_FontTexture = 0;
301 | }
302 | }
303 |
304 | bool ImGui_ImplGlfwGL3_Init(GLFWwindow* window, bool install_callbacks)
305 | {
306 | g_Window = window;
307 |
308 | ImGuiIO& io = ImGui::GetIO();
309 | io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array.
310 | io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
311 | io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
312 | io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP;
313 | io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN;
314 | io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP;
315 | io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN;
316 | io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME;
317 | io.KeyMap[ImGuiKey_End] = GLFW_KEY_END;
318 | io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE;
319 | io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE;
320 | io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER;
321 | io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE;
322 | io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
323 | io.KeyMap[ImGuiKey_C] = GLFW_KEY_C;
324 | io.KeyMap[ImGuiKey_V] = GLFW_KEY_V;
325 | io.KeyMap[ImGuiKey_X] = GLFW_KEY_X;
326 | io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
327 | io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
328 |
329 | io.RenderDrawListsFn = ImGui_ImplGlfwGL3_RenderDrawLists; // Alternatively you can set this to NULL and call ImGui::GetDrawData() after ImGui::Render() to get the same ImDrawData pointer.
330 | io.SetClipboardTextFn = ImGui_ImplGlfwGL3_SetClipboardText;
331 | io.GetClipboardTextFn = ImGui_ImplGlfwGL3_GetClipboardText;
332 | io.ClipboardUserData = g_Window;
333 | #ifdef _WIN32
334 | io.ImeWindowHandle = glfwGetWin32Window(g_Window);
335 | #endif
336 |
337 | if (install_callbacks)
338 | {
339 | glfwSetMouseButtonCallback(window, ImGui_ImplGlfwGL3_MouseButtonCallback);
340 | glfwSetScrollCallback(window, ImGui_ImplGlfwGL3_ScrollCallback);
341 | glfwSetKeyCallback(window, ImGui_ImplGlfwGL3_KeyCallback);
342 | glfwSetCharCallback(window, ImGui_ImplGlfwGL3_CharCallback);
343 | }
344 |
345 | return true;
346 | }
347 |
348 | void ImGui_ImplGlfwGL3_Shutdown()
349 | {
350 | ImGui_ImplGlfwGL3_InvalidateDeviceObjects();
351 | ImGui::Shutdown();
352 | }
353 |
354 | void ImGui_ImplGlfwGL3_NewFrame()
355 | {
356 | if (!g_FontTexture)
357 | ImGui_ImplGlfwGL3_CreateDeviceObjects();
358 |
359 | ImGuiIO& io = ImGui::GetIO();
360 |
361 | // Setup display size (every frame to accommodate for window resizing)
362 | int w, h;
363 | int display_w, display_h;
364 | glfwGetWindowSize(g_Window, &w, &h);
365 | glfwGetFramebufferSize(g_Window, &display_w, &display_h);
366 | io.DisplaySize = ImVec2((float)w, (float)h);
367 | io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0);
368 |
369 | // Setup time step
370 | double current_time = glfwGetTime();
371 | io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f);
372 | g_Time = current_time;
373 |
374 | // Setup inputs
375 | // (we already got mouse wheel, keyboard keys & characters from glfw callbacks polled in glfwPollEvents())
376 | if (glfwGetWindowAttrib(g_Window, GLFW_FOCUSED))
377 | {
378 | double mouse_x, mouse_y;
379 | glfwGetCursorPos(g_Window, &mouse_x, &mouse_y);
380 | io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); // Mouse position in screen coordinates (set to -1,-1 if no mouse / on another screen, etc.)
381 | }
382 | else
383 | {
384 | io.MousePos = ImVec2(-1,-1);
385 | }
386 |
387 | for (int i = 0; i < 3; i++)
388 | {
389 | io.MouseDown[i] = g_MousePressed[i] || glfwGetMouseButton(g_Window, i) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame.
390 | g_MousePressed[i] = false;
391 | }
392 |
393 | io.MouseWheel = g_MouseWheel;
394 | g_MouseWheel = 0.0f;
395 |
396 | // Hide OS mouse cursor if ImGui is drawing it
397 | glfwSetInputMode(g_Window, GLFW_CURSOR, io.MouseDrawCursor ? GLFW_CURSOR_HIDDEN : GLFW_CURSOR_NORMAL);
398 |
399 | // Start the frame
400 | ImGui::NewFrame();
401 | }
402 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/imgui_internal.h:
--------------------------------------------------------------------------------
1 | // dear imgui, v1.50 WIP
2 | // (internals)
3 |
4 | // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility!
5 | // Implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators)
6 | // #define IMGUI_DEFINE_MATH_OPERATORS
7 |
8 | #pragma once
9 |
10 | #ifndef IMGUI_VERSION
11 | #error Must include imgui.h before imgui_internal.h
12 | #endif
13 |
14 | #include // FILE*
15 | #include // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf
16 |
17 | #ifdef _MSC_VER
18 | #pragma warning (push)
19 | #pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport)
20 | #endif
21 |
22 | #ifdef __clang__
23 | #pragma clang diagnostic push
24 | #pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h
25 | #pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h
26 | #pragma clang diagnostic ignored "-Wold-style-cast"
27 | #endif
28 |
29 | //-----------------------------------------------------------------------------
30 | // Forward Declarations
31 | //-----------------------------------------------------------------------------
32 |
33 | struct ImRect;
34 | struct ImGuiColMod;
35 | struct ImGuiStyleMod;
36 | struct ImGuiGroupData;
37 | struct ImGuiSimpleColumns;
38 | struct ImGuiDrawContext;
39 | struct ImGuiTextEditState;
40 | struct ImGuiIniData;
41 | struct ImGuiMouseCursorData;
42 | struct ImGuiPopupRef;
43 | struct ImGuiWindow;
44 |
45 | typedef int ImGuiLayoutType; // enum ImGuiLayoutType_
46 | typedef int ImGuiButtonFlags; // enum ImGuiButtonFlags_
47 | typedef int ImGuiTreeNodeFlags; // enum ImGuiTreeNodeFlags_
48 | typedef int ImGuiSliderFlags; // enum ImGuiSliderFlags_
49 |
50 | //-------------------------------------------------------------------------
51 | // STB libraries
52 | //-------------------------------------------------------------------------
53 |
54 | namespace ImGuiStb
55 | {
56 |
57 | #undef STB_TEXTEDIT_STRING
58 | #undef STB_TEXTEDIT_CHARTYPE
59 | #define STB_TEXTEDIT_STRING ImGuiTextEditState
60 | #define STB_TEXTEDIT_CHARTYPE ImWchar
61 | #define STB_TEXTEDIT_GETWIDTH_NEWLINE -1.0f
62 | #include "stb_textedit.h"
63 |
64 | } // namespace ImGuiStb
65 |
66 | //-----------------------------------------------------------------------------
67 | // Context
68 | //-----------------------------------------------------------------------------
69 |
70 | #ifndef GImGui
71 | extern IMGUI_API ImGuiContext* GImGui; // Current implicit ImGui context pointer
72 | #endif
73 |
74 | //-----------------------------------------------------------------------------
75 | // Helpers
76 | //-----------------------------------------------------------------------------
77 |
78 | #define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR)/sizeof(*_ARR)))
79 | #define IM_PI 3.14159265358979323846f
80 | #define IM_OFFSETOF(_TYPE,_ELM) ((size_t)&(((_TYPE*)0)->_ELM))
81 |
82 | // Helpers: UTF-8 <> wchar
83 | IMGUI_API int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count
84 | IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // return input UTF-8 bytes count
85 | IMGUI_API int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count
86 | IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count)
87 | IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string as UTF-8 code-points
88 |
89 | // Helpers: Misc
90 | IMGUI_API ImU32 ImHash(const void* data, int data_size, ImU32 seed = 0); // Pass data_size==0 for zero-terminated strings
91 | IMGUI_API void* ImLoadFileToMemory(const char* filename, const char* file_open_mode, int* out_file_size = NULL, int padding_bytes = 0);
92 | IMGUI_API bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c);
93 | static inline bool ImCharIsSpace(int c) { return c == ' ' || c == '\t' || c == 0x3000; }
94 | static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }
95 |
96 | // Helpers: String
97 | IMGUI_API int ImStricmp(const char* str1, const char* str2);
98 | IMGUI_API int ImStrnicmp(const char* str1, const char* str2, int count);
99 | IMGUI_API char* ImStrdup(const char* str);
100 | IMGUI_API int ImStrlenW(const ImWchar* str);
101 | IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line
102 | IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end);
103 | IMGUI_API int ImFormatString(char* buf, int buf_size, const char* fmt, ...) IM_PRINTFARGS(3);
104 | IMGUI_API int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args);
105 |
106 | // Helpers: Math
107 | // We are keeping those not leaking to the user by default, in the case the user has implicit cast operators between ImVec2 and its own types (when IM_VEC2_CLASS_EXTRA is defined)
108 | #ifdef IMGUI_DEFINE_MATH_OPERATORS
109 | static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x*rhs, lhs.y*rhs); }
110 | static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x/rhs, lhs.y/rhs); }
111 | static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x+rhs.x, lhs.y+rhs.y); }
112 | static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x-rhs.x, lhs.y-rhs.y); }
113 | static inline ImVec2 operator*(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x*rhs.x, lhs.y*rhs.y); }
114 | static inline ImVec2 operator/(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x/rhs.x, lhs.y/rhs.y); }
115 | static inline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs) { lhs.x += rhs.x; lhs.y += rhs.y; return lhs; }
116 | static inline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs) { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; }
117 | static inline ImVec2& operator*=(ImVec2& lhs, const float rhs) { lhs.x *= rhs; lhs.y *= rhs; return lhs; }
118 | static inline ImVec2& operator/=(ImVec2& lhs, const float rhs) { lhs.x /= rhs; lhs.y /= rhs; return lhs; }
119 | static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x-rhs.x, lhs.y-rhs.y, lhs.z-rhs.z, lhs.w-rhs.w); }
120 | #endif
121 |
122 | static inline int ImMin(int lhs, int rhs) { return lhs < rhs ? lhs : rhs; }
123 | static inline int ImMax(int lhs, int rhs) { return lhs >= rhs ? lhs : rhs; }
124 | static inline float ImMin(float lhs, float rhs) { return lhs < rhs ? lhs : rhs; }
125 | static inline float ImMax(float lhs, float rhs) { return lhs >= rhs ? lhs : rhs; }
126 | static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(ImMin(lhs.x,rhs.x), ImMin(lhs.y,rhs.y)); }
127 | static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(ImMax(lhs.x,rhs.x), ImMax(lhs.y,rhs.y)); }
128 | static inline int ImClamp(int v, int mn, int mx) { return (v < mn) ? mn : (v > mx) ? mx : v; }
129 | static inline float ImClamp(float v, float mn, float mx) { return (v < mn) ? mn : (v > mx) ? mx : v; }
130 | static inline ImVec2 ImClamp(const ImVec2& f, const ImVec2& mn, ImVec2 mx) { return ImVec2(ImClamp(f.x,mn.x,mx.x), ImClamp(f.y,mn.y,mx.y)); }
131 | static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; }
132 | static inline float ImLerp(float a, float b, float t) { return a + (b - a) * t; }
133 | static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); }
134 | static inline float ImLengthSqr(const ImVec2& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y; }
135 | static inline float ImLengthSqr(const ImVec4& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y + lhs.z*lhs.z + lhs.w*lhs.w; }
136 | static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = lhs.x*lhs.x + lhs.y*lhs.y; if (d > 0.0f) return 1.0f / sqrtf(d); return fail_value; }
137 | static inline float ImFloor(float f) { return (float)(int)f; }
138 | static inline ImVec2 ImFloor(ImVec2 v) { return ImVec2((float)(int)v.x, (float)(int)v.y); }
139 |
140 | // We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax.
141 | // Defining a custom placement new() with a dummy parameter allows us to bypass including which on some platforms complains when user has disabled exceptions.
142 | #ifdef IMGUI_DEFINE_PLACEMENT_NEW
143 | struct ImPlacementNewDummy {};
144 | inline void* operator new(size_t, ImPlacementNewDummy, void* ptr) { return ptr; }
145 | inline void operator delete(void*, ImPlacementNewDummy, void*) {}
146 | #define IM_PLACEMENT_NEW(_PTR) new(ImPlacementNewDummy(), _PTR)
147 | #endif
148 |
149 | //-----------------------------------------------------------------------------
150 | // Types
151 | //-----------------------------------------------------------------------------
152 |
153 | enum ImGuiButtonFlags_
154 | {
155 | ImGuiButtonFlags_Repeat = 1 << 0, // hold to repeat
156 | ImGuiButtonFlags_PressedOnClickRelease = 1 << 1, // (default) return pressed on click+release on same item (default if no PressedOn** flag is set)
157 | ImGuiButtonFlags_PressedOnClick = 1 << 2, // return pressed on click (default requires click+release)
158 | ImGuiButtonFlags_PressedOnRelease = 1 << 3, // return pressed on release (default requires click+release)
159 | ImGuiButtonFlags_PressedOnDoubleClick = 1 << 4, // return pressed on double-click (default requires click+release)
160 | ImGuiButtonFlags_FlattenChilds = 1 << 5, // allow interaction even if a child window is overlapping
161 | ImGuiButtonFlags_DontClosePopups = 1 << 6, // disable automatically closing parent popup on press
162 | ImGuiButtonFlags_Disabled = 1 << 7, // disable interaction
163 | ImGuiButtonFlags_AlignTextBaseLine = 1 << 8, // vertically align button to match text baseline - ButtonEx() only
164 | ImGuiButtonFlags_NoKeyModifiers = 1 << 9, // disable interaction if a key modifier is held
165 | ImGuiButtonFlags_AllowOverlapMode = 1 << 10 // require previous frame HoveredId to either match id or be null before being usable
166 | };
167 |
168 | enum ImGuiSliderFlags_
169 | {
170 | ImGuiSliderFlags_Vertical = 1 << 0
171 | };
172 |
173 | enum ImGuiSelectableFlagsPrivate_
174 | {
175 | // NB: need to be in sync with last value of ImGuiSelectableFlags_
176 | ImGuiSelectableFlags_Menu = 1 << 3,
177 | ImGuiSelectableFlags_MenuItem = 1 << 4,
178 | ImGuiSelectableFlags_Disabled = 1 << 5,
179 | ImGuiSelectableFlags_DrawFillAvailWidth = 1 << 6
180 | };
181 |
182 | // FIXME: this is in development, not exposed/functional as a generic feature yet.
183 | enum ImGuiLayoutType_
184 | {
185 | ImGuiLayoutType_Vertical,
186 | ImGuiLayoutType_Horizontal
187 | };
188 |
189 | enum ImGuiPlotType
190 | {
191 | ImGuiPlotType_Lines,
192 | ImGuiPlotType_Histogram
193 | };
194 |
195 | enum ImGuiDataType
196 | {
197 | ImGuiDataType_Int,
198 | ImGuiDataType_Float,
199 | ImGuiDataType_Float2,
200 | };
201 |
202 | enum ImGuiCorner
203 | {
204 | ImGuiCorner_TopLeft = 1 << 0, // 1
205 | ImGuiCorner_TopRight = 1 << 1, // 2
206 | ImGuiCorner_BottomRight = 1 << 2, // 4
207 | ImGuiCorner_BottomLeft = 1 << 3, // 8
208 | ImGuiCorner_All = 0x0F
209 | };
210 |
211 | // 2D axis aligned bounding-box
212 | // NB: we can't rely on ImVec2 math operators being available here
213 | struct IMGUI_API ImRect
214 | {
215 | ImVec2 Min; // Upper-left
216 | ImVec2 Max; // Lower-right
217 |
218 | ImRect() : Min(FLT_MAX,FLT_MAX), Max(-FLT_MAX,-FLT_MAX) {}
219 | ImRect(const ImVec2& min, const ImVec2& max) : Min(min), Max(max) {}
220 | ImRect(const ImVec4& v) : Min(v.x, v.y), Max(v.z, v.w) {}
221 | ImRect(float x1, float y1, float x2, float y2) : Min(x1, y1), Max(x2, y2) {}
222 |
223 | ImVec2 GetCenter() const { return ImVec2((Min.x+Max.x)*0.5f, (Min.y+Max.y)*0.5f); }
224 | ImVec2 GetSize() const { return ImVec2(Max.x-Min.x, Max.y-Min.y); }
225 | float GetWidth() const { return Max.x-Min.x; }
226 | float GetHeight() const { return Max.y-Min.y; }
227 | ImVec2 GetTL() const { return Min; } // Top-left
228 | ImVec2 GetTR() const { return ImVec2(Max.x, Min.y); } // Top-right
229 | ImVec2 GetBL() const { return ImVec2(Min.x, Max.y); } // Bottom-left
230 | ImVec2 GetBR() const { return Max; } // Bottom-right
231 | bool Contains(const ImVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y; }
232 | bool Contains(const ImRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x < Max.x && r.Max.y < Max.y; }
233 | bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; }
234 | void Add(const ImVec2& rhs) { if (Min.x > rhs.x) Min.x = rhs.x; if (Min.y > rhs.y) Min.y = rhs.y; if (Max.x < rhs.x) Max.x = rhs.x; if (Max.y < rhs.y) Max.y = rhs.y; }
235 | void Add(const ImRect& rhs) { if (Min.x > rhs.Min.x) Min.x = rhs.Min.x; if (Min.y > rhs.Min.y) Min.y = rhs.Min.y; if (Max.x < rhs.Max.x) Max.x = rhs.Max.x; if (Max.y < rhs.Max.y) Max.y = rhs.Max.y; }
236 | void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; }
237 | void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; }
238 | void Reduce(const ImVec2& amount) { Min.x += amount.x; Min.y += amount.y; Max.x -= amount.x; Max.y -= amount.y; }
239 | void Clip(const ImRect& clip) { if (Min.x < clip.Min.x) Min.x = clip.Min.x; if (Min.y < clip.Min.y) Min.y = clip.Min.y; if (Max.x > clip.Max.x) Max.x = clip.Max.x; if (Max.y > clip.Max.y) Max.y = clip.Max.y; }
240 | void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; }
241 | ImVec2 GetClosestPoint(ImVec2 p, bool on_edge) const
242 | {
243 | if (!on_edge && Contains(p))
244 | return p;
245 | if (p.x > Max.x) p.x = Max.x;
246 | else if (p.x < Min.x) p.x = Min.x;
247 | if (p.y > Max.y) p.y = Max.y;
248 | else if (p.y < Min.y) p.y = Min.y;
249 | return p;
250 | }
251 | };
252 |
253 | // Stacked color modifier, backup of modified data so we can restore it
254 | struct ImGuiColMod
255 | {
256 | ImGuiCol Col;
257 | ImVec4 BackupValue;
258 | };
259 |
260 | // Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable.
261 | struct ImGuiStyleMod
262 | {
263 | ImGuiStyleVar VarIdx;
264 | union { int BackupInt[2]; float BackupFloat[2]; };
265 | ImGuiStyleMod(ImGuiStyleVar idx, int v) { VarIdx = idx; BackupInt[0] = v; }
266 | ImGuiStyleMod(ImGuiStyleVar idx, float v) { VarIdx = idx; BackupFloat[0] = v; }
267 | ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v) { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; }
268 | };
269 |
270 | // Stacked data for BeginGroup()/EndGroup()
271 | struct ImGuiGroupData
272 | {
273 | ImVec2 BackupCursorPos;
274 | ImVec2 BackupCursorMaxPos;
275 | float BackupIndentX;
276 | float BackupGroupOffsetX;
277 | float BackupCurrentLineHeight;
278 | float BackupCurrentLineTextBaseOffset;
279 | float BackupLogLinePosY;
280 | bool BackupActiveIdIsAlive;
281 | bool AdvanceCursor;
282 | };
283 |
284 | // Per column data for Columns()
285 | struct ImGuiColumnData
286 | {
287 | float OffsetNorm; // Column start offset, normalized 0.0 (far left) -> 1.0 (far right)
288 | //float IndentX;
289 | };
290 |
291 | // Simple column measurement currently used for MenuItem() only. This is very short-sighted/throw-away code and NOT a generic helper.
292 | struct IMGUI_API ImGuiSimpleColumns
293 | {
294 | int Count;
295 | float Spacing;
296 | float Width, NextWidth;
297 | float Pos[8], NextWidths[8];
298 |
299 | ImGuiSimpleColumns();
300 | void Update(int count, float spacing, bool clear);
301 | float DeclColumns(float w0, float w1, float w2);
302 | float CalcExtraSpace(float avail_w);
303 | };
304 |
305 | // Internal state of the currently focused/edited text input box
306 | struct IMGUI_API ImGuiTextEditState
307 | {
308 | ImGuiID Id; // widget id owning the text state
309 | ImVector Text; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer.
310 | ImVector InitialText; // backup of end-user buffer at the time of focus (in UTF-8, unaltered)
311 | ImVector TempTextBuffer;
312 | int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format.
313 | int BufSizeA; // end-user buffer size
314 | float ScrollX;
315 | ImGuiStb::STB_TexteditState StbState;
316 | float CursorAnim;
317 | bool CursorFollow;
318 | bool SelectedAllMouseLock;
319 |
320 | ImGuiTextEditState() { memset(this, 0, sizeof(*this)); }
321 | void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking
322 | void CursorClamp() { StbState.cursor = ImMin(StbState.cursor, CurLenW); StbState.select_start = ImMin(StbState.select_start, CurLenW); StbState.select_end = ImMin(StbState.select_end, CurLenW); }
323 | bool HasSelection() const { return StbState.select_start != StbState.select_end; }
324 | void ClearSelection() { StbState.select_start = StbState.select_end = StbState.cursor; }
325 | void SelectAll() { StbState.select_start = 0; StbState.select_end = CurLenW; StbState.cursor = StbState.select_end; StbState.has_preferred_x = false; }
326 | void OnKeyPressed(int key);
327 | };
328 |
329 | // Data saved in imgui.ini file
330 | struct ImGuiIniData
331 | {
332 | char* Name;
333 | ImGuiID Id;
334 | ImVec2 Pos;
335 | ImVec2 Size;
336 | bool Collapsed;
337 | };
338 |
339 | // Mouse cursor data (used when io.MouseDrawCursor is set)
340 | struct ImGuiMouseCursorData
341 | {
342 | ImGuiMouseCursor Type;
343 | ImVec2 HotOffset;
344 | ImVec2 Size;
345 | ImVec2 TexUvMin[2];
346 | ImVec2 TexUvMax[2];
347 | };
348 |
349 | // Storage for current popup stack
350 | struct ImGuiPopupRef
351 | {
352 | ImGuiID PopupId; // Set on OpenPopup()
353 | ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup()
354 | ImGuiWindow* ParentWindow; // Set on OpenPopup()
355 | ImGuiID ParentMenuSet; // Set on OpenPopup()
356 | ImVec2 MousePosOnOpen; // Copy of mouse position at the time of opening popup
357 |
358 | ImGuiPopupRef(ImGuiID id, ImGuiWindow* parent_window, ImGuiID parent_menu_set, const ImVec2& mouse_pos) { PopupId = id; Window = NULL; ParentWindow = parent_window; ParentMenuSet = parent_menu_set; MousePosOnOpen = mouse_pos; }
359 | };
360 |
361 | // Main state for ImGui
362 | struct ImGuiContext
363 | {
364 | bool Initialized;
365 | ImGuiIO IO;
366 | ImGuiStyle Style;
367 | ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back()
368 | float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize()
369 | float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Size of characters.
370 | ImVec2 FontTexUvWhitePixel; // (Shortcut) == Font->TexUvWhitePixel
371 |
372 | float Time;
373 | int FrameCount;
374 | int FrameCountEnded;
375 | int FrameCountRendered;
376 | ImVector Windows;
377 | ImVector WindowsSortBuffer;
378 | ImGuiWindow* CurrentWindow; // Being drawn into
379 | ImVector CurrentWindowStack;
380 | ImGuiWindow* FocusedWindow; // Will catch keyboard inputs
381 | ImGuiWindow* HoveredWindow; // Will catch mouse inputs
382 | ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only)
383 | ImGuiID HoveredId; // Hovered widget
384 | bool HoveredIdAllowOverlap;
385 | ImGuiID HoveredIdPreviousFrame;
386 | ImGuiID ActiveId; // Active widget
387 | ImGuiID ActiveIdPreviousFrame;
388 | bool ActiveIdIsAlive;
389 | bool ActiveIdIsJustActivated; // Set at the time of activation for one frame
390 | bool ActiveIdAllowOverlap; // Set only by active widget
391 | ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior)
392 | ImGuiWindow* ActiveIdWindow;
393 | ImGuiWindow* MovedWindow; // Track the child window we clicked on to move a window.
394 | ImGuiID MovedWindowMoveId; // == MovedWindow->RootWindow->MoveId
395 | ImVector Settings; // .ini Settings
396 | float SettingsDirtyTimer; // Save .ini Settings on disk when time reaches zero
397 | ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor()
398 | ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar()
399 | ImVector FontStack; // Stack for PushFont()/PopFont()
400 | ImVector OpenPopupStack; // Which popups are open (persistent)
401 | ImVector CurrentPopupStack; // Which level of BeginPopup() we are in (reset every frame)
402 |
403 | // Storage for SetNexWindow** and SetNextTreeNode*** functions
404 | ImVec2 SetNextWindowPosVal;
405 | ImVec2 SetNextWindowSizeVal;
406 | ImVec2 SetNextWindowContentSizeVal;
407 | bool SetNextWindowCollapsedVal;
408 | ImGuiSetCond SetNextWindowPosCond;
409 | ImGuiSetCond SetNextWindowSizeCond;
410 | ImGuiSetCond SetNextWindowContentSizeCond;
411 | ImGuiSetCond SetNextWindowCollapsedCond;
412 | ImRect SetNextWindowSizeConstraintRect; // Valid if 'SetNextWindowSizeConstraint' is true
413 | ImGuiSizeConstraintCallback SetNextWindowSizeConstraintCallback;
414 | void* SetNextWindowSizeConstraintCallbackUserData;
415 | bool SetNextWindowSizeConstraint;
416 | bool SetNextWindowFocus;
417 | bool SetNextTreeNodeOpenVal;
418 | ImGuiSetCond SetNextTreeNodeOpenCond;
419 |
420 | // Render
421 | ImDrawData RenderDrawData; // Main ImDrawData instance to pass render information to the user
422 | ImVector RenderDrawLists[3];
423 | float ModalWindowDarkeningRatio;
424 | ImDrawList OverlayDrawList; // Optional software render of mouse cursors, if io.MouseDrawCursor is set + a few debug overlays
425 | ImGuiMouseCursor MouseCursor;
426 | ImGuiMouseCursorData MouseCursorData[ImGuiMouseCursor_Count_];
427 |
428 | // Widget state
429 | ImGuiTextEditState InputTextState;
430 | ImFont InputTextPasswordFont;
431 | ImGuiID ScalarAsInputTextId; // Temporary text input when CTRL+clicking on a slider, etc.
432 | ImGuiStorage ColorEditModeStorage; // Store user selection of color edit mode
433 | float DragCurrentValue; // Currently dragged value, always float, not rounded by end-user precision settings
434 | ImVec2 DragLastMouseDelta;
435 | float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio
436 | float DragSpeedScaleSlow;
437 | float DragSpeedScaleFast;
438 | ImVec2 ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage?
439 | char Tooltip[1024];
440 | char* PrivateClipboard; // If no custom clipboard handler is defined
441 | ImVec2 OsImePosRequest, OsImePosSet; // Cursor position request & last passed to the OS Input Method Editor
442 |
443 | // Logging
444 | bool LogEnabled;
445 | FILE* LogFile; // If != NULL log to stdout/ file
446 | ImGuiTextBuffer* LogClipboard; // Else log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators.
447 | int LogStartDepth;
448 | int LogAutoExpandMaxDepth;
449 |
450 | // Misc
451 | float FramerateSecPerFrame[120]; // calculate estimate of framerate for user
452 | int FramerateSecPerFrameIdx;
453 | float FramerateSecPerFrameAccum;
454 | int CaptureMouseNextFrame; // explicit capture via CaptureInputs() sets those flags
455 | int CaptureKeyboardNextFrame;
456 | char TempBuffer[1024*3+1]; // temporary text buffer
457 |
458 | ImGuiContext()
459 | {
460 | Initialized = false;
461 | Font = NULL;
462 | FontSize = FontBaseSize = 0.0f;
463 | FontTexUvWhitePixel = ImVec2(0.0f, 0.0f);
464 |
465 | Time = 0.0f;
466 | FrameCount = 0;
467 | FrameCountEnded = FrameCountRendered = -1;
468 | CurrentWindow = NULL;
469 | FocusedWindow = NULL;
470 | HoveredWindow = NULL;
471 | HoveredRootWindow = NULL;
472 | HoveredId = 0;
473 | HoveredIdAllowOverlap = false;
474 | HoveredIdPreviousFrame = 0;
475 | ActiveId = 0;
476 | ActiveIdPreviousFrame = 0;
477 | ActiveIdIsAlive = false;
478 | ActiveIdIsJustActivated = false;
479 | ActiveIdAllowOverlap = false;
480 | ActiveIdClickOffset = ImVec2(-1,-1);
481 | ActiveIdWindow = NULL;
482 | MovedWindow = NULL;
483 | MovedWindowMoveId = 0;
484 | SettingsDirtyTimer = 0.0f;
485 |
486 | SetNextWindowPosVal = ImVec2(0.0f, 0.0f);
487 | SetNextWindowSizeVal = ImVec2(0.0f, 0.0f);
488 | SetNextWindowCollapsedVal = false;
489 | SetNextWindowPosCond = 0;
490 | SetNextWindowSizeCond = 0;
491 | SetNextWindowContentSizeCond = 0;
492 | SetNextWindowCollapsedCond = 0;
493 | SetNextWindowSizeConstraintRect = ImRect();
494 | SetNextWindowSizeConstraintCallback = NULL;
495 | SetNextWindowSizeConstraintCallbackUserData = NULL;
496 | SetNextWindowSizeConstraint = false;
497 | SetNextWindowFocus = false;
498 | SetNextTreeNodeOpenVal = false;
499 | SetNextTreeNodeOpenCond = 0;
500 |
501 | ScalarAsInputTextId = 0;
502 | DragCurrentValue = 0.0f;
503 | DragLastMouseDelta = ImVec2(0.0f, 0.0f);
504 | DragSpeedDefaultRatio = 1.0f / 100.0f;
505 | DragSpeedScaleSlow = 0.01f;
506 | DragSpeedScaleFast = 10.0f;
507 | ScrollbarClickDeltaToGrabCenter = ImVec2(0.0f, 0.0f);
508 | memset(Tooltip, 0, sizeof(Tooltip));
509 | PrivateClipboard = NULL;
510 | OsImePosRequest = OsImePosSet = ImVec2(-1.0f, -1.0f);
511 |
512 | ModalWindowDarkeningRatio = 0.0f;
513 | OverlayDrawList._OwnerName = "##Overlay"; // Give it a name for debugging
514 | MouseCursor = ImGuiMouseCursor_Arrow;
515 | memset(MouseCursorData, 0, sizeof(MouseCursorData));
516 |
517 | LogEnabled = false;
518 | LogFile = NULL;
519 | LogClipboard = NULL;
520 | LogStartDepth = 0;
521 | LogAutoExpandMaxDepth = 2;
522 |
523 | memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));
524 | FramerateSecPerFrameIdx = 0;
525 | FramerateSecPerFrameAccum = 0.0f;
526 | CaptureMouseNextFrame = CaptureKeyboardNextFrame = -1;
527 | memset(TempBuffer, 0, sizeof(TempBuffer));
528 | }
529 | };
530 |
531 | // Transient per-window data, reset at the beginning of the frame
532 | // FIXME: That's theory, in practice the delimitation between ImGuiWindow and ImGuiDrawContext is quite tenuous and could be reconsidered.
533 | struct IMGUI_API ImGuiDrawContext
534 | {
535 | ImVec2 CursorPos;
536 | ImVec2 CursorPosPrevLine;
537 | ImVec2 CursorStartPos;
538 | ImVec2 CursorMaxPos; // Implicitly calculate the size of our contents, always extending. Saved into window->SizeContents at the end of the frame
539 | float CurrentLineHeight;
540 | float CurrentLineTextBaseOffset;
541 | float PrevLineHeight;
542 | float PrevLineTextBaseOffset;
543 | float LogLinePosY;
544 | int TreeDepth;
545 | ImGuiID LastItemId;
546 | ImRect LastItemRect;
547 | bool LastItemHoveredAndUsable; // Item rectangle is hovered, and its window is currently interactable with (not blocked by a popup preventing access to the window)
548 | bool LastItemHoveredRect; // Item rectangle is hovered, but its window may or not be currently interactable with (might be blocked by a popup preventing access to the window)
549 | bool MenuBarAppending;
550 | float MenuBarOffsetX;
551 | ImVector ChildWindows;
552 | ImGuiStorage* StateStorage;
553 | ImGuiLayoutType LayoutType;
554 |
555 | // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings.
556 | float ItemWidth; // == ItemWidthStack.back(). 0.0: default, >0.0: width in pixels, <0.0: align xx pixels to the right of window
557 | float TextWrapPos; // == TextWrapPosStack.back() [empty == -1.0f]
558 | bool AllowKeyboardFocus; // == AllowKeyboardFocusStack.back() [empty == true]
559 | bool ButtonRepeat; // == ButtonRepeatStack.back() [empty == false]
560 | ImVector ItemWidthStack;
561 | ImVector TextWrapPosStack;
562 | ImVector AllowKeyboardFocusStack;
563 | ImVector ButtonRepeatStack;
564 | ImVectorGroupStack;
565 | ImGuiColorEditMode ColorEditMode;
566 | int StackSizesBackup[6]; // Store size of various stacks for asserting
567 |
568 | float IndentX; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.)
569 | float GroupOffsetX;
570 | float ColumnsOffsetX; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API.
571 | int ColumnsCurrent;
572 | int ColumnsCount;
573 | float ColumnsMinX;
574 | float ColumnsMaxX;
575 | float ColumnsStartPosY;
576 | float ColumnsCellMinY;
577 | float ColumnsCellMaxY;
578 | bool ColumnsShowBorders;
579 | ImGuiID ColumnsSetId;
580 | ImVector ColumnsData;
581 |
582 | ImGuiDrawContext()
583 | {
584 | CursorPos = CursorPosPrevLine = CursorStartPos = CursorMaxPos = ImVec2(0.0f, 0.0f);
585 | CurrentLineHeight = PrevLineHeight = 0.0f;
586 | CurrentLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f;
587 | LogLinePosY = -1.0f;
588 | TreeDepth = 0;
589 | LastItemId = 0;
590 | LastItemRect = ImRect(0.0f,0.0f,0.0f,0.0f);
591 | LastItemHoveredAndUsable = LastItemHoveredRect = false;
592 | MenuBarAppending = false;
593 | MenuBarOffsetX = 0.0f;
594 | StateStorage = NULL;
595 | LayoutType = ImGuiLayoutType_Vertical;
596 | ItemWidth = 0.0f;
597 | ButtonRepeat = false;
598 | AllowKeyboardFocus = true;
599 | TextWrapPos = -1.0f;
600 | ColorEditMode = ImGuiColorEditMode_RGB;
601 | memset(StackSizesBackup, 0, sizeof(StackSizesBackup));
602 |
603 | IndentX = 0.0f;
604 | GroupOffsetX = 0.0f;
605 | ColumnsOffsetX = 0.0f;
606 | ColumnsCurrent = 0;
607 | ColumnsCount = 1;
608 | ColumnsMinX = ColumnsMaxX = 0.0f;
609 | ColumnsStartPosY = 0.0f;
610 | ColumnsCellMinY = ColumnsCellMaxY = 0.0f;
611 | ColumnsShowBorders = true;
612 | ColumnsSetId = 0;
613 | }
614 | };
615 |
616 | // Windows data
617 | struct IMGUI_API ImGuiWindow
618 | {
619 | char* Name;
620 | ImGuiID ID; // == ImHash(Name)
621 | ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_
622 | int IndexWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0.
623 | ImVec2 PosFloat;
624 | ImVec2 Pos; // Position rounded-up to nearest pixel
625 | ImVec2 Size; // Current size (==SizeFull or collapsed title bar size)
626 | ImVec2 SizeFull; // Size when non collapsed
627 | ImVec2 SizeContents; // Size of contents (== extents reach of the drawing cursor) from previous frame
628 | ImVec2 SizeContentsExplicit; // Size of contents explicitly set by the user via SetNextWindowContentSize()
629 | ImRect ContentsRegionRect; // Maximum visible content position in window coordinates. ~~ (SizeContentsExplicit ? SizeContentsExplicit : Size - ScrollbarSizes) - CursorStartPos, per axis
630 | ImVec2 WindowPadding; // Window padding at the time of begin. We need to lock it, in particular manipulation of the ShowBorder would have an effect
631 | ImGuiID MoveId; // == window->GetID("#MOVE")
632 | ImVec2 Scroll;
633 | ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change)
634 | ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered
635 | bool ScrollbarX, ScrollbarY;
636 | ImVec2 ScrollbarSizes;
637 | float BorderSize;
638 | bool Active; // Set to true on Begin()
639 | bool WasActive;
640 | bool Accessed; // Set to true when any widget access the current window
641 | bool Collapsed; // Set when collapsing window to become only title-bar
642 | bool SkipItems; // == Visible && !Collapsed
643 | int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs)
644 | ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling)
645 | int AutoFitFramesX, AutoFitFramesY;
646 | bool AutoFitOnlyGrows;
647 | int AutoPosLastDirection;
648 | int HiddenFrames;
649 | int SetWindowPosAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowPos() call will succeed with this particular flag.
650 | int SetWindowSizeAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowSize() call will succeed with this particular flag.
651 | int SetWindowCollapsedAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowCollapsed() call will succeed with this particular flag.
652 | bool SetWindowPosCenterWanted;
653 |
654 | ImGuiDrawContext DC; // Temporary per-window data, reset at the beginning of the frame
655 | ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack
656 | ImRect ClipRect; // = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2.
657 | ImRect WindowRectClipped; // = WindowRect just after setup in Begin(). == window->Rect() for root window.
658 | int LastFrameActive;
659 | float ItemWidthDefault;
660 | ImGuiSimpleColumns MenuColumns; // Simplified columns storage for menu items
661 | ImGuiStorage StateStorage;
662 | float FontWindowScale; // Scale multiplier per-window
663 | ImDrawList* DrawList;
664 | ImGuiWindow* RootWindow; // If we are a child window, this is pointing to the first non-child parent window. Else point to ourself.
665 | ImGuiWindow* RootNonPopupWindow; // If we are a child window, this is pointing to the first non-child non-popup parent window. Else point to ourself.
666 | ImGuiWindow* ParentWindow; // If we are a child window, this is pointing to our parent window. Else point to NULL.
667 |
668 | // Navigation / Focus
669 | int FocusIdxAllCounter; // Start at -1 and increase as assigned via FocusItemRegister()
670 | int FocusIdxTabCounter; // (same, but only count widgets which you can Tab through)
671 | int FocusIdxAllRequestCurrent; // Item being requested for focus
672 | int FocusIdxTabRequestCurrent; // Tab-able item being requested for focus
673 | int FocusIdxAllRequestNext; // Item being requested for focus, for next update (relies on layout to be stable between the frame pressing TAB and the next frame)
674 | int FocusIdxTabRequestNext; // "
675 |
676 | public:
677 | ImGuiWindow(const char* name);
678 | ~ImGuiWindow();
679 |
680 | ImGuiID GetID(const char* str, const char* str_end = NULL);
681 | ImGuiID GetID(const void* ptr);
682 | ImGuiID GetIDNoKeepAlive(const char* str, const char* str_end = NULL);
683 |
684 | ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x+Size.x, Pos.y+Size.y); }
685 | float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale; }
686 | float TitleBarHeight() const { return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f; }
687 | ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); }
688 | float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; }
689 | ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); }
690 | };
691 |
692 | //-----------------------------------------------------------------------------
693 | // Internal API
694 | // No guarantee of forward compatibility here.
695 | //-----------------------------------------------------------------------------
696 |
697 | namespace ImGui
698 | {
699 | // We should always have a CurrentWindow in the stack (there is an implicit "Debug" window)
700 | // If this ever crash because g.CurrentWindow is NULL it means that either
701 | // - ImGui::NewFrame() has never been called, which is illegal.
702 | // - You are calling ImGui functions after ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal.
703 | inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; }
704 | inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->Accessed = true; return g.CurrentWindow; }
705 | IMGUI_API ImGuiWindow* GetParentWindow();
706 | IMGUI_API ImGuiWindow* FindWindowByName(const char* name);
707 | IMGUI_API void FocusWindow(ImGuiWindow* window);
708 |
709 | IMGUI_API void EndFrame(); // Ends the ImGui frame. Automatically called by Render()! you most likely don't need to ever call that yourself directly. If you don't need to render you can call EndFrame() but you'll have wasted CPU already. If you don't need to render, don't create any windows instead!
710 |
711 | IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window);
712 | IMGUI_API void SetHoveredID(ImGuiID id);
713 | IMGUI_API void KeepAliveID(ImGuiID id);
714 |
715 | IMGUI_API void ItemSize(const ImVec2& size, float text_offset_y = 0.0f);
716 | IMGUI_API void ItemSize(const ImRect& bb, float text_offset_y = 0.0f);
717 | IMGUI_API bool ItemAdd(const ImRect& bb, const ImGuiID* id);
718 | IMGUI_API bool IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged);
719 | IMGUI_API bool IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs = false);
720 | IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop = true); // Return true if focus is requested
721 | IMGUI_API void FocusableItemUnregister(ImGuiWindow* window);
722 | IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y);
723 | IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x);
724 |
725 | IMGUI_API void OpenPopupEx(const char* str_id, bool reopen_existing);
726 |
727 | // NB: All position are in absolute pixels coordinates (not window coordinates)
728 | // FIXME: All those functions are a mess and needs to be refactored into something decent. AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION.
729 | // We need: a sort of symbol library, preferably baked into font atlas when possible + decent text rendering helpers.
730 | IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true);
731 | IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width);
732 | IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0,0), const ImRect* clip_rect = NULL);
733 | IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f);
734 | IMGUI_API void RenderCollapseTriangle(ImVec2 pos, bool is_open, float scale = 1.0f);
735 | IMGUI_API void RenderBullet(ImVec2 pos);
736 | IMGUI_API void RenderCheckMark(ImVec2 pos, ImU32 col);
737 | IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text.
738 |
739 | IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0);
740 | IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0);
741 | IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos, float radius);
742 |
743 | IMGUI_API bool SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags = 0);
744 | IMGUI_API bool SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power);
745 | IMGUI_API bool SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format);
746 |
747 | IMGUI_API bool DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power);
748 | IMGUI_API bool DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power);
749 | IMGUI_API bool DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format);
750 |
751 | IMGUI_API bool InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback = NULL, void* user_data = NULL);
752 | IMGUI_API bool InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags);
753 | IMGUI_API bool InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags);
754 | IMGUI_API bool InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags);
755 | IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision);
756 |
757 | IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL);
758 | IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextTreeNodeOpened() data, if any. May return true when logging
759 | IMGUI_API void TreePushRawID(ImGuiID id);
760 |
761 | IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size);
762 |
763 | IMGUI_API int ParseFormatPrecision(const char* fmt, int default_value);
764 | IMGUI_API float RoundScalar(float value, int decimal_precision);
765 |
766 | } // namespace ImGui
767 |
768 | #ifdef __clang__
769 | #pragma clang diagnostic pop
770 | #endif
771 |
772 | #ifdef _MSC_VER
773 | #pragma warning (pop)
774 | #endif
775 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------